Java——Link接口(ArrayList,LinkList)和Set接口(HashSet)
List接口
List接口是Collection接口的子接口,List接口中的抽象方法,有一部分方法和他的父接口Collection是一樣,List接口的自己特有的方法, 帶有索引的功能。
-
它是一個元素存取有序的集合。例如,存元素的順序是11、22、33。那麽集合中,元素的存儲就是按照11、22、33的順序完成的)。
-
它是一個帶有索引的集合,通過索引就可以精確的操作集合中的元素(與數組的索引是一個道理)。
-
集合中可以有重復的元素,通過元素的equals方法,來比較是否為重復的元素。
List接口的常用子類有:
- ArrayList集合:數據存儲的結構是數組結構。元素增刪慢,查找快,由於日常開發中使用最多的功能為查詢數據、遍歷數據,所以ArrayList是最常用的集合。
- LinkedList集合:數據存儲的結構是鏈表結構。方便元素添加、刪除的集合。實際開發中對一個集合元素的添加與刪除經常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。
List集合存儲數據的結構
List接口下有很多個集合,它們存儲元素所采用的結構方式是不同的,這樣就導致了這些集合有它們各自的特點,供給我們在不同的環境下進行使用。數據存儲的常用結構有:堆棧、隊列、數組、鏈表。
堆棧,采用該結構的集合,對元素的存取有如下的特點:
-
先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,然後才能彈出下面的子彈。
-
棧的入口、出口的都是棧的頂端位置
-
壓棧:就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。
-
彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。
隊列,采用該結構的集合,對元素的存取有如下的特點:
-
先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,安檢。排成一列,每個人依次檢查,只有前面的人全部檢查完畢後,才能排到當前的人進行檢查。
-
隊列的入口、出口各占一側。例如,下圖中的左側為入口,右側為出口。
數組,采用該結構的集合,對元素的存取有如下的特點:
-
查找元素快:通過索引,可以快速訪問指定位置的元素
- 增刪元素慢
-
-
指定索引位置增加元素:需要創建一個新數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,復制到新數組對應索引的位置。如下圖
-
指定索引位置刪除元素:需要創建一個新數組,把原數組元素根據索引,復制到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中。如下圖
-
鏈表,采用該結構的集合,對元素的存取有如下的特點:
-
多個節點之間,通過地址進行連接。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。
-
查找元素慢:想查找某個元素,需要通過連接的節點,依次向後查找指定元素
- 增刪元素快:
-
-
增加元素:操作如左圖,只需要修改連接下個元素的地址即可。
-
刪除元素:操作如右圖,只需要修改連接下個元素的地址即可。
-
一、類ArrayList的集合
1、添加元素到指定索引上
/*
* add(int index, E)
* 將元素插入到列表的指定索引上
* 帶有索引的操作,防止越界問題 java.lang.IndexOutOfBoundsException
*/
public static void function(){
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
System.out.println(list);
list.add(1, "x5456");
System.out.println(list);
}
2、移除指定索引上的元素
/* * E remove(int index) * 移除指定索引上的元素 * 返回被刪除之前的元素 */ public static void function_1(){ List<Double> list = new ArrayList<Double>(); list.add(1.1); list.add(1.2); list.add(1.3); list.add(1.4); list.remove(1.1); // 移除指定元素,沒有返回值 Double d = list.remove(0); // 移除指定索引的值,有返回值(就是被移除的那個值) System.out.println(d); System.out.println(list); }
3、替換指定索引上的值
public static void function_2(){
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Integer i = list.set(0, 5); //將0索引的值替換成5,返回值為之前的值
System.out.println(i);
System.out.println(list);
}
4、在叠代過程中不能增加集合中的元素
import java.util.ArrayList; import java.util.Iterator; import java.util.List; /* * 叠代器的並發修改異常 java.util.ConcurrentModificationException * 就是在遍歷的過程中,使用了集合方法修改了集合的長度,不允許的 */ public class ListDemo1 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("abc1"); list.add("abc2"); list.add("abc3"); list.add("abc4"); //對集合使用叠代器進行獲取,獲取時候判斷集合中是否存在 "abc3"對象 //如果有,添加一個元素 "ABC3" Iterator<String> it = list.iterator(); while(it.hasNext()){ String s = it.next(); //對獲取出的元素s,進行判斷,是不是有"abc3" if(s.equals("abc3")){ // 字符串不是基本數據類型,所以不能用==來判斷 list.add("ABC3"); } System.out.println(s); } } }
二、類LinkedList的集合
自身特點: 由鏈表底層實現,查詢慢,增刪快;因為是子類的特有功能,不能多態調用
1、添加元素到鏈表的開頭和結尾
/* * addFirst(E) 添加到鏈表的開頭 * addLast(E) 添加到鏈表的結尾 */ public static void function(){ LinkedList<String> link = new LinkedList<String>(); link.addLast("xinge"); link.add("abc"); link.add("bcd"); link.addFirst("x5456"); System.out.println(link); }
2、獲取開頭和結尾的值
/*
* E getFirst() 獲取鏈表的開頭
* E getLast() 獲取鏈表的結尾
*/
public static void function_2(){
LinkedList<String> link = new LinkedList<String>();
link.add("1");
link.add("2");
link.add("3");
link.add("4");
if(!link.isEmpty()){ // 判斷鏈表集合是否為空,等同於link.size()!=0
String first = link.getFirst();
String last = link.getLast();
System.out.println(first);
System.out.println(last);
}
}
3、移除開頭和結尾的值
/*
* E removeFirst() 移除並返回鏈表的開頭
* E removeLast() 移除並返回鏈表的結尾
*/
public static void function_3(){
LinkedList<String> link = new LinkedList<String>();
link.add("1");
link.add("2");
link.add("3");
link.add("4");
String first = link.removeFirst();
String last = link.removeLast();
System.out.println(first);
System.out.println(last);
System.out.println(link.pop()); // 彈出集合棧頂的元素
}
Set接口
Collection中可以存放重復元素,也可以不存放重復元素,那麽我們知道List中是可以存放重復元素的。那麽不重復元素給哪裏存放呢?那就是Set接口,它裏面的集合,所存儲的元素就是不重復的。
一、HashSet
HashSet集合,采用哈希表結構存儲數據,保證元素唯一性的方式依賴於:hashCode()與equals()方法,存儲和取出都比較快,線程不安全,運行速度快。
什麽是哈希表?
哈希表底層使用的也是數組機制,數組中也存放對象,而這些對象往數組中存放時的位置比較特殊,當需要把這些對象給數組中存放時,那麽會根據這些對象的特有數據結合相應的算法,計算出這個對象在數組中的位置,然後把這個對象存放在數組中。而這樣的數組就稱為哈希數組,即就是哈希表。
當向哈希表中存放元素時,需要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。由於任何對象都是Object類的子類,所以任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這裏需要註意,如果兩個對象hashCode方法算出結果一樣,這樣現象稱為哈希沖突,這時會調用對象的equals方法,比較這兩個對象是不是同一個對象,如果equals方法返回的是true,那麽就不會把第二個對象存放在哈希表中,如果返回的是false,就會把這個值存放在哈希表中。
總結:保證HashSet集合元素的唯一,其實就是根據對象的hashCode和equals方法來決定的。如果我們往集合中存放自定義的對象,那麽保證其唯一,就必須復寫hashCode和equals方法建立屬於當前對象的比較方式。
示例:將Person對象中的姓名,年齡,相同數據,看作同一個對象
import java.util.HashSet; import cn.itcast.demo3.Person; /* * HashSet集合的自身特點: * 底層數據結構,哈希表 * 存儲,取出都比較快 * 線程不安全,運行速度快 */ public class HashSetDemo1 { public static void main(String[] args) { //判斷對象是否重復,依賴對象自己的方法 hashCode,equals HashSet<Person> setPerson = new HashSet<Person>(); setPerson.add(new Person("a",11)); setPerson.add(new Person("b",10)); setPerson.add(new Person("b",10)); setPerson.add(new Person("c",25)); setPerson.add(new Person("d",19)); setPerson.add(new Person("e",17)); System.out.println(setPerson); } }
Person.java
需要達到要求,就需要重寫Person類的HashCode和equals方法
public class Person { private String name; private int age; /* * 沒有做重寫父類,每次運行結果都是不同整數 * 如果子類重寫父類的方法,哈希值,自定義的 * 存儲到HashSet集合的依據 */ public int hashCode(){ return name.hashCode()+age*55; } //方法equals重寫父類,保證和父類相同 //public boolean equals(Object obj){} public boolean equals(Object obj){ if(this == obj) return true; if(obj == null) return false; if(obj instanceof Person){ Person p = (Person)obj; return name.equals(p.name) && age==p.age; } return false; } 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; } public Person(String name, int age) { super(); this.name = name; this.age = age; } public Person(){} public String toString(){ return name+".."+age; } }
二、LinkedHashSet
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()); } } }
判斷集合中元素唯一
ArrayList
ArrayList的contains方法會使用調用方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布爾值判斷是否有重復元素。此時,當ArrayList存放自定義類型時,由於自定義類型在未重寫equals方法前,判斷是否重復的依據是地址值,所以如果想根據內容判斷是否為重復元素,需要重寫元素的equals方法。
HashSet
Set集合不能存放重復元素,其添加方法在添加時會判斷是否有重復元素,有重復不添加,沒重復則添加。
HashSet集合由於是無序的,其判斷唯一的依據是元素類型的hashCode與equals方法的返回結果。規則如下:
先判斷新元素與集合內已經有的舊元素的HashCode值
-
如果不同,說明是不同元素,添加到集合。
-
如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,添加到集合。
所以,使用HashSet存儲自定義類型,如果沒有重寫該類的hashCode與equals方法,則判斷重復時,使用的是地址值,如果想通過內容比較元素是否相同,需要重寫該元素類的hashcode與equals方法。
Java——Link接口(ArrayList,LinkList)和Set接口(HashSet)