第19天 集合

1.1 List接口介紹:
有序的 collection(也稱爲序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。與 set 不同,列表通常允許重複的元素。
List接口:

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

List接口的常用子類有:

  • ArrayList集合
  • LinkedList集合

1.2 List接口中常用的方法:

02
增加元素方法:

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

刪除元素刪除:

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

替換元素方法:

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

查詢元素方法:

  • 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.1Iterator的併發修改異常:

在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);
	}
}

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

1.3 List集合存儲數據的結構:

List接口下有很多個集合,它們存儲元素所採用的結構方式是不同的。數據存儲的常用結構有:堆棧、隊列、數組、鏈表。

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

  • 先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,然後才能彈出下面的子彈。
  • 棧的入口、出口的都是棧的頂端位置
  • 壓棧:就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
  • 彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

02
隊列,採用該結構的集合,對元素的存取有如下的特點:

  • 先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,安檢。排成一列,每個人依次檢查,只有前面的人全部檢查完畢後,才能排到當前的人進行檢查。
  • 隊列的入口、出口各佔一側。例如,下圖中的左側爲入口,右側爲出口。
    03

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

  • 查找元素快:通過索引,可以快速訪問指定位置的元素

  • 增刪元素慢:

    • 指定索引位置增加元素:需要創建一個新數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,複製到新數組對應索引的位置。如下圖
  • 指定索引位置刪除元素:需要創建一個新數組,把原數組元素根據索引,複製到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中。如下圖
    04
    鏈表,採用該結構的集合,對元素的存取有如下的特點:

  • 多個節點之間,通過地址進行連接。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。

  • 查找元素慢:想查找某個元素,需要通過連接的節點,依次向後查找指定元素

  • 增刪元素快:

    • 增加元素:操作如左圖,只需要修改連接下個元素的地址即可。
    • 刪除元素:操作如右圖,只需要修改連接下個元素的地址即可。

05

1.4 ArrayList集合:

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

1.5 LinkedList集合:

LinkedList集合數據存儲的結構是鏈表結構。方便元素添加、刪除的集合。實際開發中對一個集合元素的添加與刪除經常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。
06
LinkedList是List的子類,List中的方法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替代。

  • Vector常見的方法:
    07
  • Enumeration枚舉常見的方法:
    08
  • Vector集合對ArrayList集合使用的對比:
    09

2.1 Set接口介紹:

List中是可以存放重複元素的,Set接口,它裏面的集合,所存儲的元素就是不重複的。
Set集合的API介紹,通過元素的equals方法,來判斷是否爲重複元素,它是個不包含重複元素的集合。Set集合取出元素的方式可以採用:迭代器、增強for。
Set集合有多個子類,這裏我們介紹其中的HahSet、LinkedHashSet這兩個集合。

2.2 Handset集合介紹:

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

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

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

當向哈希表中存放元素時,需要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。由於任何對象都是Object類的子類,所以任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這裏需要注意,如果兩個對象hashCode方法算出結果一樣,這樣現象稱爲哈希衝突,這時會調用對象的equals方法,比較這兩個對象是不是同一個對象,如果equals方法返回的是true,那麼就不會把第二個對象存放在哈希表中,如果返回的是false,就會把這個值存放在哈希表中。(採用桶的存儲方式)
10
總結:保證HashSet集合元素的唯一,其實就是根據對象的hashCode和equals方法來決定的。如果我們往集合中存放自定義的對象,那麼保證其唯一,就必須複寫hashCode和equals方法建立屬於當前對象的比較方式。
11

2.4 HashSet存儲JavaAPI中的類型元素:

給HashSet中存儲JavaAPI中提供的類型元素時,不需要重寫元素的hashCode和equals方法,因爲這兩個方法,在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中存放自定義類型元素時,需要重寫對象中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的對象唯一。

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.1 ArrayList的contains方法判斷元素是否重複原理:

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

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

3.2
Set集合不能存放重複元素,其添加方法在添加時會判斷是否有重複元素,有重複不添加,沒重複則添加。
HashSet集合由於是無序的,其判斷唯一的依據是元素類型的hashCode與equals方法的返回結果。規則如下:
先判斷新元素與集合內已經有的舊元素的HashCode值

  • 如果不同,說明是不同元素,添加到集合。
  • 如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,添加到集合。
    所以,使用HashSet存儲自定義類型,如果沒有重寫該類的hashCode與equals方法,則判斷重複時,使用的是地址值,如果想通過內容比較元素是否相同,需要重寫該元素類的hashcode與equals方法。

4.1 知識點總結:

- List與Set集合的區別?

List:

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

  • 它可以存儲重複的元素

Set:

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

  • 它不能存儲重複的元素

List集合中的特有方法:

  • void add(int index, Object element) 將指定的元素,添加到該集合中的指定位置上
  • Object get(int index)返回集合中指定位置的元素。
  • Object remove(int index) 移除列表中指定位置的元素, 返回的是被移除的元素
  • Object set(int index, Object element)用指定元素替換集合中指定位置的元素,返回值的更新前的元素

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

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

HashSet:
元素唯一,不能重複
底層結構是 哈希表結構
元素的存與取的順序不能保證一致
如何保證元素的唯一的?
    重寫hashCode() 與 equals()方法

LinkedHashSet:
元素唯一、不能重複
底層結構是 哈希表結構 + 鏈表結構
元素的存與取的順序一致

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章