傳智黑馬java基礎學習——day19(集合list、Set)

高數老師發表於2019-01-23

 

第19天 集合

  1. List介面

我們掌握了Collection介面的使用後,再來看看Collection介面中的子類,他們都具備那些特性呢?

接下來,我們一起學習Collection中的常用幾個子類(List集合、Set集合)。

    1. List介面介紹

查閱API,看List的介紹。有序的 collection(也稱為序列)。此介面的使用者可以對列表中每個元素的插入位置進行精確地控制。使用者可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。與 set 不同,列表通常允許重複的元素。

看完API,我們總結一下:

List介面:

  1. 它是一個元素存取有序的集合。例如,存元素的順序是11、22、33。那麼集合中,元素的儲存就是按照11、22、33的順序完成的)。
  2. 它是一個帶有索引的集合,通過索引就可以精確的操作集合中的元素(與陣列的索引是一個道理)。
  3. 集合中可以有重複的元素,通過元素的equals方法,來比較是否為重複的元素。

 

List介面的常用子類有:

  1. ArrayList集合
  2. LinkedList集合
    1. List介面中常用的方法

  1. 增加元素方法
  2. add(Object e):向集合末尾處,新增指定的元素
  3. add(int index, Object e):向集合指定索引處,新增指定的元素,原有元素依次後移
  4. 刪除元素刪除
  5. remove(Object e):將指定元素物件,從集合中刪除,返回值為被刪除的元素
  6. remove(int index):將指定索引處的元素,從集合中刪除,返回值為被刪除的元素
  7. 替換元素方法
  8. set(int index, Object e):將指定索引處的元素,替換成指定的元素,返回值為替換前的元素
  9. 查詢元素方法
  10. get(int index):獲取指定索引處的元素,並返回該元素

 

方法演示:

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

//1,新增元素。

list.add("小紅");

list.add("小梅");

list.add("小強");

//2,插入元素。插入元素前的集合["小紅","小梅","小強"]

list.add(1, "老王"); //插入元素後的集合["小紅","老王","小梅","小強"]

//3,刪除元素。

list.remove(2);// 刪除元素後的集合["小紅","老王","小強"]

//4,修改元素。

list.set(1, "隔壁老王");// 修改元素後的集合["小紅","隔壁老王","小強"]

 

Iterator<String> it = list.iterator();

while (it.hasNext()) {

String str = it.next();

System.out.println(str);

}

由於List集合擁有索引,因此List集合迭代方式除了使用迭代器之外,還可以使用索引進行迭代。

for (int i = 0; i < list.size(); i++) {

String str = list.get(i);

System.out.println(str);

}

 

      1. Iterator的併發修改異常

在list集合迭代元素中,對元素進行判斷,一旦條件滿足就新增一個新元素。程式碼如下

public class IteratorDemo {

//在list集合迭代元素中,對元素進行判斷,一旦條件滿足就新增一個新元素

public static void main(String[] args) {

//建立List集合

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

//給集合中新增元素

list.add("abc1");

list.add("abc2");

list.add("abc3");

list.add("abc4");

//迭代集合,當有元素為"abc2"時,集合加入新元素"itcast"

Iterator<String> it = list.iterator();

while(it.hasNext()){

String str = it.next();

//判斷取出的元素是否是"abc2",是就新增一個新元素

if("abc2".equals(str)){

list.add("itcast");// 該操作會導致程式出錯

}

}

//列印容器中的元素

System.out.println(list);

}

}

執行上述程式碼發生了錯誤 java.util.ConcurrentModificationException這是什麼原因呢?

在迭代過程中,使用了集合的方法對元素進行操作。導致迭代器並不知道集合中的變化,容易引發資料的不確定性。

併發修改異常解決辦法:在迭代時,不要使用集合的方法操作元素。

那麼想要在迭代時對元素操作咋辦?通過ListIterator迭代器操作元素是可以的,ListIterator的出現,解決了使用Iterator迭代過程中可能會發生的錯誤情況。

 

    1. List集合儲存資料的結構

List介面下有很多個集合,它們儲存元素所採用的結構方式是不同的,這樣就導致了這些集合有它們各自的特點,供給我們在不同的環境下進行使用。資料儲存的常用結構有:堆疊、佇列、陣列、連結串列。我們分別來了解一下:

  1. 堆疊,採用該結構的集合,對元素的存取有如下的特點:
  2. 先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,然後才能彈出下面的子彈。
  3. 棧的入口、出口的都是棧的頂端位置
  4. 壓棧:就是存元素。即,把元素儲存到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
  5. 彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

 

  1. 佇列,採用該結構的集合,對元素的存取有如下的特點:
  2. 先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,安檢。排成一列,每個人依次檢查,只有前面的人全部檢查完畢後,才能排到當前的人進行檢查。
  3. 佇列的入口、出口各佔一側。例如,下圖中的左側為入口,右側為出口。

 

  1. 陣列,採用該結構的集合,對元素的存取有如下的特點:
  2. 查詢元素快:通過索引,可以快速訪問指定位置的元素
  3. 增刪元素慢:
  4. 指定索引位置增加元素:需要建立一個新陣列,將指定新元素儲存在指定索引位置,再把原陣列元素根據索引,複製到新陣列對應索引的位置。如下圖
  5. 指定索引位置刪除元素:需要建立一個新陣列,把原陣列元素根據索引,複製到新陣列對應索引的位置,原陣列中指定索引位置元素不復制到新陣列中。如下圖

 

  1. 連結串列,採用該結構的集合,對元素的存取有如下的特點:
  2. 多個節點之間,通過地址進行連線。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。
  3. 查詢元素慢:想查詢某個元素,需要通過連線的節點,依次向後查詢指定元素
  4. 增刪元素快:
  5. 增加元素:操作如左圖,只需要修改連線下個元素的地址即可。
  6. 刪除元素:操作如右圖,只需要修改連線下個元素的地址即可。

 

    1. ArrayList集合

ArrayList集合資料儲存的結構是陣列結構。元素增刪慢,查詢快,由於日常開發中使用最多的功能為查詢資料、遍歷資料,所以ArrayList是最常用的集合。

許多程式設計師開發時非常隨意地使用ArrayList完成任何需求,並不嚴謹,這種用法是不提倡的。

    1. LinkedList集合

LinkedList集合資料儲存的結構是連結串列結構。方便元素新增、刪除的集合。實際開發中對一個集合元素的新增與刪除經常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。如下圖

 

LinkedList是List的子類,List中的方法LinkedList都是可以使用,這裡就不做詳細介紹,我們只需要瞭解LinkedList的特有方法即可。在開發時,LinkedList集合也可以作為堆疊,佇列的結構使用。

方法演示:

LinkedList<String> link = new LinkedList<String>();

//新增元素

link.addFirst("abc1");

link.addFirst("abc2");

link.addFirst("abc3");

//獲取元素

System.out.println(link.getFirst());

System.out.println(link.getLast());

//刪除元素

System.out.println(link.removeFirst());

System.out.println(link.removeLast());

 

while(!link.isEmpty()){ //判斷集合是否為空

System.out.println(link.pop()); //彈出集合中的棧頂元素

       }

 

    1. Vector集合

Vector集合資料儲存的結構是陣列結構,為JDK中最早提供的集合。Vector中提供了一個獨特的取出方式,就是列舉Enumeration,它其實就是早期的迭代器。此介面Enumeration的功能與 Iterator 介面的功能是類似的。Vector集合已被ArrayList替代。列舉Enumeration已被迭代器Iterator替代。

  1. Vector常見的方法:

  1. Enumeration列舉常見的方法:

  1. Vector集合對ArrayList集合使用的對比

  1. Set介面

學習Collection介面時,記得Collection中可以存放重複元素,也可以不存放重複元素,那麼我們知道List中是可以存放重複元素的。那麼不重複元素給哪裡存放呢?那就是Set介面,它裡面的集合,所儲存的元素就是不重複的。

    1. Set介面介紹

查閱Set集合的API介紹,通過元素的equals方法,來判斷是否為重複元素,

    1. HashSet集合介紹

查閱HashSet集合的API介紹:此類實現Set介面,由雜湊表支援(實際上是一個 HashMap集合)。HashSet集合不能保證的迭代順序與元素儲存順序相同。

HashSet集合,採用雜湊表結構儲存資料,保證元素唯一性的方式依賴於:hashCode()與equals()方法。

    1. HashSet集合儲存資料的結構(雜湊表)

什麼是雜湊表呢?

雜湊表底層使用的也是陣列機制,陣列中也存放物件,而這些物件往陣列中存放時的位置比較特殊,當需要把這些物件給陣列中存放時,那麼會根據這些物件的特有資料結合相應的演算法,計算出這個物件在陣列中的位置,然後把這個物件存放在陣列中。而這樣的陣列就稱為雜湊陣列,即就是雜湊表。

當向雜湊表中存放元素時,需要根據元素的特有資料結合相應的演算法,這個演算法其實就是Object類中的hashCode方法。由於任何物件都是Object類的子類,所以任何物件有擁有這個方法。即就是在給雜湊表中存放物件時,會呼叫物件的hashCode方法,算出物件在表中的存放位置,這裡需要注意,如果兩個物件hashCode方法算出結果一樣,這樣現象稱為雜湊衝突,這時會呼叫物件的equals方法,比較這兩個物件是不是同一個物件,如果equals方法返回的是true,那麼就不會把第二個物件存放在雜湊表中,如果返回的是false,就會把這個值存放在雜湊表中。

總結:保證HashSet集合元素的唯一,其實就是根據物件的hashCode和equals方法來決定的。如果我們往集合中存放自定義的物件,那麼保證其唯一,就必須複寫hashCode和equals方法建立屬於當前物件的比較方式。

    1. HashSet儲存JavaAPI中的型別元素

給HashSet中儲存JavaAPI中提供的型別元素時,不需要重寫元素的hashCode和equals方法,因為這兩個方法,在JavaAPI的每個類中已經重寫完畢,如String類、Integer類等。

  1. 建立HashSet集合,儲存String物件。

public class HashSetDemo {

public static void main(String[] args) {

//建立HashSet物件

HashSet<String> hs = new HashSet<String>();

//給集合中新增自定義物件

hs.add("zhangsan");

hs.add("lisi");

hs.add("wangwu");

hs.add("zhangsan");

//取出集合中的每個元素

Iterator<String> it = hs.iterator();

while(it.hasNext()){

String s = it.next();

System.out.println(s);

}

}

}

輸出結果如下,說明集合中不能儲存重複元素:

wangwu

lisi

zhangsan

    1. HashSet儲存自定義型別元素

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

  1. 建立自定義物件Student

public class Student {

private String name;

private int age;

public Student(String name, int age) {

super();

this.name = name;

this.age = age;

}

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;

}

@Override

public String toString() {

return "Student [name=" + name + ", age=" + age + "]";

}

@Override

public int hashCode() {

final int prime = 31;

int result = 1;

result = prime * result + age;

result = prime * result + ((name == null) ? 0 : name.hashCode());

return result;

}

@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

if(!(obj instanceof Student)){

System.out.println("型別錯誤");

return false;

}

Student other = (Student) obj;

return this.age ==  other.age && this.name.equals(other.name);

}

}

 

  1. 建立HashSet集合,儲存Student物件。

public class HashSetDemo {

public static void main(String[] args) {

//建立HashSet物件

HashSet hs = new HashSet();

//給集合中新增自定義物件

hs.add(new Student("zhangsan",21));

hs.add(new Student("lisi",22));

hs.add(new Student("wangwu",23));

hs.add(new Student("zhangsan",21));

//取出集合中的每個元素

Iterator it = hs.iterator();

while(it.hasNext()){

Student s = (Student)it.next();

System.out.println(s);

}

}

}

輸出結果如下,說明集合中不能儲存重複元素:

Student [name=lisi, age=22]

Student [name=zhangsan, age=21]

Student [name=wangwu, age=23]

    1. LinkedHashSet介紹

我們知道HashSet保證元素唯一,可是元素存放進去是沒有順序的,那麼我們要保證有序,怎麼辦呢?

在HashSet下面有一個子類LinkedHashSet,它是連結串列和雜湊表組合的一個資料儲存結構。

演示程式碼如下:

public class LinkedHashSetDemo {

public static void main(String[] args) {

Set<String> set = new LinkedHashSet<String>();

set.add("bbb");

set.add("aaa");

set.add("abc");

set.add("bbc");

Iterator it = set.iterator();

while (it.hasNext()) {

System.out.println(it.next());

}

}

}

輸出結果如下,LinkedHashSet集合保證元素的存入和取出的順序:

bbb

aaa

abc

bbc

 

  1. 判斷集合元素唯一的原理
    1. ArrayList的contains方法判斷元素是否重複原理

ArrayList的contains方法會使用呼叫方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布林值判斷是否有重複元素。此時,當ArrayList存放自定義型別時,由於自定義型別在未重寫equals方法前,判斷是否重複的依據是地址值,所以如果想根據內容判斷是否為重複元素,需要重寫元素的equals方法。

    1. HashSet的add/contains等方法判斷元素是否重複原理

Set集合不能存放重複元素,其新增方法在新增時會判斷是否有重複元素,有重複不新增,沒重複則新增。

HashSet集合由於是無序的,其判斷唯一的依據是元素型別的hashCode與equals方法的返回結果。規則如下:

先判斷新元素與集合內已經有的舊元素的HashCode值

  1. 如果不同,說明是不同元素,新增到集合。
  2. 如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,新增到集合。

所以,使用HashSet儲存自定義型別,如果沒有重寫該類的hashCode與equals方法,則判斷重複時,使用的是地址值,如果想通過內容比較元素是否相同,需要重寫該元素類的hashcode與equals方法。

  1. 總結
    1. 知識點總結
  1. List與Set集合的區別?

List:

  它是一個有序的集合(元素存與取的順序相同)

  它可以儲存重複的元素

  Set:

  它是一個無序的集合(元素存與取的順序可能不同)

  它不能儲存重複的元素

  1. List集合中的特有方法
  2. void add(int index, Object element) 將指定的元素,新增到該集合中的指定位置上
  3. Object get(int index)返回集合中指定位置的元素。
  4. Object remove(int index) 移除列表中指定位置的元素, 返回的是被移除的元素
  5. Object set(int index, Object element)用指定元素替換集合中指定位置的元素,返回值的更新前的元素
  6. ArrayList:

底層資料結構是陣列,查詢快,增刪慢

  1. LinkedList:

底層資料結構是連結串列,查詢慢,增刪快

  1. HashSet:

元素唯一,不能重複

底層結構是 雜湊表結構

元素的存與取的順序不能保證一致

如何保證元素的唯一的?

重寫hashCode() 與 equals()方法

  1. LinkedHashSet:

元素唯一不能重複

底層結構是 雜湊表結構 + 連結串列結構

元素的存與取的順序一致

 

相關文章