Java集合知識點總結
來源於部落格園,csdn,b站等各大知識分享平臺,只是整理方便自己複習,沒有任何商用,有問題聯絡立馬刪除!
集合的概述
集合:集合是java中提供的一種容器,可以用來儲存多個數據。java.util包下
集合與陣列的區別:
* 陣列的長度是固定的。集合的長度是可變的。
* 陣列中儲存的是同一型別的元素,可以儲存基本資料型別值。集合儲存的都是物件。而且物件的型別可以不一致。在開發中一般當物件多的時候,使用集合進行儲存。
集合的體系圖:
Collection:單列集合類的根介面,用於儲存一系列符合某種規則的元素,它有兩個重要的子介面,分別是java.util.List
和java.util.Set
List
的特點是元素有序、元素可重複。Set
的特點是元素無序,而且不可重複。List
介面的主要實現類有java.util.ArrayList
和java.util.LinkedList
,Set
介面的主要實現類有java.util.HashSet
和java.util.TreeSet
。
那麼根據上面的圖進行自頂向下去分析:
Collection介面時所有單列集合的父介面,因此定義了一些方法可以滿足使用,(list和set通用),注意:操作的僅僅是單列集合。
public boolean add(E e)
: 把給定的物件新增到當前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把給定的物件在當前集合中刪除。public boolean contains(E e)
: 判斷當前集合中是否包含給定的物件。public boolean isEmpty()
: 判斷當前集合是否為空。public int size()
: 返回集合中元素的個數。public Object[] toArray()
: 把集合中的元素,儲存到陣列中。
建立集合以多型的形式進行建立,用ArrayList作為其實現類去使用
Collection<String> coll = new ArrayList<String>();
新增元素:
coll.add();
輸出集合:
System.out.println(coll);
之前也說過,直接輸出說明重寫了toString方法,
迭代器(Iterator)
在程式開發中,經常需要遍歷集合中的所有元素。針對這種需求,JDK專門提供了一個介面
java.util.Iterator
。Iterator
介面也是Java集合中的一員,但它與Collection
、Map
介面有所不同,Collection
介面與Map
介面主要用於儲存元素,而Iterator
主要用於迭代訪問(即遍歷)Collection
中的元素,因此Iterator
物件也被稱為迭代器。
Iterator是一個介面,所以每個集合都存在這個方法生成一個迭代器。
public Iterator iterator()
: 獲取集合對應的迭代器,用來遍歷集合中的元素的。
迭代:即Collection集合元素的通用獲取方式。在取元素之前先要判斷集合中有沒有元素,如果有,就把這個元素取出來,繼續在判斷,如果還有就再取出出來。一直把集合中的所有元素全部取出。這種取出方式專業術語稱為迭代。
常用的方法如下所示:
* public E next()
:返回迭代的下一個元素。
* public boolean hasNext()
:如果仍有元素可以迭代,則返回 true。
使用:
Iterator<String> it = coll.iterator();// 泛型指的是 迭代出 元素的資料型別
while(it.hasNext()){ //判斷是否有迭代元素
String s = it.next();//獲取迭代出的元素
System.out.println(s);
}
如果使用next()後沒有元素,會報出java.util.NoSuchElementException沒有集合元素的錯誤。
關於迭代器的實現原理可以閱讀更高階的jvm書籍或者Java書籍去獲得,作者也並未深究,因為目前的還沒學習完
如果學過c,你可以理解成指標,它的內部就是指標進行的,如果不好理解就可以用連結串列相關的去進行一個理解。後續補上····
這裡關於遍歷,除了while,for,dowhile還有一種,foreach,也就是增強for
格式:
for(元素的資料型別 變數 : Collection集合or陣列){
//寫操作程式碼
}
它用於遍歷Collection和陣列。通常只進行遍歷元素,不要在遍歷的過程中對集合元素進行增刪操作。
int[] arr = {3,5,6,87};
for(int a : arr){//a代表陣列中的每個元素
System.out.println(a);
}
1.2##泛型
概念:
* 泛型:可以在類或方法中預支地使用未知的型別。
tips:一般在建立物件時,將未知的型別確定具體的型別。當沒有指定泛型時,預設型別為Object型別。
定義格式:
修飾符 class 類名<代表泛型的變數> { }
例如,API中的ArrayList集合:
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
....
}
使用泛型: 即什麼時候確定泛型。
在建立物件的時候確定泛型
例如,ArrayList<String> list = new ArrayList<String>();
此時,變數E的值就是String型別,那麼我們的型別就可以理解為:
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
...
}
再例如,ArrayList<Integer> list = new ArrayList<Integer>();
此時,變數E的值就是Integer型別,那麼我們的型別就可以理解為:
class ArrayList<Integer> {
public boolean add(Integer e) { }
public Integer get(int index) { }
...
}
舉例自定義泛型類
public class MyGenericClass<MVP> {//沒有MVP型別,在這裡代表 未知的一種資料型別 未來傳遞什麼就是什麼型別
private MVP mvp;
public void setMVP(MVP mvp) {
this.mvp = mvp;
}
public MVP getMVP() {
return mvp;
}
}
使用:
public class GenericClassDemo {
public static void main(String[] args) {// 建立一個泛型為String的類
MyGenericClass<String> my = new MyGenericClass<String>(); // 呼叫setMVP
my.setMVP("大鬍子登登");// 呼叫getMVP
String mvp = my.getMVP();
System.out.println(mvp);//建立一個泛型為Integer的類
MyGenericClass<Integer> my2 = new MyGenericClass<Integer>();
my2.setMVP(123);
Integer mvp2 = my2.getMVP();
}
}
含有泛型的方法
定義格式:
修飾符 <代表泛型的變數> 返回值型別 方法名(引數){ }
public <MVP> void show(MVP mvp) {
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
public static void main(String[] args) {
// 建立物件
MyGenericMethod mm = new MyGenericMethod();
// 演示看方法提示
mm.show("aaa");
mm.show(123);
mm.show(12.45);
}
含有泛型的介面
定義格式:
修飾符 interface介面名<代表泛型的變數> { }
同上
1.2.1####泛型萬用字元
當使用泛型類或者介面時,傳遞的資料中,泛型型別不確定,可以通過萬用字元<?>表示。但是一旦使用泛型的萬用字元後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
萬用字元基本使用
泛型的萬用字元:不知道使用什麼型別來接收的時候,此時可以使用? ?表示未知萬用字元。
public static void main(String[] args) {
Collection<Intger> list1 = new ArrayList<Integer>();
getElement(list1);
Collection<String> list2 = new ArrayList<String>();
getElement(list2);
}
public static void getElement(Collection<?> coll){}
泛型不存在繼承關係 Collection
萬用字元高階使用----受限泛型
之前設定泛型的時候,實際上是可以任意設定的,只要是類就可以設定。但是在JAVA的泛型中可以指定一個泛型的上限和下限。
泛型的上限:
- 格式:
型別名稱 <? extends 類 > 物件名稱
- 意義:
只能接收該型別及其子類
泛型的下限:
- 格式:
型別名稱 <? super 類 > 物件名稱
- 意義:
只能接收該型別及其父型別
比如:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement(list1);
getElement(list2);//報錯
getElement(list3);
getElement(list4);//報錯
getElement2(list1);//報錯
getElement2(list2);//報錯
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此時的泛型?,必須是Number型別或者Number型別的子類
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此時的泛型?,必須是Number型別或者Number型別的父類
public static void getElement2(Collection<? super Number> coll){}
List集合介面
api文件中的介紹:
java.util.List
介面繼承自Collection
介面,是單列集合的一個重要分支,習慣性地會將實現了List
介面的物件稱為List集合。在List集合中允許出現重複的元素,所有的元素是以一種線性方式進行儲存的,在程式中可以通過索引來訪問集合中的指定元素。另外,List集合還有一個特點就是元素有序,即元素的存入順序和取出順序一致。
List集合的特點:
- 它是一個元素存取有序的集合。例如,存元素的順序是11、22、33。那麼集合中,元素的儲存就是按照11、22、33的順序完成的)。
- 它是一個帶有索引的集合,通過索引就可以精確的操作集合中的元素(與陣列的索引是一個道理)。
- 集合中可以有重複的元素,通過元素的equals方法,來比較是否為重複的元素。
常用的方法:
public void add(int index, E element)
: 將指定的元素,新增到該集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替換集合中指定位置的元素,返回值的更新前的元素。
List的子類
####ArrayList集合
java.util.ArrayList
集合資料儲存的結構是陣列結構。元素增刪慢,查詢快,由於日常開發中使用最多的功能為查詢資料、遍歷資料,所以ArrayList
是最常用的集合。
####LinkedList集合
java.util.LinkedList
集合資料儲存的結構是連結串列結構。方便元素新增、刪除的集合
它是一個雙向連結串列:
常用方法:
public void addFirst(E e)
:將指定元素插入此列表的開頭。public void addLast(E e)
:將指定元素新增到此列表的結尾。public E getFirst()
:返回此列表的第一個元素。public E getLast()
:返回此列表的最後一個元素。public E removeFirst()
:移除並返回此列表的第一個元素。public E removeLast()
:移除並返回此列表的最後一個元素。public E pop()
:從此列表所表示的堆疊處彈出一個元素。public void push(E e)
:將元素推入此列表所表示的堆疊。public boolean isEmpty()
:如果列表不包含元素,則返回true。
在linkedList和ArrayList中都可以使用List和Collection的方法,因為他們都是子類
Set介面
API文件介紹:
java.util.Set
介面和java.util.List
介面一樣,同樣繼承自Collection
介面,它與Collection
介面中的方法基本一致,並沒有對Collection
介面進行功能上的擴充,只是比Collection
介面更加嚴格了。與List
介面不同的是,Set
介面中元素無序,並且都會以某種規則保證存入的元素不出現重複。
Set集合取出元素,使用迭代器和增強for
HashSet集合
API文件:
java.util.HashSet
是Set
介面的一個實現類,它所儲存的元素是不可重複的,並且元素都是無序的(即存取順序不一致)。java.util.HashSet
底層的實現其實是一個java.util.HashMap
支援
HashSet
是根據物件的雜湊值來確定元素在集合中的儲存位置,因此具有良好的存取和查詢效能。保證元素唯一性的方式依賴於:hashCode
與equals
方法。
public static void main(String[] args) {
//建立 Set集合
HashSet<String> set = new HashSet<String>();
//新增元素
set.add(new String("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
//遍歷
for (String name : set) {
System.out.println(name);
}
}
結果:
cba
abc
bac
HashSet集合儲存結構雜湊表:
在JDK1.8之前,雜湊表底層採用陣列+連結串列實現,即使用連結串列處理衝突,同一hash值的連結串列都儲存在一個連結串列裡。但是當位於一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查詢的效率較低。而JDK1.8中,雜湊表儲存採用陣列+連結串列+紅黑樹實現,當連結串列長度超過閾值(8)時,將連結串列轉換為紅黑樹,這樣大大減少了查詢時間。
雜湊表的構成:
雜湊表是由陣列+連結串列+紅黑樹(JDK1.8增加了紅黑樹部分)實現的
DK1.8**引入紅黑樹大程度優化了HashMap的效能,那麼對於我們來講保證HashSet集合元素的唯一,其實就是根據物件的hashCode和equals方法來決定的。如果我們往集合中存放自定義的物件,那麼保證其唯一,就必須複寫hashCode和equals方法建立屬於當前物件的比較方式。
HashSet儲存自定義型別
給HashSet中存放自定義型別元素時,需要重寫物件中的hashCode和equals方法,建立自己的比較方式,才能保證HashSet集合中的物件唯一
LinkedHashSet
在HashSet下面有一個子類
java.util.LinkedHashSet
,它是連結串列和雜湊表組合的一個數據儲存結構。
例項:
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<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
結果:
bbb
aaa
abc
bbc
可變引數:
在JDK1.5之後,如果我們定義一個方法需要接受多個引數,並且多個引數型別一致,我們可以對其簡化成如下格式:
修飾符 返回值型別 方法名(引數型別... 形參名){ }
其實這個書寫完全等價與
修飾符 返回值型別 方法名(引數型別[] 形參名){ }
只是後面這種定義,在呼叫時必須傳遞陣列,而前者可以直接傳遞資料即可。
JDK1.5以後。出現了簡化操作。... 用在引數上,稱之為可變引數。
同樣是代表陣列,但是在呼叫這個帶有可變引數的方法時,不用建立陣列(這就是簡單之處),直接將陣列中的元素作為實際引數進行傳遞,其實編譯成的class檔案,將這些元素先封裝到一個數組中,在進行傳遞。這些動作都在編譯.class檔案時,自動完成了。
注意:如果在方法書寫時,這個方法擁有多引數,引數中包含可變引數,可變引數一定要寫在引數列表的末尾位置。
工具類Collections
基本概念:
java.utils.Collections
是集合工具類,用來對集合進行操作。
常用:
public static <T> boolean addAll(Collection<T> c, T... elements)
:往集合中新增一些元素。public static void shuffle(List<?> list) 打亂順序
:打亂集合順序。public static <T> void sort(List<T> list)
:將集合中元素按照預設規則排序。public static <T> void sort(List<T> list,Comparator<? super T> )
:將集合中元素按照指定規則排序。
主要說明按規則排序sort
Comparator比較器
public static <T> void sort(List<T> list,Comparator<? super T> )
方法靈活的完成,這個裡面就涉及到了Comparator這個介面,位於位於java.util包下,排序是comparator能實現的功能之一,該介面代表一個比較器,比較器具有可比性!顧名思義就是做排序的,通俗地講需要比較兩個物件誰排在前誰排在後,那麼比較的方法就是:
-
public int compare(String o1, String o2)
:比較其兩個引數的順序。兩個物件比較的結果有三種:大於,等於,小於。
如果要按照升序排序,
則o1 小於o2,返回(負數),相等返回0,01大於02返回(正數)
如果要按照降序排序
則o1 小於o2,返回(正數),相等返回0,01大於02返回(負數)
操作如下:
public class CollectionsDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
//排序方法 按照第一個單詞的降序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) - o1.charAt(0);
}
});
System.out.println(list);
}
}
結果如下:
[sba, nba, cba, aba]
Comparable:強行對實現它的每個類的物件進行整體排序。這種排序被稱為類的自然排序,類的compareTo方法被稱為它的自然比較方法。只能在類中實現compareTo()一次,不能經常修改類的程式碼實現自己想要的排序。實現此介面的物件列表(和陣列)可以通過Collections.sort(和Arrays.sort)進行自動排序,物件可以用作有序對映中的鍵或有序集合中的元素,無需指定比較器。
Comparator強行對某個物件進行整體排序。可以將Comparator 傳遞給sort方法(如Collections.sort或 Arrays.sort),從而允許在排序順序上實現精確控制。還可以使用Comparator來控制某些資料結構(如有序set或有序對映)的順序,或者為那些沒有自然順序的物件collection提供排序。
Map集合介面:
現實生活中,我們常會看到這樣的一種集合:IP地址與主機名,身份證號與個人,系統使用者名稱與系統使用者物件等,這種一一對應的關係,就叫做對映。Java提供了專門的集合類用來存放這種物件關係的物件,即
java.util.Map
介面。
- ollection`中的集合,元素是孤立存在的(理解為單身),向集合中儲存元素採用一個個元素的方式儲存。
Map
中的集合,元素是成對存在的(理解為夫妻)。每個元素由鍵與值兩部分組成,通過鍵可以找對所對應的值。Collection
中的集合稱為單列集合,Map
中的集合稱為雙列集合。- 需要注意的是,
Map
中的集合不能包含重複的鍵,值可以重複;每個鍵只能對應一個值。
常用子類:
- HashMap<K,V>:儲存資料採用的雜湊表結構,元素的存取順序不能保證一致。由於要保證鍵的唯一、不重複,需要重寫鍵的hashCode()方法、equals()方法。
- LinkedHashMap<K,V>:HashMap下有個子類LinkedHashMap,儲存資料採用的雜湊表結構+連結串列結構。通過連結串列結構可以保證元素的存取順序一致;通過雜湊表結構可以保證的鍵的唯一、不重複,需要重寫鍵的hashCode()方法、equals()方法。
Map介面中的集合都有兩個泛型變數<K,V>,在使用時,要為兩個泛型變數賦予資料型別。兩個泛型變數<K,V>的資料型別可以相同,也可以不同。
常用方法:
public V put(K key, V value)
: 把指定的鍵與指定的值新增到Map集合中。public V remove(Object key)
: 把指定的鍵 所對應的鍵值對元素 在Map集合中刪除,返回被刪除元素的值。public V get(Object key)
根據指定的鍵,在Map集合中獲取對應的值。boolean containsKey(Object key)
判斷集合中是否包含指定的鍵。public Set<K> keySet()
: 獲取Map集合中所有的鍵,儲存到Set集合中。public Set<Map.Entry<K,V>> entrySet()
: 獲取到Map集合中所有的鍵值對物件的集合(Set集合)。
使用put方法時,若指定的鍵(key)在集合中沒有,則沒有這個鍵對應的值,返回null,並把指定的鍵值新增到集合中;
若指定的鍵(key)在集合中存在,則返回值為集合中鍵對應的值(該值為替換前的值),並把指定鍵所對應的值,替換成指定的新值。
Map集合中遍歷找值的方式
鍵找值方式:即通過元素中的鍵,獲取鍵所對應的值
分析步驟:
- 獲取Map中所有的鍵,由於鍵是唯一的,所以返回一個Set集合儲存所有的鍵。方法提示:
keyset()
- 遍歷鍵的Set集合,得到每一個鍵。
- 根據鍵,獲取鍵所對應的值。方法提示:
get(K key)
public static void main(String[] args) {
//建立Map集合物件
HashMap<String, String> map = new HashMap<String,String>();
//新增元素到集合
map.put("胡歌", "霍建華");
map.put("郭德綱", "于謙");
map.put("薛之謙", "大張偉");
//獲取所有的鍵 獲取鍵集
Set<String> keys = map.keySet();
// 遍歷鍵集 得到 每一個鍵
for (String key : keys) {
//key 就是鍵
//獲取對應值
String value = map.get(key);
System.out.println(key+"的CP是:"+value);
}
}
Entry鍵值找值方式
我們已經知道,
Map
中存放的是兩種物件,一種稱為key(鍵),一種稱為value(值),它們在在Map
中是一一對應關係,這一對物件又稱做Map
中的一個Entry(項)
。Entry
將鍵值對的對應關係封裝成了物件。即鍵值對物件,這樣我們在遍歷Map
集合時,就可以從每一個鍵值對(Entry
)物件中獲取對應的鍵與對應的值。
既然Entry表示了一對鍵和值,那麼也同樣提供了獲取對應鍵和對應值得方法:
public K getKey()
:獲取Entry物件中的鍵。public V getValue()
:獲取Entry物件中的值。
在Map集合中也提供了獲取所有Entry物件的方法:
public Set<Map.Entry<K,V>> entrySet()
: 獲取到Map集合中所有的鍵值對物件的集合(Set集合)。
鍵值對方式:即通過集合中每個鍵值對(Entry)物件,獲取鍵值對(Entry)物件中的鍵與值。
操作步驟:
-
獲取Map集合中,所有的鍵值對(Entry)物件,以Set集合形式返回。方法提示:
entrySet()
。 -
遍歷包含鍵值對(Entry)物件的Set集合,得到每一個鍵值對(Entry)物件。
-
通過鍵值對(Entry)物件,獲取Entry物件中的鍵與值。 方法提示:
getkey() getValue()
Set<Entry<String,String>> entrySet = map.entrySet();// 獲取 所有的 entry物件 entrySet
for (Entry<String, String> entry : entrySet) { // 遍歷得到每一個entry物件
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"的CP是:"+value);
Map集合不能直接使用迭代器或者foreach進行遍歷。但是轉成Set之後就可以使用了。
HashMap儲存自定義鍵值
重寫hashcode方法和equals方法確保key唯一,不重複
LinkHashMap
在HashMap下面有一個子類LinkedHashMap,它是連結串列和雜湊表組合的一個數據儲存結構。
確保有序且速度快
JDK9對集合新增的優化
通常,我們在程式碼中建立一個集合(例如,List 或 Set ),並直接用一些元素填充它。 例項化集合,幾個 add方法 呼叫,使得程式碼重複。
public class Demo01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
list.add("ghi");
System.out.println(list);
}
}
Java 9,添加了幾種集合工廠方法,更方便建立少量元素的集合、map例項。新的List、Set、Map的靜態工廠方法可以更方便地建立集合的不可變例項。
例子:
public class HelloJDK9 {
public static void main(String[] args) {
Set<String> str1=Set.of("a","b","c");
//str1.add("c");這裡編譯的時候不會錯,但是執行的時候會報錯,因為是不可變的集合
System.out.println(str1);
Map<String,Integer> str2=Map.of("a",1,"b",2);
System.out.println(str2);
List<String> str3=List.of("a","b");
System.out.println(str3);
}
}
需要注意以下兩點:
1:of()方法只是Map,List,Set這三個介面的靜態方法,其父類介面和子類實現並沒有這類方法,比如 HashSet,ArrayList等待;
2:返回的集合是不可變的;