全文大約【5800】字,不說廢話,只講可以讓你學(xué)到技術(shù)、明白原理的純干貨!本文帶有豐富的案例及配圖視頻,讓你更好地理解和運(yùn)用文中的技術(shù)概念,并可以給你帶來具有足夠啟迪的思考......
一. List集合簡(jiǎn)介
1.概述
List本身是一個(gè)接口,該接口繼承自Collection接口,它有兩個(gè)常用的實(shí)現(xiàn)子類ArrayList和LinkedList。從功能特性上來看,List是有序、可重復(fù)的單列集合,集合中的每個(gè)元素都有對(duì)應(yīng)的順序索引,我們可以通過該索引來訪問指定位置上的集合元素。默認(rèn)情況下,List會(huì)按元素的添加順序給元素設(shè)置索引,第一個(gè)添加到List集合中的元素索引為0,第二個(gè)為1,后面依此類推。所以List的行為和數(shù)組幾乎完全相同,它們都是有序的存儲(chǔ)結(jié)構(gòu)。另外List集合中允許有重復(fù)的元素,甚至可以有多個(gè)null值。
但是如果我們是使用數(shù)組來添加和刪除元素,就會(huì)非常的不方便。比如從一個(gè)已有的數(shù)組{'A', 'B', 'C', 'D', 'E'}中刪除索引為2的元素,這個(gè)“刪除”操作實(shí)際上是把'C'后面的元素依次往前挪一個(gè)位置;而“添加”操作實(shí)際上是把指定位置以后的元素依次向后挪一個(gè)位置,騰出位置給新加入的元素。針對(duì)這兩種操作,使用數(shù)組實(shí)現(xiàn)起來都會(huì)非常麻2. 類關(guān)系
我們來看看List接口的類關(guān)系,如下圖所示:煩。所以在實(shí)際應(yīng)用中,我們?cè)鰟h元素時(shí),一般都是使用有序列表(如ArrayList),而不是使用數(shù)組。
2.類關(guān)系
我們來看看List接口的類關(guān)系,如下圖所示:
從這個(gè)類關(guān)系中我們可以看到,List接口繼承了Collection接口,并且有ArrayList、LinkedList、Vector等子類,其中Vector現(xiàn)在已經(jīng)不太常用了,所以我們重點(diǎn)掌握ArrayList和LinkedList就行。
3. 常用API方法
在List接口中定義了子類的一些通用方法,如下所示:
● boolean add(E e):在集合末尾添加一個(gè)數(shù)據(jù)元素;
● boolean add(int index, E e):在集合的指定索引出添加一個(gè)數(shù)據(jù)元素;
● E remove(int index):刪除集合中指定索引的元素;
● boolean remove(Object e):刪除集合中的某個(gè)元素;
● E get(int index):獲取集合中指定索引出的元素;
● int size():獲取集合的大小(包含元素的個(gè)數(shù))。
以上這些方法,就是我們?cè)陂_發(fā)時(shí)比較常用的幾個(gè)方法,大家需要記住哦。
4. List對(duì)象創(chuàng)建方式
List作為一個(gè)接口,我們通常不能直接new List來創(chuàng)建其對(duì)象,在Java中給我們提供了如下兩種創(chuàng)建List對(duì)象的方式:
● 通過多態(tài)方式創(chuàng)建:new List的某個(gè)子類,比如new ArrayList()等;
● 通過List.of()方法創(chuàng)建:of()方法可以根據(jù)給定的數(shù)據(jù)元素快速創(chuàng)建出List對(duì)象,但該方法不接受null值,如果傳入null會(huì)拋出NullPointerException異常。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Demo01 {
public static void main(String[] args) {
//創(chuàng)建List對(duì)象方式一:
List<String> list1=new ArrayList<>();
List<String> list2=new LinkedList<>();
//創(chuàng)建List對(duì)象方式二:
List<Integer> list3 = List.of(1,3,5,7,9);
//該方式不能傳入null參數(shù),否則會(huì)產(chǎn)生NullPointerException異常
//List<Integer> list4 = List.of(1,3,5,7,9,null);
}
}
5.List集合遍歷方式
很多時(shí)候,我們都會(huì)對(duì)集合進(jìn)行遍歷操作,也就是要把集合中的每個(gè)元素挨個(gè)的取出來,以下是幾種常用的集合遍歷方式:
● 普通for循環(huán)配合get(索引值)方法進(jìn)行遍歷:這種遍歷方式實(shí)現(xiàn)起來代碼較為復(fù)雜,且get(int)取值方法只對(duì)ArrayList比較高效,但對(duì)LinkedList效率較低,索引越大時(shí)訪問速度越慢。
● 增強(qiáng)for循環(huán)進(jìn)行遍歷:我們也可以使用增強(qiáng)for循環(huán)進(jìn)行遍歷,該方式比普通for循環(huán)實(shí)現(xiàn)起來更為簡(jiǎn)潔。
● 使用Iterator迭代器進(jìn)行集合遍歷:不同的List對(duì)象調(diào)用iterator()方法時(shí),會(huì)返回不同實(shí)現(xiàn)的Iterator對(duì)象,該Iterator對(duì)象對(duì)集合總是具有最高的訪問效率。
import java.util.Iterator;
import java.util.List;
public class Demo02 {
public static void main(String[] args) {
//List遍歷方式一,普通for循環(huán):
List<String> list = List.of("java", "大數(shù)據(jù)", "壹小編");
for(int i=0;i<list.size();i++) {
System.out.println("遍歷方式一,值="+list.get(i));
}
//List遍歷方式二,迭代器:
Iterator<String> it = list.iterator();
while(it.hasNext()){
//取出下一個(gè)值
String value = it.next();
System.out.println("遍歷方式二,值="+value);
}
//List遍歷方式三,增強(qiáng)for循環(huán):內(nèi)部會(huì)自動(dòng)使用Iterator
for(String item:list) {
System.out.println("遍歷方式三,item="+item);
}
}
}
上面提到的Iterator對(duì)象,有兩個(gè)常用方法,如下所示:
● boolean hasNext():該方法用于判斷集合中是否還有下一個(gè)元素;
● E next():該方法用于返回集合的下一個(gè)元素。
雖然使用Iterator遍歷List集合的代碼,看起來比使用索引較復(fù)雜,但I(xiàn)terator遍歷List集合的效率卻是最高效的方式。
另外只要是實(shí)現(xiàn)了Iterable接口的集合類,我們都可以直接使用for each增強(qiáng)循環(huán)來遍歷。在增強(qiáng)for循環(huán)中,Java編譯器會(huì)自動(dòng)把for each循環(huán)變成基于Iterator方式的遍歷方式。
6. List與數(shù)組的轉(zhuǎn)換方式
其實(shí)List與Array數(shù)組在很多地方都是比較相似的,比如都可以根據(jù)索引對(duì)數(shù)據(jù)元素進(jìn)行遍歷取值等操作。因?yàn)榇嬖谥@種相似之處,所以在List和數(shù)組之間是可以互相轉(zhuǎn)換的,即List集合可以轉(zhuǎn)成數(shù)組,數(shù)組也可以轉(zhuǎn)成List集合。
6.1 List轉(zhuǎn)數(shù)組
一般情況下,List轉(zhuǎn)數(shù)組有如下幾種方式:
● toArray()方法:該方法會(huì)返回一個(gè)Object[]數(shù)組,但該方法會(huì)丟失類型信息,在實(shí)際開發(fā)時(shí)較少使用;
● toArray(T[])方法:傳入一個(gè)與集合的數(shù)據(jù)元素類型相同的Array,List會(huì)自動(dòng)把元素復(fù)制到傳入的Array中;
● T[] toArray(IntFunction generator)方法:函數(shù)式寫法,這是Java中的新特性,后面小編會(huì)單獨(dú)講解。
import java.util.List;
public class Demo03 {
public static void main(String[] args) {
List<String> list = List.of("java", "大數(shù)據(jù)", "壹小編");
// List轉(zhuǎn)數(shù)組方式一:返回一個(gè)Object[]數(shù)組
Object[] array = list.toArray();
for (Object val : array) {
System.out.println("方式一,value="+val);
}
// List轉(zhuǎn)數(shù)組方式二,給toArray(T[])傳入與數(shù)組元素類型相同的Array,如果數(shù)組類型與集合數(shù)據(jù)元素類型不匹配則會(huì)產(chǎn)生如下異常:
// java.lang.ArrayStoreException: arraycopy: element type mismatch:
//can not cast one of the elements of java.lang.Object[] to the type of the destination array, java.lang.Integer
String[] array2 = list.toArray(new String[list.size()]);
for (String val : array2) {
System.out.println("方式二,value="+val);
}
// List轉(zhuǎn)數(shù)組方式三:返回一個(gè)String[]數(shù)組
String[] array3 = list.toArray(String[]::new);
for (String val : array3) {
System.out.println("方式二,value="+val);
}
}
}
在本案例中,第一種實(shí)現(xiàn)方式是調(diào)用toArray()方法直接返回一個(gè)Object[]數(shù)組,但這種方法會(huì)丟失類型信息,所以開發(fā)是很少使用。
第二種方式要給toArray(T[])方法傳入一個(gè)與集合數(shù)據(jù)元素類型相同的Array,List內(nèi)部會(huì)自動(dòng)把元素復(fù)制到傳入的Array數(shù)組中。如果Array類型與集合的數(shù)據(jù)元素類型不匹配,就會(huì)產(chǎn)生”java.lang.ArrayStoreException: arraycopy: element type mismatch: can not cast one of the elements of java.lang.Object[] to the type of the destination array......“異常。
第三種方式是通過List接口定義的T[] toArray(IntFunction generator)方法,這是一種函數(shù)式寫法,小編以后再單獨(dú)給大家講解。
6.2 數(shù)組轉(zhuǎn)List
反過來,數(shù)組也可以轉(zhuǎn)為List集合,一般的方式如下:
● List.of(T...)方法:該方法會(huì)返回一個(gè)只讀的List集合,如果我們對(duì)只讀List調(diào)用add()、remove()方法會(huì)拋出UnsupportedOperationException異常。其中的T是泛型參數(shù),代表要轉(zhuǎn)成List集合的數(shù)組;
● Arrays.asList(T...)方法:該方法也會(huì)返回一個(gè)List集合,但它返回的List不一定就是ArrayList或者LinkedList,因?yàn)長ist只是一個(gè)接口。
import java.util.Arrays;
import java.util.List;
public class Demo04 {
public static void main(String[] args) {
// 數(shù)組轉(zhuǎn)List的方式一:List.of()返回一個(gè)只讀的集合,不能進(jìn)行add/remove等修改操作。
List<Integer> values = List.of(1,8,222,10,5);
for (Integer val : values) {
System.out.println("方式一,value="+val);
//該集合是一種只讀的集合,不能在遍歷時(shí)進(jìn)行增刪改等更新操作,只能進(jìn)行讀取操作,
//否則會(huì)產(chǎn)生java.lang.UnsupportedOperationException異常
//values.remove(0);
}
// 數(shù)組轉(zhuǎn)List的方式二:Arrays.asList()返回一個(gè)只讀的集合,不能進(jìn)行add/remove等修改操作。
List<String> items = Arrays.asList("java","壹小編","元宇宙");
for (String item : items) {
System.out.println("方式二,value="+item);
//不可以進(jìn)行增刪改操作
//items.add("sss");
//items.remove(0);
}
}
}
在本案例中,無論我們是通過List.of()方法,還是通過Arrays.asList()方法,都只會(huì)返回一個(gè)只讀的集合。這種集合在遍歷時(shí)不能進(jìn)行增刪改等更新操作,只能進(jìn)行讀取操作,否則會(huì)產(chǎn)生java.lang.UnsupportedOperationException異常。
二. ArrayList集合
1.簡(jiǎn)介
ArrayList是一個(gè)數(shù)組隊(duì)列,位于java.util包中,它繼承自AbstractList,并實(shí)現(xiàn)了List接口。其底層是一個(gè)可以動(dòng)態(tài)修改的數(shù)組,該數(shù)組與普通數(shù)組的區(qū)別,在于它沒有固定的大小限制,我們可以對(duì)其動(dòng)態(tài)地進(jìn)行元素的添加或刪除。
存儲(chǔ)在集合內(nèi)的數(shù)據(jù)被稱為”元素“,我們可以利用索引來訪問集合中的每個(gè)元素。為了方便我們操作這些元素,ArrayList給我們提供了相關(guān)的添加、刪除、修改、遍歷等功能。
因?yàn)锳rrayList的底層是一個(gè)動(dòng)態(tài)數(shù)組,所以該集合適合對(duì)元素進(jìn)行快速的隨機(jī)訪問(遍歷查詢),另外尾部成員的增加和刪除操作速度也較快,但是其他位置上元素的插入與刪除速度相對(duì)較慢。基于這種特性,所以ArrayList具有查詢快,增刪慢的特點(diǎn)。
2.常用方法
ArrayList給我們提供了如下這些方法,我們可以先來了解一下:
接下來小編就挑選幾個(gè)常用的方法,通過幾個(gè)案例來給大家講解一下ArrayList的用法。
3.添加元素
ArrayList給我們提供了多個(gè)與添加相關(guān)的方法,比如add()和addAll()方法,可以將元素添加到集合中。另外如果我們要計(jì)算ArrayList中元素的數(shù)量,可以使用size()方法。
import java.util.ArrayList;
public class Demo05 {
public static void main(String[] args) {
//創(chuàng)建ArrayList集合,<String>中的是泛型,后面我們會(huì)專門講解泛型
ArrayList<String> names = new ArrayList<String>();
//一個(gè)一個(gè)地添加元素
names.add("java");
names.add("數(shù)學(xué)");
//遍歷集合
for (String name : names) {
System.out.println("name="+name+",size="+names.size());
}
ArrayList<String> names2 = new ArrayList<String>();
names2.add();
//在A集合中追加B集合
names2.addAll(names);
//遍歷集合
for (String name : names2) {
System.out.println("name="+name);
}
}
}
在上面的代碼中,這部分是泛型,小編會(huì)在后面給大家專門講解,敬請(qǐng)期待哦。
4. 遍歷元素
我們對(duì)ArrayList中元素進(jìn)行遍歷的方式,其實(shí)與List的遍歷是一樣的,我們可以使用普通for循環(huán)、增強(qiáng)for循環(huán)、Iterator迭代器等方式對(duì)集合進(jìn)行遍歷,這里我們就不再單獨(dú)展示其用法了。
5. 修改元素
我們使用add()方法將元素添加到集合中之后,如果想對(duì)集合中的元素進(jìn)行修改,可以使用set()方法。
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) {
//創(chuàng)建ArrayList集合,<String>中的是泛型,后面我們會(huì)專門講解泛型
ArrayList<String> names = new ArrayList<String>();
//一個(gè)一個(gè)地添加元素
names.add("java");
names.add("數(shù)學(xué)");
//修改集合中的元素:第一個(gè)參數(shù)是集合中的索引,第二個(gè)是要修改的值
names.set(1, "Android");
names.set(2, "iOS");
//遍歷集合
for (String name : names) {
System.out.println("name="+name);
}
}
}
6.刪除元素
如果我們要?jiǎng)h除ArrayList中的元素,可以使用remove()、removeAll()等方法。
import java.util.ArrayList;
public class Demo07 {
public static void main(String[] args) {
//創(chuàng)建ArrayList集合,<String>中的是泛型,后面我們會(huì)專門講解泛型
ArrayList<String> names = new ArrayList<String>();
//一個(gè)一個(gè)地添加元素
names.add("java");
names.add("數(shù)學(xué)");
//刪除集合中指定位置上的某個(gè)元素
names.remove(0);
//刪除集合中的某個(gè)指定元素
names.remove("java");
//遍歷集合
for (String name : names) {
System.out.println("name="+name);
}
ArrayList<String> names2 = new ArrayList<String>();
names2.add("語文");
names2.add("英語");
names2.add("數(shù)學(xué)");
//刪除本集合中的另一個(gè)集合
names2.removeAll(names);
//遍歷集合
for (String name : names2) {
System.out.println("name2="+name);
}
}
}
7.集合排序
我們可以使用Collections.sort()方法對(duì)集合進(jìn)行升序排列。
import java.util.ArrayList;
import java.util.Collections;
public class Demo08 {
public static void main(String[] args) {
//創(chuàng)建ArrayList集合
ArrayList<Integer> nums = new ArrayList<>();
//一個(gè)一個(gè)地添加元素
nums.add(100);
nums.add(85);
nums.add(120);
nums.add(55);
//對(duì)集合進(jìn)行排序,默認(rèn)是升序排列
Collections.sort(nums);
//遍歷集合
for (Integer num : nums) {
System.out.println("num="+num);
}
}
}
三. LinkedList集合
1.簡(jiǎn)介
LinkedList采用鏈表結(jié)構(gòu)來保存數(shù)據(jù),所以是一種鏈表集合,類似于ArrayList,也是List的一個(gè)子類,位于java.util包中。它的底層是基于線性鏈表這種常見的數(shù)據(jù)結(jié)構(gòu),但并沒有按線性的順序存儲(chǔ)數(shù)據(jù),而是在每個(gè)節(jié)點(diǎn)中都存儲(chǔ)了下一個(gè)節(jié)點(diǎn)的地址。
LinkedList的優(yōu)點(diǎn)是便于向集合中插入或刪除元素,尤其是需要頻繁地向集合中插入和刪除元素時(shí),使用LinkedList類比ArrayList的效率更高。但LinkedList隨機(jī)訪問元素的速度則相對(duì)較慢,即檢索集合中特定索引位置上的元素速度較慢。
2. LinkedList類關(guān)系
LinkedList直接繼承自AbstractSequentialList,并實(shí)現(xiàn)了List、Deque、Cloneable、Serializable等多個(gè)接口。通過實(shí)現(xiàn)List接口,具備了列表操作的能力;通過實(shí)現(xiàn)Cloneable接口,具備了克隆的能力;通過實(shí)現(xiàn)Queue和Deque接口,可以作為隊(duì)列使用;通過實(shí)現(xiàn)Serializable接口,可以具備序列化能力。LinkedList類結(jié)構(gòu)關(guān)系如下圖所示:
3. LinkedList與ArrayList對(duì)比
與ArrayList相比,LinkedList進(jìn)行添加和刪除的操作效率更高,但查找和修改的操作效率較低。基于這種特性,我們可以在以下情況中使用ArrayList:
● 需要經(jīng)常訪問獲取列表中的某個(gè)元素;
● 只需要在列表的末尾進(jìn)行添加和刪除某個(gè)元素。
當(dāng)遇到如下情況時(shí),可以考慮使用LinkedList:
● 需要經(jīng)常通過循環(huán)迭代來訪問列表中的某些元素;
● 需要經(jīng)常在列表的開頭、中間、末尾等位置進(jìn)行元素的添加和刪除操作。
4. 常用方法
LinkedList中的很多方法其實(shí)都來自于List接口,所以它的很多方法與ArrayList是一樣的。但由于其自身特點(diǎn),也具有一些特有的常用方法,這里小編只列出LinkedList特有的常用方法,如下表所示:
對(duì)這些方法進(jìn)行基本的了解之后,接下來我們選擇幾個(gè)核心方法來來看看具體該怎么使用。
5. 添加/刪除元素
我們可以通過addFirst()和addLast()方法,分別在鏈表的開頭和結(jié)尾添加一個(gè)元素。當(dāng)我們要頻繁地在一個(gè)列表的開頭和結(jié)尾進(jìn)行元素添加、刪除時(shí),使用LinkedList要比ArrayList的效率更高。
import java.util.LinkedList;
public class Demo09 {
public static void main(String[] args) {
// 創(chuàng)建LinkedList集合
LinkedList<String> names = new LinkedList<String>();
// 一個(gè)一個(gè)地添加元素
names.add("java");
names.add("數(shù)學(xué)");
//在鏈表的開頭添加元素
names.addFirst("");
//在鏈表的結(jié)尾添加元素
names.addLast("歷史");
// 遍歷集合
for (String name : names) {
System.out.println("name=" + name);
}
//移除鏈表開頭的元素
names.removeFirst();
//移除鏈表結(jié)尾的元素
names.removeLast();
}
}
6.迭代獲取元素
我們可以通過getFirst()、getLast()等方法獲取到集合中的第一個(gè)、最后一個(gè)元素。
import java.util.LinkedList;
public class Demo10 {
public static void main(String[] args) {
// 創(chuàng)建LinkedList集合
LinkedList<String> names = new LinkedList<String>();
// 一個(gè)一個(gè)地添加元素
names.add("java");
names.add("數(shù)學(xué)");
System.out.println("first=" + names.getFirst());
System.out.println("last=" + names.getLast());
// 迭代遍歷集合
for (String name : names) {
System.out.println("name=" + name);
}
}
}
四. 結(jié)語
至此,小編就把List集合給大家講解完畢了,最后我們?cè)賮砜纯幢疚牡闹攸c(diǎn)吧:
● List是按索引順序訪問的、長度可變的有序列表;
● 一般開發(fā)時(shí),ArrayList比LinkedList的使用更頻繁;
● List和Array可以相互轉(zhuǎn)換;
● 集合遍歷時(shí)有多種方式,增強(qiáng)for循環(huán)和Iterator迭代器的效率更高;
● ArrayList與LinkedList都是List接口的實(shí)現(xiàn)類,都實(shí)現(xiàn)了List中所有未實(shí)現(xiàn)的方法,但實(shí)現(xiàn)的方式有所不同;
● ArrayList底層的數(shù)據(jù)結(jié)構(gòu)基于動(dòng)態(tài)數(shù)組,訪問元素速度快于LinkedList,在快速訪問數(shù)據(jù)時(shí)ArrayList的執(zhí)行效率比較高;
● LinkedList底層的數(shù)據(jù)結(jié)構(gòu)基于鏈表,占用的內(nèi)存空間較大,但批量插入或刪除數(shù)據(jù)時(shí)快于ArrayList。當(dāng)頻繁向集合中插入和刪除元素時(shí),使用LinkedList比ArrayList的效率更高。