目錄
一、Java集合簡介
1.什么是集合?
2.集合接口
3.小結
二、List集合
1.List集合簡介
三、ArrayList容器類
1.初始化
1.1無參初始化
1.2有參初始化
2.數據結構
3.常用方法
3.1增加元素
3.2查找元素
3.3 修改元素
3.4 刪除元素
3.5 其他方法
4.擴容機制
5.總結特點
四、LinkedList類
1.初始化
2.數據結構
3.常用方法
3.1增加元素
3.2查找元素
3.3修改元素
3.4刪除元素
3.5轉換為數組
3.6元素的遍歷
4.總結特點
一、Java集合簡介
1.什么是集合?
什么是集合?集合就是“由若干個確定的元素所構成的整體”,在程序中,一般代表保存若干個元素(數據)的某種容器類。在數學中,我們經常遇到集合的概念。例如:
有限集合:
????????一個班所有的同學構成的集合;
????????一個網站所有的商品構成的集合;
????????...
無限集合:
????????全體自然數集合:1,2,3,……
????????有理數集合;
????????實數集合;
????????...
為什么要在計算機中引入集合呢?這是為了便于處理一組類似的數據,例如:
????????計算所有同學的總成績和平均成績;
????????列舉所有的商品名稱和價格;
????????……
在Java
中,如果一個Java
對象可以在內部持有(保存)若干其他Java
對象,并對外提供訪問接口,我們把這種Java
對象的容器稱為集合。很顯然,Java
的數組也可以看作是一種集合:
String[] ss = new String[10]; // 可以持有10個String對象
ss[0] = "Hello"; // 可以放入String對象
String first = ss[0]; // 可以獲取String
既然Java
提供了數組這種數據類型,可以充當集合,那么,我們為什么還需要其他集合類?這是因為數組有如下限制:
????????數組初始化后大小不可變;
????????數組只能按索引順序存取;
因此,我們需要各種不同類型的集合類來處理不同的數據,例如:
????????可變大小的順序鏈表;
????????保證無重復元素的集合;
????????...
2.集合接口
Java
標準庫自帶的java.util
包提供了集合相關的接口和實現類:Collection接口
,它是除Map接口
外所有其他集合類的根接口。
collection特點:
(1)collection接口是單列集合的根接口,英文含義集中,收集。
(2)單列集合是將數據進行一個一個的存儲,存儲的是值
Java
的java.util
包主要提供了以下三種類型的集合:
??List
:一種有序列表的集合;
??Set
:一種保證沒有重復元素的集合;
??Map
:一種通過鍵值(key-value)查找的映射表集合,例如,根據Student
的name
查找對應Student
的Map
。
Java
集合的設計有幾個特點:首先實現了接口和實現類相分離,例如,有序表的接口是List
,具體的實現類有ArrayList
,LinkedList
等;其次支持泛型,我們可以限制在一個集合中只能放入同一種數據類型的元素,例如:
List<String> list = new ArrayList<>(); // 只能放入String類型
最后,Java訪問集合總是通過統一的方式——迭代器(Iterator
)來實現,它最明顯的好處在于無需知道集合內部元素是按什么方式存儲的。
另外,由于Java
的集合設計非常久遠,中間經歷過大規模改進,我們要注意到有一小部分集合類是遺留類,不應該繼續使用:
? Hashtable
:一種線程安全的Map
實現;
? Vector
:一種線程安全的List
實現;
? Stack
:基于Vector
實現的LIFO
的棧;
3.小結
(1)Java的集合類定義在java.util
包中;
(2)支持泛型,主要提供了3種集合類,包括List
,Set
和Map
;
(3)Java集合使用統一的Iterator
遍歷,盡量不要使用遺留接口;
二、List集合
1.List集合簡介
在集合類中,List
是最基礎的一種集合:它是一種有序列表。List
的行為和數組幾乎完全相同:List
內部按照放入元素的先后順序存放,每個元素都可以通過索引確定自己的位置,List
的索引和數組一樣,從0
開始。
List
和數組類似,也是有序結構。還可重復。但是,如果我們使用數組,在添加和刪除元素的時候,會非常不方便。例如,從一個已有的數組{'A', 'B', 'C', 'D', 'E'}
中刪除索引為2
的元素:這個“刪除”操作實際上是把'C'
后面的元素依次往前挪一個位置,而“添加”操作實際上是把指定位置以后的元素都依次向后挪一個位置,騰出來的位置給新加的元素。這兩種操作,用數組實現非常麻煩。
┌───┬───┬───┬───┬───┬───┐
│ A │ B │ C │ D │ E │ │
└───┴───┴───┴───┴───┴───┘│ │┌───┘ ││ ┌───┘│ │▼ ▼
┌───┬───┬───┬───┬───┬───┐
│ A │ B │ D │ E │ │ │
└───┴───┴───┴───┴───┴───┘
因此,在實際應用中,需要增刪元素的有序列表,我們使用最多的是ArrayList
。實際上,ArrayList
在內部使用了數組來存儲所有元素。例如,一個ArrayList
擁有5個元素,實際數組大小為10
(即有5個空位):
size=5
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ C │ D │ E │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘0 1 2 3 4 5 6 7 8 9
當需要在指定索引位置(索引=2)添加一個元素(X)到ArrayList
時,ArrayList
自動移動需要移動的元素:
size=5
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ │ C │ D │ E │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘0 1 2 3 4 5 6 7 8 9
然后,往內部指定索引的數組位置添加一個元素,然后把size
加1
:
size=6
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ X │ C │ D │ E │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘0 1 2 3 4 5 6 7 8 9
當繼續添加元素,但是數組已滿,沒有空閑位置的時候,ArrayList
先創建一個更大的新數組(新數組長度為原數組的1.5倍),然后把舊數組的所有元素復制到新數組,緊接著用新數組取代舊數組:
size=10
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ X │ C │ D │ E │ F │ G │ H │ I │ │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
現在,新數組就有了空位,可以繼續添加一個元素到數組末尾,同時size
加1
:
size=11
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ X │ C │ D │ E │ F │ G │ H │ I │ J │ │ │ │ │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
可見,ArrayList
把添加和刪除的操作封裝起來,讓我們操作List
類似于操作數組,卻不用關心內部元素如何移動。我們觀察List<E>
接口,可以看到幾個主要的接口方法:
(1)在末尾添加一個元素:boolean add(E e)
(2)在指定索引添加一個元素:boolean add(int index, E e)
(3)刪除指定索引的元素:E remove(int index)
(4)刪除某個元素:boolean remove(Object e)
(5)獲取指定索引的元素:E get(int index)
(6)獲取鏈表大小(包含元素的個數):int size()
三、ArrayList容器類
ArrayList是List接口的實現類。
1.初始化
初始化分為無參初始化和有參初始化兩種。
1.1無參初始化
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
底層elementData數組指向DEFAULTCAPACITY_EMPTY_ELEMENTDATA數組,默認為空數組時,第一次添加元素,擴容為10。
擴充:泛型只是在編譯的時候進行規則校驗,不將代碼只有轉換為對應的數據類型。
//泛型類型:一定是引用數據類型 //泛型使用好處:幫助我們建立類型安全的結合,使用時傳入具體的類型 //代碼的可讀性增強,程序更安全
1.2有參初始化
(1)ArrayList (int initialCapacity)
初始化時根據數字,如果>0則是對應數字內存;
== 0 是EMPTY_ELEMENTDATA數組;
< 0 時報錯;
可以根據預估規模,設置初始值,可以減少擴容頻次。
(2)ArrayList (Collection <? extends E >c)
2.數據結構
底層是基于數組實現的,隨著元素的增加而動態擴容;
使用場景:適合數據連續性遍歷,讀多寫少的場景。
3.常用方法
3.1增加元素
(1)boolean add(E e) 添加指定元素到集合尾部
ArrayList<String> arrayList=new ArrayList<>();
//添加元素boolean add(E,e)添加指定元素到集合尾部
boolean b1=arrayList.add("g關羽");
boolean b2=arrayList.add("z張飛");
System.out.println(arrayList);
(2)void add(int index, E element)添加新元素到集合指定的下標位置
arrayList.add(2,"rosie");
System.out.println(arrayList);
(3)boolean addAll(Collection<? extends E> c) 添加集合C 內所有元素到當前集合
List<String> list=Arrays.asList("lisa","jisoo","rosie","jennie");
arrayList.addAll(list);
System.out.println("集合內容為:"+arrayList);
(4)boolean addAll(int index,Collection<? extends E> c) 從指定的位置開始,將指定 collection 中的所有元素插入到此列表中
//使用Arrays快速生成一個List類型的集合
List<String> list= Arrays.asList("a阿拉善","b北京","d丹東");
//boolean addAll(int index,Collection<? extends E> c)
//從指定的位置開始,將指定Collection中的所有元素插入到此列表中
boolean b6=arrayList.addAll(0,list);
System.out.println("添加集合是否成功:"+b6);
System.out.println(arrayList);
3.2查找元素
(1)int size()--查集合的長度,具體的元素個數
System.out.println("長度: " + arrayList.size());
(2)E get(int index)--獲取下標中的元素
String item = arrayList.get(0);
System.out.println("首元素: " + item);
System.out.println("尾元素: " + arrayList.get(arrayList.size() - 1));
(3)int indexOf(Object c)--查找找到指定元素的下標位置,如果不存在,則返回數組-1
int index = arrayList.indexOf("rosie");
System.out.println("元素下標為: " + index);
(4)boolean contains(Object c)--判斷集合中是否存在元素
boolean b = arrayList.contains("rosie");
System.out.println("元素是否存在: " + b);
(5)boolean isEmpty()--判斷集合是否為空
boolean b1 = arrayList.isEmpty();
System.out.println("是否為空: " + b1);
(6)List<E> SubList(int fromindex,int toIndex)--按照指定下標區間截取子集合 如果下標越界,則IndexOutOfBoundsException
不要強制類型轉化為ArrayList,可能會出現類型轉換異常;
對父類集合元素的增加或者刪除,均會導致列表的遍歷、增加、刪除產生ConcurrentModificationException異常;
List<String> subArrayList = arrayList.subList(0, arrayList.size());
System.out.println("截取后的集合: " + subArrayList);
(7)boolean equals(Object o)--判斷兩個集合中的元素是否相同
boolean b2 = arrayList.equals(subArrayList);
System.out.println("集合和截取后的集合是否相等: " + b2);
(8)使用for循環遍歷
普通for循環和增強for循環。
ArrayList<String> arrayList=new ArrayList<>();
arrayList.addAll(Arrays.asList("rosie","lisa","jisoo","rosie","jennie"));
System.out.println("集合內容為:"+arrayList);
//遍歷集合
//1.for
for (int i = 0; i <arrayList.size() ; i++) {System.out.println(arrayList.get(i)+" ");
}
System.out.println();
//2.foreach
for (String str:arrayList) {System.out.println(str+"__");
}System.out.println();
(9)使用迭代器進行遍歷
? ? ? ? Iterator<E> iterator{}--該迭代器僅提供向后遍歷
? ? ? ??ListIterator{}
//3.迭代器
//3.1獲取迭代器對象
Iterator<String> itor=arrayList.iterator();
//判斷是否有下一個元素
while(itor.hasNext()){//獲取下一個String item=itor.next();System.out.println(item+"**");
}
System.out.println();
//3.2獲取list迭代器對象
ListIterator<String> listIterator=arrayList.listIterator(arrayList.size());
//判斷是否有上一個元素
while(listIterator.hasPrevious()){//獲取上一個String item=listIterator.previous();System.out.println(item);
}
迭代器詳細解釋:
定義:是訪問數據的模型,主要是用來遍歷Collection集合。
Iterator接口詳細方法:
(1)hasNext():判斷當前指針位置的下一個位置是否有元素,有則返回true,沒有則返回false。
(2)next():移動指針到下一個位置,并返回該位置的元素。
(3)remove():①在使用迭代器的remove()操作時,會將更新后的modCount給expectedModCount,兩者會得到同步,但是在調用集合的remove()方法后,兩個不會進行同步,進而導致在checkForComodification()校驗時不通過,拋出java.util.ConcurrentModificationException異常。
②所以,在單線程下使用迭代器是沒有問題的,但是在多線程下同時操作集合就不允許了,可以通過fail-fast快速失敗機制,快速判斷是否存在同時操作問題。因此,集合在多線程下使用是不安全的。
迭代器總結:
(1)對任何集合都采用同一種訪問模型
(2)調用者對集合內部結構可以不清楚
(3)迭代器類返回的迭代器對象清楚要怎么進行集合內部訪問
3.3 修改元素
(1)oldE set(int index, E element)--用指定的元素替代此列表中指定位置上的元素。
ArrayList<String> arrayList=new ArrayList<>();
arrayList.addAll(Arrays.asList("朱元璋","朱祁鎮","朱棣","朱棣","朱高熾"));
System.out.println("集合內容為:"+arrayList);
//1.oldE set(int index, E element):用指定的元素替代此列表中指定位置上的元素。
String str=arrayList.set(2,"烏薩奇");
int index=arrayList.indexOf("朱元璋");
if(index>=0){arrayList.set(index,"鄭和");
}
System.out.println("修改后的元素為:"+str);
3.4 刪除元素
(1)E remove(int index)--根據指定索引刪除元素,并把刪除的元素返回
????????如果下標超過集合的最大下標,輸出IndexOutOfBoundsException
//E remove(int index):根據指定索引刪除元素,并把刪除的元素返回
String item =arrayList.remove(0);
System.out.println("刪除的元素為:"+item);
(2)boolean remove(Object o):從集合中刪除指定的元素,刪除一個就返回(有多個就只刪除一個)
????????如果查到或者刪除1個就返回,并不是刪除所有相同的集合元素
//boolean remove(Object o):從集合中刪除指定的元素,刪除一個就返回
boolean b=arrayList.remove("朱棣");
System.out.println("刪除的元素是否成功:"+b);
(3)void clear():刪除集合中的所有元素,此集合仍舊存在,集合元素長度變0。
//void clear():刪除集合中的所有元素,此集合仍舊存在,集合元素長度變0
arrayList.clear();
System.out.println("操作后的集合為:"+arrayList);
(4)boolean removeAll(Collection<?> c)--- 差集
????????從集合中刪除一個指定的集合元素: 刪除A集合中存在B集合中的所有相同的元素,如果有刪除返回True。
ArrayList<String> arrayList1=new ArrayList<>();
ArrayList<String> arrayList2=new ArrayList<>();
arrayList1.addAll(Arrays.asList("朱元璋","朱祁鎮","朱棣","朱棣","朱高熾"));
arrayList2.addAll(Arrays.asList("孫皇后","朱祁鎮","朱棣","李時珍","朱高熾"));
System.out.println("arraylist1:"+arrayList1);
System.out.println("arraylist2:"+arrayList2);//boolean removeAll(Collection<?> c)--- 差集 誰調用修改誰
boolean b1=arrayList1.removeAll(arrayList2);
System.out.println(b1);
System.out.println("arrayList1和arrayList2的差集為:"+arrayList1);
(5)boolean retainAll(Collection<?> c) --交集
????????保留集合A和集合B中相同的元素,刪除不同的元素,誰調用操作的是就是誰
//boolean retainAll(Collection<?> c) --交集 誰調用修改誰
boolean b=arrayList1.retainAll(arrayList2);
System.out.println(b);
System.out.println("arrayList1和arrayList2的交集為:"+arrayList1);
3.5 其他方法
(1)list.clone()克隆一個集合,得到的一個長度,個數,內容,順序完全一致的集合,單是復制了一份。
//Object clone() 克隆一個集合,得到一個長度,個數,內容,順序完全一致的集合,單是復制了一份
ArrayList<String> list1=new ArrayList<>();
list1.addAll(Arrays.asList("zyz朱元璋","zqz朱祁鎮","zd朱棣","lsz李時珍","zgc朱高熾","shh孫皇后"));//克隆
Object obj=list1.clone();
if (obj instanceof ArrayList){ArrayList<String> cloneList=(ArrayList<String>)obj;System.out.println(cloneList);
}
(2)list.sort() 對list中的內容進行排序,需要自定義排序規則。
//list.sort() 對list中的內容進行排序,需要自定義排序規則
list1.sort(new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {//先按照字符串的長度排,長度相同按照內容排序if (o1.length()==o2.length()){return o1.compareTo(o2);}else{return o1.length()-o2.length();}}
});
System.out.println("排序后的結果為:"+list1);
(3)轉換--Object[] toArray()--T[] toArray(T[] a)
//Object[] toArray()
Object[] objs=list1.toArray();
System.out.println("轉數組后:"+Arrays.toString(objs));
//T[] toArray(T[] a),返回的數組長度以集合對象或者傳入的參數的長度較長的那個為準
String[] strs=list1.toArray(new String[10]);
System.out.println(Arrays.toString(strs));
4.擴容機制
(1)無參構造的對象初始容量為0,第一次添加元素時,擴容內存大小容量為10。
(2)有參構造時,根據參數設置內存大小
在進行 arrayList1.add()時,如果數組容量不足時,按照原容量的1.5倍進行擴容增長。
(3)最大容量為:Integer.MAX_VALUE - 8 到 Inter.Max_Value之間,如果超出。則輸出OutMemoryError錯誤。
5.總結特點
(1)在內存中分配連續的空間,實現了長度可變的動態數組,有序集合(插入的順序==輸出的順序)。
(2)優點:查效率高,增加和刪除的效率比較低。
(3)缺點:添加和刪除需要大量移動元素效率,按照內容查詢效率低,線程不安全。
四、LinkedList類
1.初始化
(1)無參初始化--LinkedList() 構造一個空列表。
(2)有參初始化--LinkedList(Collection<? extends E> c)。
2.數據結構
(1)是雙向鏈表:鏈表由若干個Node對象組成,在每個節點里存了上一個節點和下一個節點的地址。
(2)擴容:由于采用鏈表結構,每次添加元素,都在創建新的Node節點并進行分配空間,所以不存在擴容。
3.常用方法
3.1增加元素
(1)boolean add(E,e) --添加到列表的尾部
(2)add(int index, E element) --在此列表中的指定位置插入指定的元素。
(3)void addFirst(E,e)--添加到列表的頭部。
(4)void addlast(E,e)--添加到列表的尾部。
(5)boolean addAll(Collection<? extends E> c)--按照指定集合的迭代器返回的順序將指定集合中的所有元素追加到此列表的末尾。
(6)boolean addAll(int index, Collection<? extends E> c)--將指定集合中的所有元素插入到此列表中,從指定的位置開始。
3.2查找元素
(1)int size()--查集合的長度,具體的元素個數。
(2)E get(int index)--查詢某個下標對應的元素。
(3)E getLast()獲取列表的尾元素。
(4)E getFirst() 獲取列表的頭元素。
(5)int indexOf(Object c) 查詢列表中的指定元素的下標,如果不存在,則刪除-1。
(6)boolean contains(Object o) 如果此列表包含指定的元素,則返回 true。
3.3修改元素
(1)set(int index, E element):用指定的元素替代此列表中指定位置上的元素。
3.4刪除元素
(1)E remove():刪除鏈表的頭元素。
(2)E remove(int index) :刪除鏈表中的指定下表的元素。
(3)boolean remove(Object o):刪除指定內容的元素。
(4)removeFirst() :從此列表中刪除并返回第一個元素。
(5)E removeLast() : 刪除鏈表中的尾元素。
3.5轉換為數組
(1)T[] toArray(T[] a)
3.6元素的遍歷
(1)for循環:普通、foreach。
(2)迭代器:iterator、listIterator。
//底層:雙鏈表
public class Demo01 {public static void main(String[] args) {//創建LinkedList的集合對象LinkedList<String> list=new LinkedList<>();//1.添加元素list.add("rosie");list.add(0,"lisa");list.addAll(Arrays.asList("jisoo","jennie"));list.addAll(1,Arrays.asList("hank","black"));list.addFirst("blackpink");list.addLast("pink");System.out.println(list);//2.獲取元素String item=list.get(4);System.out.println("獲取元素為:"+item);System.out.println("首元素為:"+list.getFirst());System.out.println("尾元素為:"+list.getLast());System.out.println(list);//3.刪除String item1=list.remove();System.out.println("刪除元素為:"+item1);System.out.println("刪除首元素為:"+list.removeFirst());System.out.println("刪除尾元素為:"+list.removeLast());System.out.println(list);// //1.遍歷1--不推薦
// for (int i = 0; i <list.size() ; i++) {
// System.out.println(list.get(i));
// }
//
// //2.遍歷 增強for
// for (String str: list) {
// System.out.println(str);
// }//3.普通迭代器
// Iterator<String> itor = list.iterator();
// while(itor.hasNext()){
// System.out.println(itor.next()+"-");
// }//3.list迭代器
// ListIterator<String> itor1 = list.listIterator(3);
// while(itor1.hasPrevious()){
// System.out.println(itor1.previous()+"*");
// }}
}
4.總結特點
(1)采用雙向鏈表存儲方式
(2)優點:插入、刪除元素效率高
(3)缺點:遍歷和隨機訪問效率低下
(4)適用場景:適合數據頻繁的添加和刪除操作,寫多讀少的場景