Java中List集合與Set集合


一、List 集合

1、List 接口特點

2、List 接口中常用的方法

3、List 集合存儲數據結構

4、ArrayList 集合ArrayList 集合是最常用的集合,是用存儲數據結構,元素增刪慢,查找快。

5、LinkedList 集合

6、Vector 集合

二、Set 接口

1、HashSet 集合

2、HashSet 集合存儲數據的結構(哈希表)

3、String 類的哈希值

4、自定義對象重寫hashCode和equals

5、LinkedHashSet集合


一、List 集合

List 是一個接口,是有序的 collection,此接口的用戶可以對列表中每個元素的插入位置進行精確的控制,用戶可以根據元素的整數索引訪問元素,並搜索列表中的元素。List 接口允許存放重復的元素,並且元素都是有序的(Set 接口不允許存放重復元素,元素是無序的)

1、List 接口特點

  • 它是一個有序的集合
  • 他是一個帶索引的集合,通過索引就可以精確地操作集合中的元素(與數組的索引是一個道理)
  • 集合中可以有重復的元素,可以通過 equals 方法來比較是否為重復的元素
  • List 接口常用的子類有:ArrayList 集合、LinkedList 集合

2、List 接口中常用的方法

  • boolean add(Object e):向集合末尾添加指定元素
  • void add(int index,Object e):向集合指定索引處添加指定元素,原有元素依次后移
  • remove(Object e):將指定元素對象從集合中刪除,返回被刪除的元素
  • remove(int index):將指定索引處的元素從集合中刪除,返回被刪除的元素
  • set(int index,Object e):將指定索引處的元素替換成指定的元素,返回替換前的元素
  • get(int index):獲取指定索引處的元素,返回該元素
  1.  
    public static void main(String[] args)
  2.  
    {
  3.  
    List<String> L = new ArrayList<>();
  4.  
    //末尾添加元素
  5.  
    L.add( "abc");
  6.  
    L.add( "bcd");
  7.  
    L.add( "cde");
  8.  
    L.add( "def");
  9.  
    L.add( "efg");
  10.  
    //指定位置添加元素
  11.  
    L.add( 2,"fgh");
  12.  
    //刪除指定元素
  13.  
    L.remove( "abc");
  14.  
    //刪除指定索引元素
  15.  
    L.remove( 1);
  16.  
    //將指定索引處的元素替換成指定元素
  17.  
    L.set( 1,"hello");
  18.  
    //獲取指定索引處的元素
  19.  
    L.get( 1);
  20.  
    //使用迭代器獲取出集合中的元素,最好使用listIterator進行迭代
  21.  
    Iterator<String> it = L.listIterator();
  22.  
    while(it.hasNext())
  23.  
    {
  24.  
    System.out.println(it.next());
  25.  
    }
  26.  
    //由於List集合是有索引的,還可以使用索引進行迭代
  27.  
    for(int i = 0;i < L.size();i++)
  28.  
    {
  29.  
    System.out.println(L.get(i));
  30.  
    }
  31.  
    }

注:

  • List 集合是帶索引的有序集合,因此除了使用迭代器進行獲取元素外,還可以使用索引下表進行元素獲取
  • Iterator 迭代 List 異常問題:在迭代過程中,如果要添加一個新的元素,使用集合的方法對元素進行操作,會導致迭代器不知道集合中的變化,容易發生數據的不確定性。因此,通過 listIterator 迭代能避免這個異常。

3、List 集合存儲數據結構

List 接口下有多個集合,它們存儲元素所采用的數據結構方式有所不同,這就導致了不同集合有其不同特點,供程序員在不同的環境下使用。

數據存儲的常用結構有:堆棧、隊列、數組、鏈表

  • 堆棧
    (1) 先進后出
    (2) 棧的入口、出口都是棧的頂端位置
    (3) 壓棧:即存元素
    (4) 出棧:即取元素
  • 隊列
    (1) 先進先出
    (2) 隊列的入口、出口為兩端
  • 數組
    (1) 查找元素快:通過索引快速訪問指定元素位置
    (2) 增刪元素慢:指定位置增加、刪除元素都需要創建一個新的數組,將指定新元素存儲在指定索引位置,再把原數組索引根據索引復制到新數組對應索引位置
  • 鏈表
    (1) 多個節點之間,通過地址進行連接
    (2) 查找元素慢:想要查找某個元素,需要通過連接的節點,依次向后查找指定元素
    (3) 增刪元素快:只需修改連接下個元素的地址即可

4、ArrayList 集合
ArrayList 集合是最常用的集合,是用存儲數據結構,元素增刪慢,查找快。

在我的另一篇博客:Java中ArrayList集合有其的介紹,這里不多說

5、LinkedList 集合

LinkedList 集合數據存儲的結構是鏈表結構,對元素的增刪很方便,實際開發中對一個集合元素的增刪經常涉及到首位操作,而 LinkedList 集合提供了大量的首位操作方法。

  • void addFirst(E e):將指定元素插入鏈表的開頭
  • void addLast(E e):將指定元素插入鏈表的結尾
  • E getFirst():返回鏈表的第一個元素
  • E getLast():返回鏈表的最后一個元素
  • E removeFirst():移除並返回鏈表的第一個元素
  • E removeLast():移除並返回鏈表的最后一個元素
  • E pop(E e):取出鏈表棧頂元素
  • void push(E e):將元素推入此鏈表所示的堆棧
  • boolean isEmpty():判斷鏈表中是否有元素

邏輯實例:

  1.  
    public static void main(String[] args)
  2.  
    {
  3.  
    //創建鏈表
  4.  
    LinkedList<String> link = new LinkedList<>();
  5.  
    //添加元素
  6.  
    link.add( "abc");
  7.  
    link.add( "bcd");
  8.  
    link.add( "cde");
  9.  
    link.add( "def");
  10.  
    //獲取元素
  11.  
    System.out.println(link.getFirst());
  12.  
    System.out.println(link.getLast());
  13.  
    //刪除元素
  14.  
    System.out.println(link.remove( "abc"));
  15.  
    System.out.println(link.removeFirst());
  16.  
     
  17.  
    while (link.isEmpty()) //判斷集合是否有元素
  18.  
    {
  19.  
    System.out.println(link.pop()); //取出棧頂元素
  20.  
    }
  21.  
    }

6、Vector 集合

Vector 集合數據存儲結構是數組結構,與 ArrayList 不同之處在於提供了一個獨特的取出方法:枚舉Enumeration,與 Iterator 接口功能類似。Vector 集合已被 ArrayList 集合替代,枚舉Enumeration 已被迭代器 Iterator 替代(這里盜用一張圖)

二、Set 接口

Set 集合里面存儲的是無序的不重復元素,沒有索引,可以采用迭代器和增強for來獲取元素,Set 常用的子類有 HashSet、LinkedHashSet 集合,可以通過 equals 方法來判斷是否為重復元素。

1、HashSet 集合

HashSet 類實現 Set 接口,由哈希表支持(實際上是一個 HashMap 集合),HashSet 集合不能保證迭代順序與元素存儲順序相同,采用哈希表結構存儲數據結構,保證元素唯一性的方式依賴於:hashCode() 於 equals() 方法。

  • 特點:無序集合,存儲和取出的順序不同,沒有索引,不存儲重復元素

  • 在代碼編寫上和 ArrayList 完全一致

  • 存儲、取出數據都比較快

  • 線程不安全,運行速度快

  • 底層數據結構為哈希表(鏈表數組結合體)

  1.  
    public static void main(String[] args)
  2.  
    {
  3.  
    //使用多態創建哈希表
  4.  
    Set<String> S = new HashSet<>();
  5.  
    S.add( "abc");
  6.  
    S.add( "bcd");
  7.  
    S.add( "cde");
  8.  
    //使用迭代器獲取元素
  9.  
    Iterator<String> it = S.iterator();
  10.  
    while (it.hasNext())
  11.  
    {
  12.  
    System.out.println(it.next());
  13.  
    }
  14.  
    //使用增強獲取元素
  15.  
    for(String s : S)
  16.  
    {
  17.  
    System.out.println(s);
  18.  
    }
  19.  
    }

2、HashSet 集合存儲數據的結構(哈希表)

哈希表介紹:

哈希表底層使用的是數組機制,數組中也存放對象,而這些對象往數組中存放時的位置比較特殊,當需要把對象在這些數組中存放時,會根據這些對象特有的數據來結合相應的算法,計算出這個對象在數組中的位置,然后把這個對象存放在數組中。這樣的數組就稱為哈希數組,即哈希表。

在哈希表存儲數組時,會先記錄第一個元素的地址,繼續存儲時,會讓先來的元素記錄后來的地址,在這里有一個“桶”和“加載因子”的概念,桶:數組的初始容量,初始容量為16;加載因子:數組的長度百分比,默認為0.75。數組的長度:16*0.75 = 12;當存放的數據超出數組長度 12 時,數組就會進行擴容,即復制(這個過程很耗費資源),這個過程也稱為數據的再哈希 rehash。

當向哈希表存放元素時,會根據元素的特有數據結合響應的算法,這個算法就是 Object 類中的 hashCode 方法。由於任何對象都是 Object 類的子類,所以任何對象都有這個方法,即:在哈希表中存放對象時,會調用對象的 hashCode 方法,算出對象在表中的位置,需要注意的是,如果兩個對象 hashCode 方法算出結果一樣,稱為哈希沖突,這樣會調用對象 equals 方法來比較兩個對象是不是同一個對象,如果返回 true,則把第一個對象存放在哈希表中,如果返回 false,就會把這兩個值都存放在哈希表中。

總結:保證 HashSet 集合元素的唯一,其實就是根據對象 hashCode 和 equals 方法來決定的。如果往集合中存放自定義對象,為了保證唯一性,就必須重寫 hashCode 和 equals 方法建立屬於當前對象的比較方法。

3、String 類的哈希值

哈希值表示普通的十進制整數, 是父類 Object 方法 public int hashCode() 的計算結果,String 類繼承了Object,重寫了 hashCode 方法。String 類中 hashCode 源碼:

  1.  
    public int hashCode() {
  2.  
    int h = hash; //一開始變量 hash 為 0
  3.  
    if (h == 0 && value.length > 0) {
  4.  
    char val[] = value;
  5.  
    //返回字符串ASCII經過計算的和
  6.  
    for (int i = 0; i < value.length; i++) {
  7.  
    h = 31 * h + val[i];
  8.  
    }
  9.  
    hash = h;
  10.  
    }
  11.  
    return h;
  12.  
    }

因此,當使用String類定義兩個對象:String S1 = new String("abc"); String S2 = new String("abc"); 對象S1和S2哈希值是相同的,然后集合會讓后來的對象調用 equals 方法,如果返回 true,則集合判定元素重復,將其去除。

4、自定義對象重寫hashCode和equals

給HashSet中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的對象唯一。

創建Person類,在類中重寫 hashCode 方法和 equals 方法

  1.  
    public class Person {
  2.  
    private String name;
  3.  
    private int age;
  4.  
    public Person(String name,int age)
  5.  
    {
  6.  
    this.name = name;
  7.  
    this.age = age;
  8.  
    }
  9.  
    public void setName(String name)
  10.  
    {
  11.  
    this.name = name;
  12.  
    }
  13.  
    public void setAge(int age)
  14.  
    {
  15.  
    this.age = age;
  16.  
    }
  17.  
    public String getName()
  18.  
    {
  19.  
    return name;
  20.  
    }
  21.  
    public int getAge()
  22.  
    {
  23.  
    return age;
  24.  
    }
  25.  
    public String toString() {
  26.  
    return name + age;
  27.  
    }
  28.  
    //重寫hashCode方法
  29.  
    public int hashCode()
  30.  
    {
  31.  
    return name.hashCode() + age;
  32.  
    }
  33.  
    //重寫equals方法
  34.  
    public boolean equals(Object obj)
  35.  
    {
  36.  
    if(this == obj)
  37.  
    return true;
  38.  
    if(obj == null)
  39.  
    return false;
  40.  
    if(obj instanceof Person)
  41.  
    {
  42.  
    Person P = (Person)obj;
  43.  
    return name.equals(obj.name) && age == P.age;
  44.  
    }
  45.  
    return false;
  46.  
    }
  47.  
    }

在main中調用,由於重寫了 hashCode 和 equals 方法,所以相同類型元素將不會打印出

  1.  
    public static void main(String[] args)
  2.  
    {
  3.  
    //創建存儲Person類的哈希表
  4.  
    HashSet<Person> H = new HashSet<>();
  5.  
    H.add( new Person("a",18));
  6.  
    H.add( new Person("a",18));
  7.  
    H.add( new Person("b",19));
  8.  
    H.add( new Person("c",20));
  9.  
    System.out.println(H);
  10.  
    }

5、LinkedHashSet集合

LinkedHashSet 類是基於鏈表的哈希表的實現,繼承自 HashSet,是 Set 接口的實現,此實現與 HashSet 的不同之外在於,后者維護着一個運行於所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,即按照將元素插入到 set 中的順序(插入順序)進行迭代。

  • LinkedHashSet  特點:具有順序,存儲和取出元素順序相同
  1.  
    public class LinkedHashSetDemo {
  2.  
     
  3.  
    public static void main(String[] args) {
  4.  
    LinkedHashSet<Integer> link = new LinkedHashSet<Integer>();
  5.  
    link.add( 123);
  6.  
    link.add( 44);
  7.  
    link.add( 33);
  8.  
    link.add( 33);
  9.  
    link.add( 66);
  10.  
    link.add( 11);
  11.  
    System.out.println(link);
  12.  
    }
  13.  
    }

hashCode 和 equals 方法的面試題

(1) 兩個對象 Person P1 P2,如果兩個對象的哈希值相同,則兩個對象的 equals 一定返回 true 嗎?

不一定為 true

(2) 兩個對象 Person P1 P2,如果兩個對象的 equals 方法返回 true,則兩個對象的哈希值一定相同嗎?

一定相同


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM