Java集合框架


1.1.1 集合框架--使用

image.png

從上面的圖可以看出java集合類主要有以下幾種:

1 .List結(jié)構(gòu)的集合類

ArrayList類逊谋,LinkedList類客年,Vector類挡毅,Stack類

集合框架List結(jié)構(gòu)集合類--ArrayList 類的使用( 無同步性氮惯,線程不安全 )[Demo139.java]

//java集合類用法--List結(jié)構(gòu)--ArrayList類
import java.util.*;//集合類基本上在util包中
public class Demo139 {
    public static void main(String[] args) {
        //定義ArrayList對象
        ArrayList al=new ArrayList();
        //顯示大小
        System.out.println("al大小:"+al.size());
        //向all中加入數(shù)據(jù)(類型是Object)
        //創(chuàng)建一個職員
        Clerk clerk1=new Clerk("宋江",50,1000);
        Clerk clerk2=new Clerk("吳用",45,1200);
        Clerk clerk3=new Clerk("林沖",35,1300);
        //將clerk1加入到al中
        al.add(clerk1);
        al.add(clerk2);
        al.add(clerk3);
        //可不可以放入同樣的對象绍刮?
        al.add(clerk1);
        //顯示大小
        System.out.println("al大小:"+al.size());
        //如何訪問al中的對象(數(shù)據(jù))
        //訪問第一個對象
        //Clerk temp=(Clerk)al.get(0);
        
        //System.out.println("第一個人的名字是:"+temp.getName());
        
        //遍歷al所有的對象(數(shù)據(jù))
        for(int i=0;i<al.size();i++){
            Clerk temp=(Clerk)al.get(i);
            System.out.println("名字:"+temp.getName());
        }
        //如何從al中刪除一個對象
        al.remove(1);
        System.out.println("===刪除吳用===");
        //遍歷al所有的對象(數(shù)據(jù))
        for(int i=0;i<al.size();i++){
            Clerk temp=(Clerk)al.get(i);
            System.out.println("名字:"+temp.getName());
        }
    }
}
//定義一個員工類
class Clerk{
    private String name;
    private int age;
    private float sal;
    public Clerk(String name,int age,float sal){
        this.name=name;
        this.age=age;
        this.sal=sal;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

2.LinkedList 集合類的使用方法[Demo141.java]

//LinkedList集合類的使用
import java.util.*;
public class Demo141 {
    public static void main(String[] args) {
        LinkedList ll=new LinkedList();
        Empp emp1=new Empp("sa01","aa",1.2f);
        Empp emp2=new Empp("sa02","bb",1.2f);
        Empp emp3=new Empp("sa03","cc",1.2f);
        //addFirst表示把emp1加載(鏈表)隊列的最前面
        ll.addFirst(emp1);//addFirst方法是可以插入在數(shù)組之前
        ll.addFirst(emp2);//也可以理解為addFirst方法是后進(jìn)先出的方法
        //addLast表示把emp3加載(鏈表)隊列的后面
        ll.addLast(emp3);
        System.out.println("測試LinkedList集合類中的addFist及addLast方法");
        for(int i=0;i<ll.size();i++){
            System.out.println(((Empp)ll.get(i)).getName());
        }
        //remove表示將某一條數(shù)據(jù)進(jìn)行刪除
        ll.remove(emp1);//將ll中的emp1數(shù)據(jù)刪除
        System.out.println("測試LinkedList集合類中的remove方法");
        for(int i=0;i<ll.size();i++){
            System.out.println(((Empp)ll.get(i)).getName());
        }
        ll.removeAll(ll);//清除整個鏈表
        System.out.println("測試LinkedList集合類中的remmoveall方法");
        for(int i=0;i<ll.size();i++){
            System.out.println(((Empp)ll.get(i)).getName());
        }
    }
}
//創(chuàng)建員工類
class Empp{//在同一個包中的類桶略,可以同包中的其它class文件直接訪問或調(diào)用
    //定義成員變量工號语淘、姓名、薪水
    private String empNo;
    private String name;
    private float sal;
    //創(chuàng)建構(gòu)造函數(shù)际歼,初始化成員變量
    public Empp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    //使用set惶翻、get方法進(jìn)行數(shù)據(jù)傳遞
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

===============================================================================

3.Vector 集合類的使用(線程安全具有同步性) [Demo142.java]

//Vector集合類(向量)的使用方法
import java.util.*;
public class Demo142 {
    public static void main(String[] args) {
        //Vector的用法
        Vector vv=new Vector();
        AEmp emp1=new AEmp("1","aa",1.2f);
        AEmp emp2=new AEmp("2","bb",1.2f);
        AEmp emp3=new AEmp("3","cc",1.2f);
        vv.add(emp1);
        vv.add(emp2);
        vv.add(emp3);
        //遍歷
        for(int i=0;i<vv.size();i++){
            AEmp emp=(AEmp)vv.get(i);
            System.out.println(emp.getName());
        }
    }
}
//創(chuàng)建員工類
class AEmp{
    //定義成員變量工號、姓名鹅心、薪水
    private String empNo;
    private String name;
    private float sal;
    //創(chuàng)建構(gòu)造函數(shù)吕粗,初始化成員變量
    public AEmp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    //使用set、get方法進(jìn)行數(shù)據(jù)傳遞
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

===============================================================================

4.Stack 集合類(棧)的使用[Demo143.java]

//Stack集合類(棧)的使用方法
package com.haiding.set;
import java.util.*;
public class Demo143 {
    public static void main(String[] args) {
        //Stack的用法
        Stack stack=new Stack();
        AEmp emp1=new AEmp("s1","aa",1.2f);
        AEmp emp2=new AEmp("s2","bb",1.2f);
        stack.add(emp1);
        stack.add(emp2);
        for(int i=0;i<stack.size();i++){
            System.out.println(((AEmp)stack.get(i)).getName());
        }
    }
}
//創(chuàng)建員工類
class AEmp{
    //定義成員變量工號旭愧、姓名颅筋、薪水
    private String empNo;
    private String name;
    private float sal;
    //創(chuàng)建構(gòu)造函數(shù),初始化成員變量
    public AEmp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    //使用set输枯、get方法進(jìn)行數(shù)據(jù)傳遞
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

1.1.2 集合框架--深入討論

1.ArrayList 和Vector的區(qū)別

ArrayList與Vector都是java的集合類议泵,都可以用來存放java對象,這是他們的相同點桃熄,但是他們也有區(qū)別:

1先口、同步性

Vector是線程同步的。這個類中的一些方法保證了Vector中的對象是線程安全的瞳收。而ArrayList則是線程異步的碉京,因此ArrayList中的對象并不是線程安全的。因為同步的要求會影響執(zhí)行的效率螟深,所以如果你不需要線程安全的集合那么使用ArrayList是一個很好的選擇谐宙,這樣可以避免由于同步帶來的不必要的性能開銷。

2血崭、數(shù)據(jù)增長

從內(nèi)部實現(xiàn)機(jī)制來講ArrayList和Vector都是使用數(shù)組(Array)來控制集合中的對象卧惜。當(dāng)你向這兩種類型中增加元素的時候厘灼,如果元素的數(shù)目超出了內(nèi)部數(shù)組目前的長度它們都需要擴(kuò)展內(nèi)部數(shù)組的長度,Vector缺省情況下自動增長原來一倍的數(shù)組長度咽瓷,ArrayList是原來的50%设凹,所以最后你獲得的這個集合所占的空間總是比你實際需要的要大。所以如果你要在集合中保存大量的數(shù)據(jù)那么使用Vector有一些優(yōu)勢茅姜,因為你可以通過設(shè)置集合的初始化大小來避免不必要的資源開銷闪朱。

===============================================================================

2.Map 結(jié)構(gòu)的集合類

HashMap類,Hashtable類

HashMap集合類的使用[Demo143.java]

//HashMap集合類的使用
import java.util.*;
public class Demo143 {
    public static void main(String[] args) {
        //創(chuàng)建HashMap對象
        HashMap hm=new HashMap();
        Emp emp1=new Emp("s001","aa",3.4f);
        Emp emp2=new Emp("s002","bb",5.6f);
        Emp emp3=new Emp("s003","cc",1.2f);
        //將emp放入到hm中
        //hm.put(null,null);//可以放空值
        hm.put("s001", emp1);
        hm.put("s002", emp2);
        hm.put("s002", emp3);//不允許key重復(fù),所以emp3會覆蓋emp2
        //如果你要查找編號是s002
        if(hm.containsKey("s002")){//取鍵值containsKey
            System.out.println("有該員工");
            //如何取出钻洒,鍵<key>值
            Emp emp=(Emp)hm.get("s002");
            System.out.println("名字"+emp.getName());
        }else{
            System.out.println("沒該員工");
        }
        //遍歷HashMap中所有的key和value值
        //Iterator迭代
        Iterator it=hm.keySet().iterator();
        //hasNext返回一個boolean值
        while(it.hasNext()){
            //如果有下一個取出key值
            String key=it.next().toString();
            //通過key取出value
            Emp emp=(Emp)hm.get(key);
            System.out.println("名字:"+emp.getName());
            System.out.println("工資:"+emp.getSal());
        }
    }
}
//創(chuàng)建員工類
class Emp{
    //定義成員變量工號奋姿、姓名、薪水
    private String empNo;
    private String name;
    private float sal;
    //創(chuàng)建構(gòu)造函數(shù)素标,初始化成員變量
    public Emp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    //使用set称诗、get方法進(jìn)行數(shù)據(jù)傳遞
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

3.Hashtable 集合類的使用( Hashtable 具有同步性,線程安全 )

import java.util.*;
public class Demo144{
    public static void main(String []args){
        Hashtable ht=new Hashtable();//Hashtable與HsahMap在用法上一致
        Emp emp4=new Emp("s101","a1",2.2f);
        Emp emp5=new Emp("s102","a2",1.2f);
        Emp emp6=new Emp("s103","a3",4.2f);
        ht.put("s101", emp4);
        ht.put("s102", emp5);
        ht.put("s103", emp6);
        //遍歷
        for(Iterator it=ht.keySet().iterator();it.hasNext();){
            String key=it.next().toString();
            Emp emp=(Emp)ht.get(key);
            System.out.println("名字:"+emp.getName()+"\t工資:"+emp.getSal());
        }
    }
}
//創(chuàng)建員工類
class Emp{
    //定義成員變量工號头遭、姓名寓免、薪水
    private String empNo;
    private String name;
    private float sal;
    //創(chuàng)建構(gòu)造函數(shù),初始化成員變量
    public Emp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    //使用set计维、get方法進(jìn)行數(shù)據(jù)傳遞
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

4.HashMap 和Hashtable集合類的區(qū)別

HashMap與Hashtable都是java的集合類袜香,都可以用來存放java對象,這是他們的相同點鲫惶,但是他們也有區(qū)別蜈首。

1、歷史原因

Hashtable是基于陳舊的Dictionary類的欠母,HashMap是java 1.2引進(jìn)的Map接口的一個實現(xiàn)欢策。

2、同步性

Hashtable是線程同步的艺蝴。這個類中的一些方法保證了Hashtable中的對象是線程安全的猬腰。而HashMap則是線程異步的,因此HashMap中的對象并不是線程安全的猜敢。因為同步的要求會影響執(zhí)行的效率,所以如果你不需要線程安全的集合那么使用HashMap是一個很好的選擇盒延,這樣可以避免由于同步帶來的不必要的性能開銷缩擂,從而提高效率。

3添寺、值

HashMap可以讓你將空值作為一個表的條目的key或value但是Hashtable是不能放入空值的(null)

1.1.3 集合框架--深入討論(2)

進(jìn)一步理解集合框架

java的設(shè)計者給我們提供了這些集合類胯盯,在后面編程中是相當(dāng)有用的,具體什么時候用什么集合计露,要根據(jù)我們剛才分析的集合異同來選取博脑。

如何選用集合類憎乙?

1、要求線程安全叉趣,使用Vector泞边、Hashtable

2、不要求線程安全疗杉,使用ArrayList,LinkedList,HashMap

3阵谚、要求key和value鍵值,則使用HashMap,Hashtable

4烟具、數(shù)據(jù)量很大梢什,又要線程安全,則使用Vector

===============================================================================

1.Set 結(jié)構(gòu)的集合類

HashSet 類朝聋,TreeSet類

HashSet是基于HashMap實現(xiàn)的嗡午,HashSet底層采用HashMap來保存所有元素。

hashCode和equal()是HashMap用的冀痕,因為無需排序所以只需要關(guān)注定位和唯一性即可

hashCode是用來計算hash值的翼馆,hash值是用來確定hash表索引的

hash表中的一個索引存放的是一張鏈表,所以還要通過equal方法循環(huán)比較鏈上的每一個對象才可以真正定位到鍵值對應(yīng)的Entry

put時金度,如果hash表中沒定定位到应媚,就在鏈表前加一個Entry,如果定位到了猜极,則更換Entry中的value(值)并返回舊value(值)

覆寫key的hashCode()和equal()時一定要注意中姜,不要把它們和可變屬性關(guān)聯(lián)上,否則屬性變了之后hashCode會變跟伏,equal也會為false丢胚,這樣在Map中就找不到它了而且這樣的對象因為找不到它所以得不到釋放,這樣就變成了一個無效引用(相當(dāng)于內(nèi)存泄漏)

//HashSet的使用方法[Demo145.java]
import java.util.*;
public class Demo145{
    public static void main(String []args){
        HashSet<Emp> hs=new HashSet<Emp>();
        Emp emp1=new Emp("s001","aa",1.2f);
        Emp emp2=new Emp("s002","bb",1.6f);
        Emp emp3=new Emp("s003","cc",1.8f);
        Emp emp4=new Emp("s001","aa",1.2f);
        hs.add(emp1);
        hs.add(emp2);
        hs.add(emp3);
        hs.add(emp4);
        hs.add(emp1);//重復(fù)的emp1,HashSet會自動去除
        System.out.println("HashSet_size="+hs.size());
        System.out.println();
        ArrayList<Emp> al=new ArrayList<Emp>();
        Emp emp5=new Emp("s004","dd",1.0f);
        Emp emp6=new Emp("s005","ee",2.5f);
        al.add(emp5);
        al.add(emp6);
        //al.add(emp1);
        hs.addAll(al);//將al中的值加到hs中受扳,并去除重復(fù)的emp1
        System.out.println("HashSet_ArrayList_size="+hs.size());
        System.out.println();
        //轉(zhuǎn)換數(shù)組o[]携龟,遍歷并輸出HashSet中的無素
        Object o[]=hs.toArray();
        for(int i=0;i<o.length;i++){
            System.out.println("工號:"+((Emp)o[i]).getEmpNo()+"\t姓名:"+((Emp)o[i]).getName()+"\t工資:"+((Emp)o[i]).getSal());
        }
    }
}
class Emp{
    private String empNo;
    private String name;
    private float sal;
    public Emp(String empNo,String name,float sal){
        this.empNo=empNo;
        this.name=name;
        this.sal=sal;
    }
    public String getEmpNo() {
        return empNo;
    }
    public void setEmpNo(String empNo) {
        this.empNo = empNo;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getSal() {
        return sal;
    }
    public void setSal(float sal) {
        this.sal = sal;
    }
}

TreeSet 集合類是一個有序集合,它的元素按照升序排序勘高,默認(rèn)是自然順序排列峡蟋,也就是說

TreeSet中的對象元素需要實現(xiàn)Comparable接口。TreeSet與HashSet類一樣沒有g(shù)et()方法來獲取列表中的元素华望,所以也只能通過迭代器方法來獲取蕊蝗。

由于TreeMap需要排序,所以需要一個Comparator為鍵值進(jìn)行大小比較赖舟,當(dāng)然也是用Comparator定位的Comparator可以在創(chuàng)建TreeMap時指定蓬戚,這時排序時使用Comparator.compare

如果創(chuàng)建時沒有指定Comparator那么就會使用key.compareTo()方法,這就要求key必須實現(xiàn)Comparable接口

TreeMap 是使用Tree數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的宾抓,所以使用compare接口就可以完成定位了子漩。

TreeSet 是依靠TreeMap來實現(xiàn)的豫喧,TreeSet是一個有序集合,它的元素按照升序排列幢泼,默認(rèn)是按照自然順序排列紧显,也就是說TreeSet中的對象元素需要實現(xiàn)Comparable接口。

TreeSet 類中跟HashSet類一要也沒有g(shù)et()方法來獲取列表中的元素旭绒,所以也只能通過迭代器的方法來獲取 鸟妙。

//TreeSet集合類的使用[Demo146.java]
import java.util.*;
public class Demo146{
    public static void main(String[] args){
        //傳遞一個比較器來實現(xiàn)你自己的排序方式
        TreeSet tr =new TreeSet(new Student.StudentComparator());
        tr.add(new Student(301,"張三"));//將學(xué)生數(shù)據(jù)寫入TreeSet集合類的tr中
        tr.add(new Student(201,"李二"));
        tr.add(new Student(101,"王五"));
        tr.add(new Student(101,"窮一"));
        Iterator it=tr.iterator();//迭代器,遍歷
        while(it.hasNext()){//判斷是否有下一個元素
            System.out.println(it.next());
        }
    }
}
//創(chuàng)建Strudent學(xué)生類并實現(xiàn)Comparable與Comparator接口
class Student implements Comparable,Comparator{
    private int num;//定義學(xué)號
    private String name;//定義名字
    public Student(int num,String name){
        this.num=num;
        this.name=name;
    }
    public int compareTo(Object o){
        Student st=(Student)o;
        int result;
        result=num>st.num?1:(num==st.num?0:-1);//判斷學(xué)號是否相同并返回result的值
        //如果學(xué)號相等挥吵,就按姓名排列
    /*  if(result==0){
            return name.compareTo(st.name);
        }*/
        return result;
    }
    //實現(xiàn)Comparator接口并實現(xiàn)它的抽象方法
    public int compare(Object o1,Object o2){
        Student st1 =(Student)o1;
        Student st2 =(Student)o2;
        return st1.name.compareTo(st2.name);//比較姓名是否相同
    }
    //重寫toString()方法重父,因為如果不重寫,打印出來的是16進(jìn)制代碼
    public String toString(){
        return "num="+num+"; name="+name;
    }
    public static class StudentComparator implements Comparator{//定義一個靜態(tài)StudentComparator類并實現(xiàn)Comparator接口
        public int compare(Object o1,Object o2){
            Student st1 =(Student)o1;
            Student st2 =(Student)o2;
            int result;
            result=st1.num>st2.num?1:(st1.num==st2.num?0:-1);//判斷學(xué)號是否相同進(jìn)行排序
            if(result==0){//如果學(xué)號相等 就進(jìn)行名字排序
                result=st1.name.compareTo(st2.name);
            }
            return result;
        }
    }
}

HashSet 與TreeSet集合類的區(qū)別:

HashSet是基于hash算法實現(xiàn)的忽匈,性能優(yōu)于TreeSet房午。通常使用HashSet,在我們需要對其中元素排序的時候才使用TreeSet丹允。

===============================================================================

2.Queue 結(jié)構(gòu)的集合

Queue接口

Queue接口與List郭厌、Set同一級別,都是繼承了Collection接口雕蔽。LinkedList實現(xiàn)了Queue接口折柠。Queue接口窄化了對LinkedList的方法的訪問權(quán)限(即在方法中的參數(shù)類型如果是Queue時,就完全只能訪問Queue接口所定義的方法了批狐,而不能直接訪問 LinkedList的非Queue的方法)扇售,以使得只有恰當(dāng)?shù)姆椒ú趴梢允褂谩lockingQueue繼承了Queue接口嚣艇。

隊列是一種數(shù)據(jù)結(jié)構(gòu)承冰。它有兩個基本操作:在隊列尾部加人一個元素,和從隊列頭部移除一個元素食零。就是說困乒,隊列以一種先進(jìn)先出的方式管理數(shù)據(jù),如果你試圖向一個已經(jīng)滿了的阻塞隊列中添加一個元素或者是從一個空的阻塞隊列中移除一個元索贰谣,將導(dǎo)致線程阻塞娜搂。

在多線程進(jìn)行合作時,阻塞隊列是很有用的工具冈爹。工作者線程可以定期地把中間結(jié)果存到阻塞隊列中而其他工作者線線程把中間結(jié)果取出并在將來修改它們涌攻。隊列會自動平衡負(fù)載。如果第一個線程集運行得比第二個慢频伤,則第二個線程集在等待結(jié)果時就會阻塞。如果第一個線程集運行得快芝此,那么它將等待第二個線程集趕上來憋肖。

阻塞隊列的操作可以根據(jù)它們的響應(yīng)方式分為以下三類:add因痛、remove和element操作在你試圖為一個已滿的隊列增加元素或從空隊列取得元素時 拋出異常。當(dāng)然岸更,在多線程程序中鸵膏,隊列在任何時間都可能變成滿的或空的,所以你可能想使用offer怎炊、poll谭企、peek方法。這些方法在無法完成任務(wù)時 只是給出一個出錯示而不會拋出異常评肆。

注意:poll和peek方法出錯進(jìn)返回null债查。因此,向隊列中插入null值是不合法的瓜挽。

offer 盹廷,add區(qū)別:

一些隊列有大小限制,因此如果想在一個滿的隊列中加入一個新項久橙,多出的項就會被拒絕俄占。這時新的 offer 方法就可以起作用了。它不是對調(diào)用 add() 方法拋出一個 unchecked 異常淆衷,而只是得到由 offer() 返回的 false缸榄。

poll ,remove區(qū)別:

remove()和poll()方法都是從隊列中刪除第一個元素(head)祝拯。remove()的行為與Collection 接口的版本相似甚带,但是新的 poll()方法在用空集合調(diào)用時不是拋出異常,只是返回null鹿驼。因此新的方法更適合容易出現(xiàn)異常條件的情況欲低。

peek ,element區(qū)別:

element()和peek()用于在隊列的頭部查詢元素畜晰。與 remove()方法類似砾莱,在隊列為空時,element()拋出一個異常凄鼻,而peek()返回null腊瑟。

五個隊列所提供的各有不同:

  • ArrayBlockingQueue :

一個由數(shù)組支持的有界隊列】榘觯基于數(shù)組的阻塞循環(huán)隊列闰非,先進(jìn)先出原則對元素進(jìn)行排序。

  • LinkedBlockingQueue:

一個由鏈接節(jié)點支持的可選有界隊列峭范〔扑桑基于鏈表的隊列,先進(jìn)先出排序元素。

  • PriorityBlockingQueue:

一個由優(yōu)先級堆支持的無界優(yōu)先級隊列辆毡。PriorityBlockingQueue是對PriorityQueue的再次包裝菜秦,是基于堆數(shù)據(jù)結(jié)構(gòu)的,而PriorityQueue是沒有容量限制的舶掖,與ArrayList一樣球昨,所以在優(yōu)先阻塞隊列上put時是不會受阻的。但是由于資源被耗盡眨攘,所以試圖執(zhí)行添加操作可能會導(dǎo)致OutOfMemoryError)主慰,但是如果隊列為空,那么取元素的操作take就會阻塞鲫售,所以它的檢索操作take是受阻的共螺。另外,往入該隊列中的元素要具有比較能力龟虎。

  • DelayQueue:

一個由優(yōu)先級堆支持的璃谨、基于時間的調(diào)度隊列。(基于PriorityQueue來實現(xiàn)的)是一個存放Delayed 元素的無界阻塞隊列鲤妥,只有在延遲期滿時才能從中提取元素佳吞。該隊列的頭部是延遲期滿后保存時間最長的 Delayed 元素。如果延遲都還沒有期滿棉安,則隊列沒有頭部底扳,并且poll將返回null。當(dāng)一個元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一個小于或等于零的值時贡耽,則出現(xiàn)期滿衷模,poll就以移除這個元素了。此隊列不允許使用null元素蒲赂。

  • SynchronousQueue:

一個利用 BlockingQueue接口的簡單聚集(rendezvous)機(jī)制阱冶。

SynchronousQueue 類是最簡單的。它沒有內(nèi)部容量滥嘴。它就像線程之間的手遞手機(jī)制木蹬。在隊列中加入一個元素的生產(chǎn)者會等待另一個線程的消費者。當(dāng)這個消費者出現(xiàn)時若皱,這個元素就直接在消費者和生產(chǎn)者之間傳遞镊叁,永遠(yuǎn)不會加入到阻塞隊列中。

注意:Queue隊列是不能直接實例化的走触。

原先在java編程中晦譬,Queue的實現(xiàn)都是用LinkedList

如:Queue qe=new LinkedList();

注意:此實現(xiàn)不是同步的。如果多個線程同時訪問一個鏈接列表互广,而其中至少一個線程結(jié)構(gòu)上修改了該列表敛腌,則它必須保質(zhì)外部同步。(結(jié)構(gòu)修改指添加或刪除一個或多個元素的任何操作;僅設(shè)置元素的值不是結(jié)構(gòu)修改迎瞧。)這一般通過對自然封裝該列表的對象進(jìn)行同步操作來完成夸溶。

JDK1.6中:

接口Queue<E>

類型參數(shù):E - collection 中所保存元素的類型逸吵。

所有超級接口:Collection<E>, Iterable<E>

所有已知子接口:BlockingDeque<E>, BlockingQueue<E>, Deque<E>

所有已知實現(xiàn)類:AbstractQueue, ArrayBlockingQueue, ArrayDeque, ConcurrentLinkedQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedList, PriorityBlockingQueue, PriorityQueue, SynchronousQueue

JDK1.7中:

接口Queue<E>

類型參數(shù):E - collection 中所保存元素的類型凶硅。

所有超級接口:Collection<E>, Iterable<E>

所有已知子接口:BlockingDeque<E>, BlockingQueue<E>, Deque<E>, TransferQueue<E>

所有已知實現(xiàn)類:AbstractQueue, ArrayBlockingQueue, ArrayDeque, ConcurrentLinkedQueue, ConcurrentLinkedQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedList, LinkedTransferQueue, PriorityBlockingQueue, PriorityQueue, SynchronousQueue

===============================================================================

3.Java 中的List/Set和Map的區(qū)別

List按對象進(jìn)入的順序保存對象,不做排序和編輯操作扫皱。

Set對每個對象只接受一次足绅,并使用自己內(nèi)部的排序方法(通常,你只關(guān)心某個元素是否屬于Set而不關(guān)心它的順序--否則使用List)韩脑。

Map同樣對每個元素保存一份氢妈,但這是基于"鍵"(key)的,Map也有內(nèi)置的排序段多,因而不關(guān)心元素添加的順序首量。

如果添加元素的順序?qū)Τ绦蛟O(shè)計很重要,應(yīng)該使用LinkedHashSet或者LinkedHashMap进苍。

List 的功能方法

實際上有兩種List:一種是基本的ArrayList其優(yōu)點在于隨機(jī)訪問元素加缘,另一種是更強(qiáng)大的LinkedList它并不是為快速隨機(jī)訪問設(shè)計的,而是具有一套更通用的方法觉啊。

List:次序是List最重要的特點:它保證維護(hù)元素特定的順序拣宏。List為Collection添加了許多方法,使得能夠向List中間插入與移除元素(這只推薦LinkedList使用)一個List可以生成Listlterator杠人,使用它可以從兩個方向遍歷List勋乾,也可以從List中間插入和移除元素。

ArrayList:由數(shù)組實現(xiàn)的List嗡善。允許對元素進(jìn)行快速隨機(jī)訪問辑莫,但是向List中間插入與移除元素的速率很慢。Listlterator只應(yīng)該用來由后向前遍歷ArrayList罩引。而不是用來插入和移除元素各吨。因為那比LinkedList開銷要大很多。

LinkedList:對順序訪問進(jìn)行了優(yōu)化蜒程,向List中間插入與刪除的開銷并不大绅你。隨機(jī)訪問則相對較慢。(使用ArrayList代替)還具有下列方法:addFirst()昭躺,addLast()忌锯,getFirst(),getLast()领炫,removeFirst()和removeLast()這些方法(沒有在任何接口或基類中定義過)使得LinkedList可以當(dāng)作堆棧偶垮、隊列和雙向隊列使用。

Set 的功能方法

Set具有與Collection完全一樣的接口,因此沒有任何額外的功能似舵,不象前面有兩個不同的List脚猾。實際上Set就是Collection,只是行為不同砚哗。(這是繼承與多態(tài)思想的典型應(yīng)用:表現(xiàn)不同的行為龙助。)Set不保存重復(fù)的元素(至于如何判斷元素相同則較為負(fù)責(zé))

Set:存入Set的每個元素都必須是唯一的,因為Set不保存重復(fù)元素蛛芥。加入Set的元素必需定義equals()方法以確保對象的唯一性绞吁。Set與Collection有完全一樣的接口越锈。Set接口不保證維護(hù)元素的次序鸠补。

HashSet:為快速查找設(shè)計的Set绘梦。存入HashSet的對象必須定義hashCode()。

TreeSet:保存次序的Set涯竟,底層為樹結(jié)構(gòu)赡鲜。使用它可以從Set中提取有序的序列。

LinkedHashSet:具有HashSet的查詢速度庐船,且內(nèi)部使用鏈表維護(hù)元素的順序(插入的次序)银酬。于是在使用迭代器遍歷Set時,結(jié)果會按元素插入的次序顯示醉鳖。

Map 的功能方法

方法put(Object key,Object value)添加一個"值"(想要得東西)和與"值"相關(guān)的"鍵"(key)(使用它來查找)捡硅。方法get(Object key)返回與給定"鍵"相關(guān)聯(lián)的"值"〉量茫可以用containsKey()和containsValue()測試Map中是否包含某個"鍵"或"值"壮韭。標(biāo)準(zhǔn)的java類庫中包含了幾種不同的Map:HashMap,TreeMap纹因,LinkedHashMap喷屋,WeakHashMap,ldentityHashMap瞭恰。它們都有同樣的基本接口Map屯曹,但是行為、效率惊畏、排序策略恶耽、保存對象的生命周期和判定"鍵"等價的策略等各不相同。

執(zhí)行效率是Map的一個大問題颜启⊥导螅看看get()要做哪些事,就會明白為什么在ArrayList中搜索"鍵"是相當(dāng)慢的缰盏。這正是HashMap提高速度的地方涌萤。HashMap使用了特殊的值淹遵,稱為"散列碼"(hash code),來取代對鍵的緩慢搜索负溪。"散列碼"是"相對唯一"用以代表對象的int值透揣,它是通過將該對象的某些信息進(jìn)行轉(zhuǎn)換而生成的。所有java對象都能產(chǎn)生散列碼川抡,因為hashCode()是定義在基類Object中的方法辐真。

HashMap就是使用對象的hashCode()進(jìn)行快速查詢的。此方法能夠顯著提高性能猖腕。

Map:維護(hù)"鍵值對"的關(guān)聯(lián)性拆祈,使你可通過"鍵"查找"值"

HashMap:Map基于散列表的實現(xiàn)。插入和查詢"鍵值對"的開銷是固定的倘感。可以通過構(gòu)造器設(shè)置容量capacity和負(fù)載因子load factor咙咽,以調(diào)整容器的性能老玛。

LinkedHashMap:類似于HashMap,但是迭代遍歷它時钧敞,取得"鍵值對"的順序是其插入次序蜡豹,或者是最近最少使(LRU)的次序。只能HashMap慢一點溉苛。而在迭代訪問時發(fā)而更快镜廉,因為它使用鍵表維護(hù)內(nèi)部次序。

TreeMap:基于紅黑樹數(shù)據(jù)結(jié)果的實現(xiàn)愚战。查看"鍵"或"鍵值對"時娇唯,它們會被排序(次序由Comparabel或Comparator決定)。TreeMap的特點在于寂玲,你得到的結(jié)果是經(jīng)過排序的塔插。TreeMap是唯一的帶有subMap()方法的Map,它可以返回一個子樹拓哟。

WeakHashMap:旨鍵(weak key)Map想许,Map中使用的對象也被允許釋放:這是為解決特殊問題設(shè)計的。如果沒有map之外的引用指向某個"鍵"断序,則此"鍵"可以被垃圾收集器回收流纹。

ldentifyHashMap:使用==代替equals()對"鍵"作比較的hash map。專為解決特殊問題而設(shè)計违诗。


4.Java 中的Iterator(迭代器)的用法

java.util包中包含了一系列重要的集合類漱凝,集合類的根接口Collection。

Collection接口是所有集合類的根類型较雕。它的一個主要的接口方法是:

boolean add(Object c)添加數(shù)據(jù)

add()方法將添加一個新元素碉哑。注意這個方法會返回一個boolean挚币,但是返回值不是表示添加成功與否。Collection規(guī)定:如果一個集合拒絕添加這個元素扣典,無論任何原因妆毕,都必須拋出異常。這個返回值表示的意義是add()方法執(zhí)行后贮尖,集合的內(nèi)容是否改變了(就是元素有無數(shù)量笛粘,位置等變化),這是由具體類實現(xiàn)的湿硝。即:如果方法出錯薪前,總會拋出異常;返回值僅僅表示該方法執(zhí)行后這個Collection的內(nèi)容有無變化关斜。

類似還有:

boolean addall(Collection c);添加所有數(shù)據(jù)

boolean remove(Object o);刪除數(shù)據(jù)

boolean removeall(Collection c);刪除所有數(shù)據(jù)

boolean remainall(Collection c);保持所有數(shù)據(jù)

Object[]toArray()方法很簡單示括,把集合轉(zhuǎn)換成數(shù)組返回。Object[]toArray(Object[] a)方法就有點復(fù)雜了痢畜,首先垛膝,返回的Object[]仍然是把集合的所有元素變成數(shù)組,但是類型和參數(shù)a的類型是相同的丁稀。

如:String[] o=(String)c.toArray(new String[0]);

得到的o實際類型是String[]數(shù)組吼拥。

其次,如果參數(shù)a的大小裝不下集合的所有元素线衫,返回的將是一個新的數(shù)組凿可。如果參數(shù)a的大小能裝下集合的所有元素,則返回的還是a授账,但a的內(nèi)容用集合的元素來填充枯跑。尤其要注意的是,如果a的大小比集合元素的個數(shù)還多矗积,a后面的部分全部被置為null(空)全肮。

最后一個最重要的方法是Iterator(),返回一個Iterator(迭代子)棘捣,用于遍歷集合的所有元素辜腺。

用Iterator模式實現(xiàn)遍歷集合

Iterator模式 是用于遍歷集合類的標(biāo)準(zhǔn)訪問方法。它可以把訪問邏輯從不同類型的集合類中抽象出來乍恐,從而避免向客戶端暴露集合的內(nèi)部結(jié)構(gòu)评疗。

例如,如果沒有使用Iterator茵烈,遍歷一個數(shù)組的方法是使用索引:

for(int i=0;i<array.size();i++){..get(i)...}

而訪問一個鏈表(LinkedList)又必須使用while循環(huán):

while((e=e.next())!=null){...e.data()...}

以上兩種方法客戶端都必須事先知道集合的內(nèi)部結(jié)構(gòu)百匆,訪問代碼和集合本身是緊耦合,無法將訪問邏輯從集合類和客戶端代碼中分離出來呜投,每一種集合對應(yīng)一種遍歷方法加匈,客戶端代碼無法復(fù)用存璃。

更恐怖的是,如果以后需要把ArrayList更換為LinkedList雕拼,則原來的客戶端代碼必須全部重寫纵东。

為解決以上問題,Iterator模式總是用同一種邏輯來遍歷集合:

for(Iterator it=c.iterater();it.hasNext();){ ...}

奧秘在于客戶端自身不維護(hù)遍歷集合的"指針"啥寇,所有的內(nèi)部狀態(tài)(如當(dāng)前元素位置偎球,是否有下一個元素)都由Iterator來維護(hù),而這個Iterator由集合類通過工廠方法生成辑甜,因此衰絮,它知道如何遍歷整個集合。

客戶端從不直接和集合類打交道磷醋,它總是控制Iterator猫牡,向它發(fā)送"向前","向后"子檀,"取當(dāng)前元素"的命令镊掖,就可以間接遍歷整個集合。

首先看看java.util.Iterator接口的定義:

public interface Iterator {

  boolean hasNext();

  Object next();

  void remove();

}

依賴前兩個方法就能完成遍歷褂痰,典型的代碼如下:

for(Iterator it=c.iterator();it.hasNext();){

  Object o=it.next();

 // 對o的操作...

}

在JDK1.5中,還對上面的代碼在語法上作了簡化:

// Type是具體的類型症虑,如String缩歪。

for(Type t:c){

// 對t的操作...

}

每一種集合類返回的Iterator具體類型可能不同,Array可能返回ArrayIterator谍憔,Set可能返回SetIterator匪蝙,Tree 可能返回TreeIterator,但是它們都實現(xiàn)了Iterator接口习贫,因此逛球,客戶端不關(guān)心到底是哪種Iterator,它只需要獲得這個 Iterator接口即可苫昌,這就是面向?qū)ο蟮耐Α?/p>

要確保遍歷過程順利完成颤绕,必須保證遍歷過程中不更改集合的內(nèi)容(Iterator的remove()方法除外),因此祟身,確保遍歷可靠的原則是只在一個線程中使用這個集合奥务,或者在多線程中對遍歷代碼進(jìn)行同步。

Iterator示例:

  Collection c=new ArrayList();
    c.add("abc");
    c.add("xyz");
    for(Iterator it=c.iterator();it.hasNext();){
        String s=(String)it.next();
        System.out.println(s);
    }

如果你把第一行代碼的ArrayList換成LinkedList或Vector袜硫,剩下的代碼不用改動一行就能編譯氯葬,而且功能不變,這就是針對抽象編程的原則:對具體類的依賴性最小婉陷。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末帚称,一起剝皮案震驚了整個濱河市官研,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闯睹,老刑警劉巖戏羽,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瞻坝,居然都是意外死亡蛛壳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進(jìn)店門所刀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙荐,“玉大人,你說我怎么就攤上這事浮创∮且鳎” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵斩披,是天一觀的道長溜族。 經(jīng)常有香客問我,道長垦沉,這世上最難降的妖魔是什么煌抒? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮厕倍,結(jié)果婚禮上寡壮,老公的妹妹穿的比我還像新娘。我一直安慰自己讹弯,他們只是感情好况既,可當(dāng)我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著组民,像睡著了一般棒仍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上臭胜,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天莫其,我揣著相機(jī)與錄音,去河邊找鬼庇楞。 笑死榜配,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吕晌。 我是一名探鬼主播蛋褥,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睛驳!你這毒婦竟也來了烙心?” 一聲冷哼從身側(cè)響起膜廊,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淫茵,沒想到半個月后爪瓜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡匙瘪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年铆铆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹喻。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡薄货,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碍论,到底是詐尸還是另有隱情谅猾,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布鳍悠,位于F島的核電站税娜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏藏研。R本人自食惡果不足惜敬矩,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蠢挡。 院中可真熱鬧谤绳,春花似錦、人聲如沸袒哥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堡称。三九已至,卻和暖如春艺演,著一層夾襖步出監(jiān)牢的瞬間却紧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工胎撤, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留晓殊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓伤提,卻偏偏與公主長得像巫俺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子肿男,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容