集合(List、Set)

19天 集合java

第1章 List接口

咱們掌握了Collection接口的使用後,再來看看Collection接口中的子類,他們都具有那些特性呢?程序員

接下來,咱們一塊兒學習Collection中的經常使用幾個子類(List集合、Set集合)。算法

1.1 List接口介紹

查閱API,看List的介紹。有序的 collection(也稱爲序列)。此接口的用戶能夠對列表中每一個元素的插入位置進行精確地控制。用戶能夠根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。與 set 不一樣,列表一般容許重複的元素。數組

看完API,咱們總結一下:數據結構

List接口:併發

它是一個元素存取有序的集合。例如,存元素的順序是十一、2二、33。那麼集合中,元素的存儲就是按照十一、2二、33的順序完成的)。ide

l 它是一個帶有索引的集合,經過索引就能夠精確的操做集合中的元素(與數組的索引是一個道理)。學習

集合中能夠有重複的元素,經過元素的equals方法,來比較是否爲重複的元素。this

 

List接口的經常使用子類有:spa

l ArrayList集合

l LinkedList集合

1.2 List接口中經常使用的方法

 

 

l 增長元素方法

l add(Object e):向集合末尾處,添加指定的元素

l add(int index, Object e):向集合指定索引處,添加指定的元素,原有元素依次後移

l 刪除元素刪除

l remove(Object e):將指定元素對象,從集合中刪除,返回值爲被刪除的元素

l remove(int index):將指定索引處的元素,從集合中刪除,返回值爲被刪除的元素

l 替換元素方法

l set(int index, Object e):將指定索引處的元素,替換成指定的元素,返回值爲替換前的元素

l 查詢元素方法

l 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.2.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.3 List集合存儲數據的結構

List接口下有不少個集合,它們存儲元素所採用的結構方式是不一樣的,這樣就致使了這些集合有它們各自的特色,供給咱們在不一樣的環境下進行使用。數據存儲的經常使用結構有:堆棧、隊列、數組、鏈表。咱們分別來了解一下:

l 堆棧,採用該結構的集合,對元素的存取有以下的特色:

l 先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,而後才能彈出下面的子彈。

l 棧的入口、出口的都是棧的頂端位置

l 壓棧:就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。

l 彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

 

 

 

l 隊列,採用該結構的集合,對元素的存取有以下的特色:

l 先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,安檢。排成一列,每一個人依次檢查,只有前面的人所有檢查完畢後,才能排到當前的人進行檢查。

l 隊列的入口、出口各佔一側。例如,下圖中的左側爲入口,右側爲出口。

 

 

 

l 數組,採用該結構的集合,對元素的存取有以下的特色:

l 查找元素快:經過索引,能夠快速訪問指定位置的元素

l 增刪元素慢:

指定索引位置增長元素:須要建立一個新數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,複製到新數組對應索引的位置。以下圖

指定索引位置刪除元素:須要建立一個新數組,把原數組元素根據索引,複製到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中。以下圖

 

 

 

l 鏈表,採用該結構的集合,對元素的存取有以下的特色:

l 多個節點之間,經過地址進行鏈接。例如,多我的手拉手,每一個人使用本身的右手拉住下我的的左手,依次類推,這樣多我的就連在一塊兒了。

l 查找元素慢:想查找某個元素,須要經過鏈接的節點,依次向後查找指定元素

l 增刪元素快:

l 增長元素:操做如左圖,只須要修改鏈接下個元素的地址便可。

l 刪除元素:操做如右圖,只須要修改鏈接下個元素的地址便可。

 

 

 

1.4 ArrayList集合

ArrayList集合數據存儲的結構是數組結構。元素增刪慢,查找快,因爲平常開發中使用最多的功能爲查詢數據、遍歷數據,因此ArrayList是最經常使用的集合。

許多程序員開發時很是隨意地使用ArrayList完成任何需求,並不嚴謹,這種用法是不提倡的。

1.5 LinkedList集合

LinkedList集合數據存儲的結構是鏈表結構。方便元素添加、刪除的集合。實際開發中對一個集合元素的添加與刪除常常涉及到首尾操做,而LinkedList提供了大量首尾操做的方法。以下圖

 

 

 

LinkedListList的子類,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.6 Vector集合

Vector集合數據存儲的結構是數組結構,爲JDK中最先提供的集合。Vector中提供了一個獨特的取出方式,就是枚舉Enumeration,它其實就是早期的迭代器。此接口Enumeration的功能與 Iterator 接口的功能是相似的。Vector集合已被ArrayList替代枚舉Enumeration已被迭代器Iterator替代。

l Vector常見的方法:

 

 

l Enumeration枚舉常見的方法:

 

 

l Vector集合對ArrayList集合使用的對比

 

 

第2章 Set接口

學習Collection接口時,記得Collection中能夠存放重複元素,也能夠不存放重複元素,那麼咱們知道List中是能夠存放重複元素的。那麼不重複元素給哪裏存放呢?那就是Set接口,它裏面的集合,所存儲的元素就是不重複的。

2.1 Set接口介紹

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

2.2 HashSet集合介紹

查閱HashSet集合的API介紹:此類實現Set接口,由哈希表支持(其實是一個 HashMap集合)。HashSet集合不能保證的迭代順序與元素存儲順序相同

HashSet集合,採用哈希表結構存儲數據,保證元素惟一性的方式依賴於:hashCode()equals()方法。

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

什麼是哈希表呢?

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

當向哈希表中存放元素時,須要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。因爲任何對象都是Object類的子類,因此任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這裏須要注意,若是兩個對象hashCode方法算出結果同樣,這樣現象稱爲哈希衝突,這時會調用對象的equals方法,比較這兩個對象是否是同一個對象,若是equals方法返回的是true,那麼就不會把第二個對象存放在哈希表中,若是返回的是false,就會把這個值存放在哈希表中。

總結:保證HashSet集合元素的惟一,其實就是根據對象的hashCodeequals方法來決定的。若是咱們往集合中存放自定義的對象,那麼保證其惟一,就必須複寫hashCodeequals方法創建屬於當前對象的比較方式。

 

 

2.4 HashSet存儲JavaAPI中的類型元素

HashSet中存儲JavaAPI中提供的類型元素時,不須要重寫元素的hashCodeequals方法,由於這兩個方法,在JavaAPI的每一個類中已經重寫完畢,如String類、Integer類等。

建立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

2.5 HashSet存儲自定義類型元素

HashSet中存放自定義類型元素時,須要重寫對象中的hashCodeequals方法,創建本身的比較方式,才能保證HashSet集合中的對象惟一

建立自定義對象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);

}

}

 

建立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]

2.6 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

 

第3章 判斷集合元素惟一的原理

3.1 ArrayListcontains方法判斷元素是否重複原理

 

 

ArrayListcontains方法會使用調用方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布爾值判斷是否有重複元素。此時,當ArrayList存放自定義類型時,因爲自定義類型在未重寫equals方法前,判斷是否重複的依據是地址值,因此若是想根據內容判斷是否爲重複元素,須要重寫元素的equals方法。

3.2 HashSetadd/contains等方法判斷元素是否重複原理

 

 

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

HashSet集合因爲是無序的,其判斷惟一的依據是元素類型的hashCodeequals方法的返回結果。規則以下:

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

l 若是不一樣,說明是不一樣元素,添加到集合。

若是相同,再判斷equals比較結果。返回true則相同元素;返回false則不一樣元素,添加到集合。

因此,使用HashSet存儲自定義類型,若是沒有重寫該類的hashCodeequals方法,則判斷重複時,使用的是地址值,若是想經過內容比較元素是否相同,須要重寫該元素類的hashcodeequals方法。

第4章 總結

4.1 知識點總結

l List與Set集合的區別?

List:

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

  它能夠存儲重複的元素

  Set:

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

  它不能存儲重複的元素

l List集合中的特有方法

l void add(int index, Object element) 將指定的元素,添加到該集合中的指定位置上

l Object get(int index)返回集合中指定位置的元素。

l Object remove(int index) 移除列表中指定位置的元素, 返回的是被移除的元素

l Object set(int index, Object element)用指定元素替換集合中指定位置的元素,返回值的更新前的元素

l ArrayList:

底層數據結構是數組,查詢快,增刪慢

l LinkedList:

底層數據結構是鏈表,查詢慢,增刪快

l HashSet:

元素惟一,不能重複

底層結構是 哈希表結構

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

如何保證元素的惟一的?

重寫hashCode() equals()方法

l LinkedHashSet:

元素惟一不能重複

底層結構是 哈希表結構 + 鏈表結構

元素的存與取的順序一致

相關文章
相關標籤/搜索