第16章 集合類

1. 集合的概念

集合是一種數(shù)學概念,表述的是“由一個或多個確定的元素所構成的整體”。

1.1 Java中集合與數(shù)組的比較

數(shù)組

  • 數(shù)組在初始化時是定長的,初始化后數(shù)組長度不可改變。(長度表示的是最大容量)
  • 數(shù)組中只能存放同類型的數(shù)據(jù),比如int數(shù)組只能存數(shù)字不能存String
  • 數(shù)組中沒有現(xiàn)成API方法使用,數(shù)組只有一個length屬性可用

集合

  • 集合在初始化時可以不設置長度,隨著元素放入集合,長度可變。(集合中已經存放了多少個元素)
  • 集合在沒有約束泛型的情況下,其可以存放任意類型的數(shù)據(jù)
  • 集合類中提供了大量的API方法,方便程序開發(fā)者操作集合
1.2 集合的分類
集合的繼承體系
  • Collection接口(線性集合)
    (1) List接口(有序號可重復)
    ArrayList實現(xiàn)類基于數(shù)組方式實現(xiàn)
    LinkedList實現(xiàn)類基于鏈表方式實現(xiàn)
    (2) Set接口(無序號不重復)
    HashSet實現(xiàn)類基于Hash表算法
  • Map接口(鍵值集合)
    HashMap實現(xiàn)類基于Hash表算法
  • Iterator接口(迭代器)

2. 線性集合(Collection接口)

2.1 線性集合的概念

Collection接口表述的是“線性集合”,線性結構是n個數(shù)據(jù)元素的有序(次序)集合
線性集合的特征
1.集合中必存在唯一的一個"第一個元素"。
2.集合中必存在唯一的一個"最后的元素"。
3.除最后元素之外,其它數(shù)據(jù)元素均有唯一的"后繼"。
4.除第一元素之外,其它數(shù)據(jù)元素均有唯一的"前驅"。

2.2 線性集合(Collection)的實現(xiàn)類
2.2.1 List接口

有序集合(也稱為序列 )。 該界面的用戶可以精確控制列表中每個元素的插入位置。 用戶可以通過整數(shù)索引(下標)訪問元素,并搜索列表中的元素。允許集合中存放重復的數(shù)據(jù)。
有序號可重復

2.2.1.1 ArrayList實現(xiàn)類

底層是基于數(shù)組實現(xiàn)的,方法都是線程不安全的
API方法

  • add(Object o)返回值是boolean
    在集合的結尾處添加一個元素
  • add(int index, Object o)返回值是void
    在index位置插入一個元素,插入后,后面的元素會自動后移
  • addAll(Collection c)返回值是boolean
    在集合結尾處添加集合c中的所有元素
  • clear()返回值是void
    清空集合,保留集合對象
  • contains(Object o)返回值是boolean
    在集合中查找o是否存在,存在判斷依據(jù)是元素是否“equals”o
    如果在集合中找到了“相等”的元素,返回true,否則返回false
  • get(int index)返回值是Object
    獲得集合中index位置上的元素,不會移除這個元素
  • set(int index, Object o)返回值是Object
    用指定元素o替換集合中index位置上的元素,并返回被替代的元素
  • remove(int index)返回值是Object
    移除集合中index位置上的元素,并返回這個元素
  • remove(Object o)返回值是boolean
    移除集合中的第一個o元素(判斷依據(jù)是集合中的某個元素“equals”o),返回是否移除成功。
  • size()返回值是int
    返回集合中元素的個數(shù)(集合的長度)
  • indexOf(Object o)返回值是Object
    查找o在集合中位置,如果沒找到返回-1
public class Test {

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        System.out.println(list.size());
        list.add(new String("哈哈"));
        list.add(23);
        list.add(new Date());
        list.add("哈哈");
        list.add(3.14);
        System.out.println(list.size());
        System.out.println("插入了5個元素==========================");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("在2位置插入了嘻嘻==========================");
        list.add(2, "嘻嘻");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("替換2位置為嘿嘿==========================");
        list.set(2, "嘿嘿");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("移除位置2的元素==========================");
        list.remove(2);
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("移除元素“3.14”的元素==========================");
        list.remove(3.14);
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("移除元素“哈哈”的元素==========================");
        list.remove(new String("哈哈"));
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("判斷集合中是否有“哈哈”的元素==========================");
        boolean b = list.contains("哈哈");
        System.out.println(b);
        System.out.println("判斷集合中是否有“3.14”的元素==========================");
        boolean x = list.contains(3.14);
        System.out.println(x);
        System.out.println("清空集合==========================");
        list.clear();
        System.out.println(list.size());
    }
}

如果遇到了集合中有元素2,且還需要移除2下標時,注意寫法

list.remove(2);  //移除下標位置2的元素
list.remove(new Integer(2)); //移除集合中元素2

2.2.1.2 LinkedList實現(xiàn)類

底層是基于鏈表實現(xiàn)的
主要的增刪改查的方法參照ArrayList類的API學習
特有API方法

  • addFirst(Object o)和addLast(Object o)返回值void
    在集合的開始和結尾處插入一個元素o
  • getFirst()和getLast()返回值Object
    返回集合的開始和結尾處的元素

將LinkedList看做為一個棧

  • peek()返回值Object
    查看棧頂元素,但不移除它
  • poll()和pop()返回值Object
    出棧
  • push(Object o)返回值void
    將Object o入棧
public class Test3 {
    public static void main(String[] args) {
        LinkedList<Object> list = new LinkedList<Object>();     
        list.push("哈哈");
        list.push("嘻嘻");
        list.push(100);
        list.push(3.14);
        list.push(new Date());
        
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("查看棧頂==============================");
        Object x = list.peek(); //查看棧頂
        System.out.println(x);
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("出棧==============================");
        Object y = list.pop();
        System.out.println(y);
        System.out.println("====");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("入棧==============================");
        list.push("abc");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
        System.out.println("出棧==============================");
        Object z = list.poll();
        System.out.println(z);
        System.out.println("====");
        for(int i = 0; i < list.size(); i++) {
            Object o = list.get(i);
            System.out.println(o);
        }
    }
}
2.2.1.3 Vector實現(xiàn)類

底層是基于數(shù)組實現(xiàn)的,方法都是線程安全的
API方法參照ArrayList學習

筆試題/面試題
(1)ArrayList和LinkedList的區(qū)別?
本質上就是問“數(shù)組”與“鏈表”的區(qū)別。
ArrayList基于數(shù)組實現(xiàn)的,LinkedList基于鏈表實現(xiàn)
數(shù)組:在查詢元素速度快,插入刪除速度慢
鏈表:在查詢元素速度慢,插入刪除速度快

(2)ArrayList和Vector的區(qū)別?
ArrayList線程不安全,Vector線程安全

2.2.2 Set接口

不包含重復元素的集合。如果有元素e1和e2(e1.equals(e2) 為true),e1和e2只能保留一個元素。Set接口的特征是使用者無法控制元素的存放順序,其存放順序有Set接口的實現(xiàn)類自身的算法決定

2.2.2.1 HashSet實現(xiàn)類

實現(xiàn)算法是基于Hash表算法,存放順序不由使用者控制,允許存放一個null值
Object類中有hashcode方法,返回int值,該int值是由對象的內存地址計算出來的
一個對象會有一個HashCode(哈希碼,Hash碼,HashCode),它有如下特征:

  • 如果兩個對象擁有相同的內存地址,他們一定有相同的哈希碼
  • 如果兩個對象擁有相同的哈希碼,他們的內存地址可能不一樣
    哈希表算法,按照哈希碼的大小順序存放數(shù)據(jù)。

比如:字符串“哈哈”的哈希碼是34,字符串“嘻嘻”的哈希碼是20。
使用代碼存入集合的順序:哈哈,嘻嘻
實際集合中存儲順序:嘻嘻,哈哈

比如:字符串“哈哈”的哈希碼是34,字符串“嘻嘻”的哈希碼是20,字符串“嘿嘿”的哈希碼也是34。
使用代碼存入集合的順序:哈哈,嘻嘻
實際集合中存儲順序:嘻嘻,(哈哈,嘿嘿)


類似的存儲結構

API方法

  • add(Object o)返回值boolean
    將o對象放入集合,放入的位置由集合的算法自身決定
  • clear() 返回值void
    清空集合
  • isEmpty()返回值boolean
    判斷集合是否為空
  • remove(Object o)返回值boolean
    在集合中移除o元素
  • size()返回值int
    返回集合的長度
  • iterator()返回值Iterator
    返回set集合對應的迭代器

因為Set集合無法使用下標,所以不能通過下標進行遍歷訪問集合中的元素。只能通過迭代器去遍歷Set集合

2.2.3 Iterator(迭代器)

負責通過一個向前的“游標”不斷的訪問元素和移動,完成Set集合的遍歷。


理解迭代器

API方法

  • hasNext()返回值:boolean
    判斷游標當前位置之后是否有數(shù)據(jù)
  • next()返回值:Object
    取得游標當前位置之后的數(shù)據(jù),再將游標向后移動一位
    由于next()包含了移動的操作,所以在循環(huán)中應該只調用一次

示例:利用迭代器遍歷set集合

public class Test1 {

    public static void main(String[] args) {
        HashSet<Object> set = new HashSet<Object>();
        set.add("哈哈");
        set.add(3.14);
        set.add(new Date());
        set.add("hehe");
        set.add(23);    
        set.add(12);
        System.out.println(set.size());
        
        Iterator<Object> ite = set.iterator();//獲得了set集合對應的迭代器
        while(ite.hasNext()) {
            Object o = ite.next();
            System.out.println(o);
        }
    }
}

面試題/筆試題:
(1)List集合和Set集合的區(qū)別?
List集合:有序可重復,可以通過下標訪問數(shù)據(jù),存放順序可控,允許存放重復數(shù)據(jù)
Set集合:無序不重復,不能通過下標訪問數(shù)據(jù),存放數(shù)據(jù)由實現(xiàn)類自己決定,重復數(shù)據(jù)只能保留一個
(2)HashSet和TreeSet的區(qū)別?
HashSet基于哈希表算法實現(xiàn)
TreeSet基于紅黑樹算法實現(xiàn)

很多時候使用Set集合完成去重復的操作。

3. 鍵值集合(Map接口)

Map集合是一種以鍵值對為單位,進行數(shù)據(jù)存儲的一種結構,它與線性集合有所區(qū)別:線性集合一個位置上只存儲一個數(shù)據(jù),鍵值對集合一個位置上存儲的是一個鍵(key)和一個值(value)。不同數(shù)據(jù)之間鍵(key)是不能重復的,值(value)是也可重復的。存放數(shù)據(jù)時,必須提供key和value,取得數(shù)據(jù)時一般是根據(jù)key來獲取value
Map的主要實現(xiàn)類

3.1 HashMap和HashTable

基于哈希表算法實現(xiàn)存儲

3.1.1 構造方法
  • HashMap()創(chuàng)建一個初始容量是16,負載因子是0.75的默認的對象
    初始容量:對象初始默認可以存放16個鍵值對
    負載因子:表示當容量達到最大容量的75%時(比如,初始16,16的75%是12),,容量會加倍擴容(16*2=32)。
    這個構造方法是比較常用的
  • HashMap(int init)創(chuàng)建一個初始容量是init,負載因子是0.75的對象
  • HashMap(int init, float ext)創(chuàng)建一個初始容量是init,負載因子是ext的對象
  • HashMap(Map map)利用map的數(shù)據(jù)重新構建一個同樣數(shù)據(jù)的HashMap
3.1.2 API方法
  • put(Object key, Object value)返回值:Object
    將key-value組成的鍵值對,放入集合;如果集合中已經存在了同key的鍵值對,會利用新的key-value替換原有的key-value
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(101, "趙四");  //向集合中添加一條數(shù)據(jù)
map.put(102, "劉能");  //向集合中添加一條數(shù)據(jù)
map.put(101, "哈哈");  //替換集合中原來101這條數(shù)據(jù)
  • remove(Object key)返回值:Object
    根據(jù)key值,刪除集合中的一個鍵值對,并返回對應value
    這個方法是使用頻率較高的
map.remove(103);
  • remove(Object key, Object value)返回值:boolean
    根據(jù)key-value值,刪除集合中的一個鍵值對。返回是否刪除成功
map.remove(104,"劉能"); //成功,有這對數(shù)據(jù)
map.remove(102,"aaa");//失敗,雖然有102,102中不是aaa
  • clear()返回值:void
    清空整個集合
map.clear();
  • get(Object key)返回值:Object
    根據(jù)key值,返回集合中key對應value,如果集合中沒有這個key,返回null
String v1 = map.get(103);
String v2 = map.get(105);
  • size()返回值:int
    返回集合中鍵值對的個數(shù)
System.out.println("集合中的元素個數(shù)是:"+map.size());    
  • entrySet()返回值:Set<Entry>
    一個entry對象表示一個鍵值對
    返回所有鍵值對組成的一個Set集合
Set<Entry<Integer, String>> set = map.entrySet();//得到鍵值對的Set集合
Iterator<Entry<Integer, String>> ite = set.iterator();//將set集合變成迭代器
while(ite.hasNext()) { //遍歷迭代器
    Entry<Integer, String> e = ite.next(); //一個鍵值對
    System.out.println(e.getKey()+":"+e.getValue());
}
  • keySet()返回值:Set<Object>
    返回所有key值組成的一個Set集合
Set<Integer> kset = map.keySet(); //所有key值組成的set集合
Iterator<Integer> kite = kset.iterator(); //將set集合變成迭代器
while(kite.hasNext()) { //遍歷迭代器
    int key = kite.next();
    String value = map.get(key); //取得value
    System.out.println(key+":"+value);
}
  • isEmpty()返回值:boolean
    判斷集合是否為空集合
boolean x = map.isEmpty();
  • containsKey(Object key)返回值boolean
    判斷集合中的key是否包含參數(shù)key,(判斷依據(jù):equals)
boolean b1 = map.containsKey(101);
boolean b2 = map.containsKey(105);
  • containsValue(Object value)返回值boolean
    判斷集合中的value是否包含參數(shù)value,(判斷依據(jù):equals)
boolean b3 = map.containsValue("劉能");
boolean b4 = map.containsValue("AAA");

面試題/筆試題
(1)HashMap與HashTable的區(qū)別
HashMap是線程不安全的,HashTable是線程安全。
HashMap中允許null作為key,HashTable不允許null作為key

(2)HashMap與TreeMap的區(qū)別
HashMap底層基于Hash表算法
TreeMap底層基于二叉樹算法

(3)HashMap的桶容量(擴容機制)
以默認hashmap()構造方法解釋:,默認初始值16,負載因子0.75.
初始時hashmap的最大容量是16,當存儲數(shù)據(jù)至12時(16*0.75),進行擴容,擴容方式是16*2=32; 當繼續(xù)存儲數(shù)據(jù)至24時(32*0.75),繼續(xù)擴容,擴容方式32*2=64

3.2 TreeMap

基于紅黑樹算法實現(xiàn)存儲
參照HashMap的API去理解

  • put
  • get
  • remove
  • keySet
  • entrySet
  • containsKey

4. 泛型

泛型是用于約束集合中輸入數(shù)據(jù)類型的。集合默認是可以將任何數(shù)據(jù)都放入其中,很多情況下我們需要約束集合中輸入的元素類型,借助于泛型可以達到效果。
泛型的優(yōu)勢

  • 約束輸入至集合的數(shù)據(jù)類型,控制數(shù)據(jù)準確
  • 從集合輸出數(shù)據(jù)時,無需考慮類型轉換問題
  • 可以使用for...each句式快速遍歷集合

約束輸入類型為String

ArrayList<String> list = new ArrayList<String>();

由于泛型約束只支持引用數(shù)據(jù)類型,如果需要約束一個整數(shù)集合,需要使用包裝類

ArrayList<Integer> list = new ArrayList<Integer>();

泛型約束Employee類的集合

ArrayList<Employee> list = new ArrayList<Employee>();

泛型約束其他的集合

ArrayList<HashSet<String>> list = new ArrayList<HashSet<String>>();

for...each句式:使用變量s去遍歷list集合中每(each)一個元素

ArrayList<String> list = new ArrayList<String>();
for(String s : list) {
    System.out.println(s);
}

5. Collections類(集合工具類)

主要提供了大量的關于集合操作的方法,方便操作集合

  • copy(List dest, List src)返回值:void
    將集合src復制到集合dest中
    list2一開始必須有不少于list1的數(shù)據(jù),否則會報出異常
public class Test2 {

    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        Random r = new Random();
        for(int i = 0; i < 10; i++) {
            int x = r.nextInt(100);
            list1.add(x);
        }
        for(int i = 0; i < 10; i++) {
            int x = r.nextInt(100);
            list2.add(x);
        }
        System.out.println(list1);
        System.out.println(list2);
        System.out.println("=======================");
        //集合copy
        Collections.copy(list2, list1);
        
        System.out.println(list1);
        System.out.println(list2);
    }
}
  • sort(List list)返回值:void
    將集合按照“自然排序”(實際上就是升序)
public class Test3 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        Random r = new Random();
        for(int i = 0; i < 10; i++) {
            int x = r.nextInt(100);
            list1.add(x);
        }
        System.out.println(list1);
        Collections.sort(list1);//升序
        System.out.println(list1); 
    }
}

如果想比較的是自己編寫的類,可以通過讓自己的類實現(xiàn)Comparable接口并完成CompareTo方法
Student類

public class Student implements Comparable<Student>{
    
    private int sno;
    private String sname;
    
    public Student() {}
    
    public Student(int sno, String sname) {
        this.sno = sno;
        this.sname = sname;
    }

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public int compareTo(Student o) {
        if(this.sno < o.sno) {
            return -1;
        }else if(this.sno == o.sno) {
            return 0;
        }else {
            return 1;
        }
    }
}

利用sort方法進行排序

public class Test {

    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<Student>();
        
        Student s1 = new Student(1002,"趙四");
        Student s2 = new Student(1004,"劉能");
        Student s3 = new Student(1001,"謝廣坤");
        Student s4 = new Student(1003,"王大拿");
        
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        
        for(Student s : list) {
            System.out.println(s.getSno()+"\t"+s.getSname());
        }
        System.out.println("============================");
        Collections.sort(list);
        
        for(Student s : list) {
            System.out.println(s.getSno()+"\t"+s.getSname());
        }   
    }
}
  • sort(List list, Comparator c)返回值:void
    將集合按照“ Comparator”的規(guī)則進行排序
    Student類
public class Student{
    
    private int sno;
    private String sname;
    
    public Student() {}
    
    public Student(int sno, String sname) {
        this.sno = sno;
        this.sname = sname;
    }

    public int getSno() {
        return sno;
    }

    public void setSno(int sno) {
        this.sno = sno;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

}

編寫一個比較器

public class StudentComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        if(o1.getSno() < o2.getSno()) {
            return -1;
        }else if(o1.getSno() == o2.getSno()) {
            return 0;
        }else {
            return 1;
        }
    }
}

利用sort進行排序

public class Test {

    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<Student>();
        
        Student s1 = new Student(1002,"趙四");
        Student s2 = new Student(1004,"劉能");
        Student s3 = new Student(1001,"謝廣坤");
        Student s4 = new Student(1003,"王大拿");
        
        list.add(s1);
        list.add(s2);
        list.add(s3);
        list.add(s4);
        
        for(Student s : list) {
            System.out.println(s.getSno()+"\t"+s.getSname());
        }
        System.out.println("============================");
        Collections.sort(list, new StudentComparator());
        
        for(Student s : list) {
            System.out.println(s.getSno()+"\t"+s.getSname());
        }       
    }
}
  • reverse(List list)返回值:void
    將集合list中的所有數(shù)據(jù)反轉
public class Test3 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        Random r = new Random();
        for(int i = 0; i < 10; i++) {
            int x = r.nextInt(100);
            list1.add(x);
        }
        System.out.println(list1);
        Collections.sort(list1);//升序
        System.out.println(list1); 
        Collections.reverse(list1);//降序
        System.out.println(list1); 
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容