Java集合-List
點(diǎn)擊關(guān)注,與你共同成長!

Java集合-List
List接口(java.util.List)代表著有序的對象集合, List中包含的元素可以根據(jù)它們在List中的內(nèi)部順序進(jìn)行插入、訪問、迭代和刪除,元素的順序就是這個(gè)數(shù)據(jù)結(jié)構(gòu)被稱為列表的原因。List中的每個(gè)元素都有一個(gè)索引,第一個(gè)元素的索引是0,第二個(gè)元素的索引是1。索引的意思是“離List的第一個(gè)元素間隔多少個(gè)元素”。因?yàn)榈谝粋€(gè)元素在List的開頭,所有間隔為0。如果List不是類型化的,使用Java泛型,那么甚至可以在同一個(gè)列表中混合不同類型(類)的對象
然而,在時(shí)間開發(fā)中很少在List中混合不同類型的對象。
List是一個(gè)標(biāo)準(zhǔn)的接口,是Collection接口的子類,意味著繼承了Collection的特性。
List 和Set
List和Set非常相似,都代表了一組元素的集合,但是也有一些明顯的不一樣, 這些差異反映在List和Set接口提供的方法中。
第一個(gè)不同是List中可以重復(fù)添加相同的元素,而Set中只能添加一次。第二個(gè)不同是,List中的元素是有順序的,可以按順序迭代,Set不會對內(nèi)部保存的元素的順序做出任何承諾。
List的實(shí)現(xiàn)
作為 Collection 的子類,Collection中的所有方法在List中同樣實(shí)用。既然List是個(gè)接口,所有初始化時(shí)需要具體的實(shí)現(xiàn),可以選擇下面的List的實(shí)現(xiàn):
java.util.ArrayList java.util.LinkedList java.util.Vector java.util.Stack
這些實(shí)現(xiàn)中, ArrayList應(yīng)用最廣泛。在java.util.concurrent包中也有List的并發(fā)類的實(shí)現(xiàn),更多細(xì)節(jié)后面的文章會講述。
創(chuàng)建List
通過List的實(shí)現(xiàn)創(chuàng)建List實(shí)例,下面是代碼:
List listA = new ArrayList();
List listB = new LinkedList();
List listC = new Vector();
List listD = new Stack();
記住,最常用的是ArrayList,其他的實(shí)現(xiàn)我們可以根據(jù)具體場景使用。
List的泛型
List中默認(rèn)的是添加Object,但從JAVA5以后增加了泛型,可以讓List中添加的元素類型受到限制,下面是代碼:
List<MyObject> list = new ArrayList<MyObject>();
List中只能添加 MyObject的實(shí)例對象,同時(shí)迭代元素時(shí)不許強(qiáng)制類型轉(zhuǎn)換,看看下面的代碼:
List<MyObject> list = new ArrayList<MyObject>();
list.add(new MyObject("First MyObject"));
MyObject myObject = list.get(0);
for(MyObject anObject : list){
//do someting to anObject...
}
如果不使用泛型將是下面的樣子:
List list = new ArrayList(); //no generic type specified
list.add(new MyObject("First MyObject"));
MyObject myObject = (MyObject) list.get(0); //cast needed
for(Object anObject : list){
//cast needed
MyObject theMyObject = (MyObject) anObject;
//do someting to anObject...
}
注意如何將從列表中檢索到的MyObject實(shí)例強(qiáng)制轉(zhuǎn)換為MyObject,如果沒有設(shè)置泛型,編譯的時(shí)候java只識別Object實(shí)例對象,需要強(qiáng)制轉(zhuǎn)換它們的類型。List變量指定泛型是很好得實(shí)踐,避免了向List中插入錯(cuò)誤得類型,能夠從List中檢索對象,而不必將它們轉(zhuǎn)換為其真實(shí)類型, 而且-它幫助代碼的讀者了解List應(yīng)該包含什么類型的對象。只有在有充分理由的情況下才應(yīng)該省略泛型類型。
List中插入元素
可以通過List得add()方法插入元素,下面是代碼:
List<String> listA = new ArrayList<>();
listA.add("element 1");
listA.add("element 2");
listA.add("element 3");
調(diào)用了三次add()方法,增加String到List中。
插入null值
實(shí)際是有可能向List中插入null值的,下面是代碼:
Object element = null;
List<Object> list = new ArrayList<>();
list.add(element);
向指定的索引插入元素
可以將元素插入指定索引位置,List有一個(gè)add()方法,第一個(gè)參數(shù)是索引的位置,第二個(gè)參數(shù)是元素,下面是代碼:
list.add(0, "element 4");
如果List中已經(jīng)包含元素,那么這些元素現(xiàn)在將在列表的內(nèi)部序列往后退一個(gè)序列,比如在插入新元素前索引是0,然后在0的位置在插入一個(gè)元素,則原來的元素索引為1。w element was inserted at index 0, will get pushed to index 1 etc.
添加另外一個(gè)List的所有元素
可以將一List的所有元素加到另外一個(gè)List中,可以使用List的addAll()方法,下面是代碼示例:
List<String> listSource = new ArrayList<>();
listSource.add("123");
listSource.add("456");
List<String> listDest = new ArrayList<>();
listDest.addAll(listSource);
上面例子把listSource 中所有的元素添加到listDest ,
addAll()方法的參數(shù)是Collection ,所以可以傳入 List 或者Set作為參數(shù),意思就是可以把通過addAll()方法把List或者Set元素加到List中。
從List中獲取元素
可以通過 List的索引獲取元素,可以用get(int index),下面的代碼是通過索引訪問List的元素:
List<String> listA = new ArrayList<>();
listA.add("element 0");
listA.add("element 1");
listA.add("element 2");
//access via index
String element0 = listA.get(0);
String element1 = listA.get(1);
String element3 = listA.get(2);
也可以通過迭代按內(nèi)部順序訪問List的元素,這個(gè)后面會講述。
查找List中的元素
可以通過List的下面兩個(gè)方法查找是否包含元素:
indexOf() lastIndexOf() indexOf() 方法查找的是給定元素在List中元素第一次出現(xiàn)的索引:
List<String> list = new ArrayList<>();
String element1 = "element 1";
String element2 = "element 2";
list.add(element1);
list.add(element2);
int index1 = list.indexOf(element1);
int index2 = list.indexOf(element2);
System.out.println("index1 = " + index1);
System.out.println("index2 = " + index2);
輸出結(jié)果:
index1 = 0
index2 = 1
查找元素List中最后一個(gè)位置
lastIndexOf()方法可以查找元素在List中出現(xiàn)的最后一個(gè)索引值,下面是代碼:
List<String> list = new ArrayList<>();
String element1 = "element 1";
String element2 = "element 2";
list.add(element1);
list.add(element2);
list.add(element1);
int lastIndex = list.lastIndexOf(element1);
System.out.println("lastIndex = " + lastIndex);
輸出結(jié)果:
lastIndex = 2
元素“element 1”在List中出現(xiàn)了兩次,最后一個(gè)位置索引是2(第三個(gè)元素)。
檢查List中是否包含給定的元素
可以通過 List的contains()方法檢查List中是否包含給定的元素:
List<String> list = new ArrayList<>();
String element1 = "element 1";
list.add(element1);
boolean containsElement =
list.contains("element 1");
System.out.println(containsElement);
輸出結(jié)果:
true
因?yàn)長ist中包含"element 1",為了決定List中是否包含給定的元素,List內(nèi)部戶將要迭代,并且調(diào)用equal()方法檢查是否與給定的元素相等。既然可以添加null值到List中,那么同樣可以檢查List中是否包含null值,下面是代碼:
list.add(null);
containsElement = list.contains(null);
System.out.println(containsElement);
顯然,如果contains()中傳入的是null值,那么contains()內(nèi)部調(diào)用的不是equals()方法而是==。
從List中移除元素
可以通過下面兩個(gè)方法從List中移除元素:
remove(Object element) remove(int index) remove(Object element)移除是List中如果存在的話第一個(gè)出現(xiàn)的元素,所有后面的元素前移一個(gè),索引值減1,下面是代碼:
List<String> list = new ArrayList<>();
String element = "first element";
list.add(element);
list.remove(element);
這個(gè)例子首先增加了一個(gè)元素然后移除,List的 remove(int index)方法是移除給定索引值對應(yīng)的元素,所有的后面元素往前移動(dòng)一位,索引值也相應(yīng)的減1,下面是代碼:
List<String> list = new ArrayList<>();
list.add("element 0");
list.add("element 1");
list.add("element 2");
list.remove(0);
執(zhí)行完后,List包含 element 1 和 element 2 ,索引值分別是 0 和1。第一個(gè)元素 (element 0) 已經(jīng)被從List中移除。
從List中移除所有元素
List接口包含了clear()方法,這個(gè)方法可以移除List中的所有元素。從List中刪除所有元素也被稱為清除List,下面是代碼:
List<String> list = new ArrayList<>();
list.add("object 1");
list.add("object 2");
//etc.
list.clear();
首先創(chuàng)建List,其次向List中添加兩個(gè)元素,第三調(diào)用clear()方法,調(diào)用后List為空。
在List保留給定List中的所有元素
List接口中有個(gè)retainAll(),它能夠保留一個(gè)列表中的所有元素,這些元素也存在于另一個(gè)列表中。意思就是,retain()方法移除目標(biāo)List中在給定的List,中不存在的元素, 就是兩個(gè)List的交集,下面是代碼:
List<String> list = new ArrayList<>();
List<String> otherList = new ArrayList<>();
String element1 = "element 1";
String element2 = "element 2";
String element3 = "element 3";
String element4 = "element 4";
list.add(element1);
list.add(element2);
list.add(element3);
otherList.add(element1);
otherList.add(element3);
otherList.add(element4);
list.retainAll(otherList);
首先創(chuàng)建兩個(gè)List,然后每個(gè)List都新加三個(gè)元素,第三步調(diào)用list的retainAll()方法參數(shù)是otherList , list.retainAll(otherList)執(zhí)行完后, list中只有 list 和 otherList都有的元素也就是, element1 和element3 。
List大小
可以通過size()獲取List的大小,也就是List中元素的個(gè)數(shù):
List<String> list = new ArrayList<>();
list.add("object 1");
list.add("object 2");
int size = list.size();
List的子集
List接口包含一個(gè)subList()方法,這個(gè)方法可以創(chuàng)建一個(gè)原來List的子集。subList()有兩個(gè)參數(shù):開始索引和結(jié)束索引,第一個(gè)索引是原List中對應(yīng)的元素索引,第二個(gè)是結(jié)束索引,子集中包含起始索引不包括結(jié)束索引,和String的substring()非常相似。下面是代碼:
List<String> list = new ArrayList<>();
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 4");
List<String> sublist = list.subList(1, 3);
執(zhí)行完list.subList(1,3)后, sublist will contain the 包含索引為1(第二個(gè))和索引為 2(第三個(gè))元素。記住原來List中半酣四個(gè)元素,索引是0到3,調(diào)用list.subList(1,3)后將包含索引1,但不包括索引3,因此將元素保留在索引1和索引2處。
List 轉(zhuǎn)換成 Set
可以通過創(chuàng)建啊一個(gè)新的Set,然后調(diào)用add方法List作為參數(shù),Set會刪除List中的重復(fù)元素,只保留一個(gè),下面是代碼:
List<String> list = new ArrayList<>();
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
Set<String> set = new HashSet<>();
set.addAll(list);
注意 List中element 3添加了兩次,Set中包含一次,執(zhí)行后Set中包含元素 element 1, element 2 和 element 3 。
List轉(zhuǎn)換成數(shù)組
可以通過List的 toArray()方法,將List轉(zhuǎn)換成數(shù)組:
List<String> list = new ArrayList<>();
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
Object[] objects = list.toArray();
也可以將List轉(zhuǎn)換成指定類型得數(shù)組,下面是代碼:
List<String> list = new ArrayList<>();
list.add("element 1");
list.add("element 2");
list.add("element 3");
list.add("element 3");
String[] objects1 = list.toArray(new String[0]);
注意,即使我們將大小為0的字符串?dāng)?shù)組傳遞給toArray(),返回的數(shù)組中也會包含List中的所有元素,它將具有與List相同數(shù)量的元素。
數(shù)組轉(zhuǎn)換成List
同樣可以將數(shù)組轉(zhuǎn)換成List,下面是代碼:
String[] values = new String[]{ "one", "two", "three" };
List<String> list = (List<String>) Arrays.asList(values);
Arrays.asList()方法可以將數(shù)組轉(zhuǎn)成List。
List排序
可以調(diào)用 Collections 的sort()方法給List排序,之前在前面Collections文章中講到 ,但是下面我會講述幾種方法:
使用Comparable對List排序
如果List中得元素都是實(shí)現(xiàn)了Comparable (java.lang.Comparable)接口,那么他們可以自行比較,看下面得代碼:
List<String> list = new ArrayList<>();
list.add("c");
list.add("b");
list.add("a");
Collections.sort(list);
String類實(shí)現(xiàn)了 Comparable接口,可以使用Collections 的sort()方法對他們進(jìn)行自然排序。
使用Comparator對List排序
如果List中的對象元素沒有實(shí)現(xiàn)Comparable接口,或者想通過其他方式對它們排序而不是用compare()的實(shí)現(xiàn),那么可以實(shí)現(xiàn)Comparator (java.util.Comparator)接口。下面是使用Comparator類對Car類型的List排序:
public class Car{
public String brand;
public String numberPlate;
public int noOfDoors;
public Car(String brand, String numberPlate, int noOfDoors) {
this.brand = brand;
this.numberPlate = numberPlate;
this.noOfDoors = noOfDoors;
}
}
下面代碼是對List中的Car對象排序:
List<Car> list = new ArrayList<>();
list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));
Comparator<Car> carBrandComparator = new Comparator<Car>() {
@Override
public int compare(Car car1, Car car2) {
return car1.brand.compareTo(car2.brand);
}
};
Collections.sort(list, carBrandComparator);
上面是實(shí)現(xiàn) Comparator的例子,實(shí)現(xiàn)只是簡單的比較了Car的brand屬性,也可以再實(shí)現(xiàn)Comparator 比較number plates或者門的數(shù)量noOfDoors屬性, 同樣可以使用Lambda表達(dá)式實(shí)現(xiàn)Comparator,下面是代碼示例:
List<Car> list = new ArrayList<>();
list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));
Comparator<Car> carBrandComparatorLambda =
(car1, car2) -> car1.brand.compareTo(car2.brand);
Comparator<Car> carNumberPlatComparatorLambda =
(car1, car2) -> car1.numberPlate.compareTo(car2.numberPlate);
Comparator<Car> carNoOfDoorsComparatorLambda =
(car1, car2) -> car1.noOfDoors - car2.noOfDoors;
Collections.sort(list, carBrandComparatorLambda);
Collections.sort(list, carNumberPlatComparatorLambda);
Collections.sort(list, carNoOfDoorsComparatorLambda);
迭代List
可以通過幾種不同的方法迭代List:
使用Iterator 使用for-each循環(huán) 使用for循環(huán) 使用Stream API 使用Iterator迭代List
第一種方法使用Iterator 迭代List,下面是代碼:
List<String> list = new ArrayList<>();
list.add("first");
list.add("second");
list.add("third");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String next = iterator.next();
}
可以調(diào)用List的iterator()Iterator,一旦獲取了Iterator ,就可以一直調(diào)用 hasNext()方法循環(huán)直到返回fasle, 調(diào)用hasNext()是在while循環(huán)中完成的。While內(nèi)部循環(huán),可以調(diào)用Iterator的 next() 方法獲取下一個(gè)元素。如果List使用了泛型,那么可以在while循環(huán)中保存一些對象轉(zhuǎn)換。下面是代碼:
List<String> list = new ArrayList<>();
list.add("first");
list.add("second");
list.add("third");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String obj = iterator.next();
}
使用For-Each循環(huán)迭代List
第二種方法For-Each循環(huán)是在Java5中引入的,下面是代碼:
List list = new ArrayList();
list.add("first");
list.add("second");
list.add("third");
for(Object element : list) {
System.out.println(element);
}
for循環(huán)對列表中的每個(gè)元素執(zhí)行一次,在for循環(huán)中,每個(gè)元素依次綁定到obj變量,下面是使用泛型的List迭代:
List<String> list = new ArrayList<String>();
//add elements to list
for(String element : list) {
System.out.println(element);
}
注意這人的泛型是String, 因此,可以將for循環(huán)中的變量類型設(shè)置為String。
使用For循環(huán)迭代List
第三種方法是使用標(biāo)準(zhǔn)的for循環(huán)迭代:
List list = new ArrayList();
list.add("first");
list.add("second");
list.add("third");
for(int i=0; i < list.size(); i++) {
Object element = list.get(i);
}
For循環(huán)創(chuàng)建一個(gè)int變量,初始值是0,然后循環(huán),直到i的值等于List的大下停止,也就是小于List的大小時(shí)一直循環(huán),i的值每次加1,for循環(huán)內(nèi)部可以使用List的get()方法獲取元素,下標(biāo)索引為i。
下面是使用了泛型String:
List<String> list = new ArrayList<String>();
list.add("first");
list.add("second");
list.add("third");
for(int i=0; i < list.size(); i++) {
String element = list.get(i);
}
注意此時(shí)For循環(huán)內(nèi)部的變量是String,因?yàn)長ist的泛型是String,List中只能包含String對象,因此編譯后get()方法返回的是String類型,不需要強(qiáng)制轉(zhuǎn)換。
使用Stream API迭代List
第四種方式是是由Java Stream API迭代List,為了迭代List,需要從List中獲取Stream ,可以通過List的 stream()方法獲取,下面是代碼:
List<String> stringList = new ArrayList<String>();
stringList.add("abc");
stringList.add("def");
Stream<String> stream = stringList.stream();
最后一行代碼調(diào)用了List的 stream() 方法,從List中獲取了Stream,一旦獲取到了Stream,就可以調(diào)用Stream的forEach()方法迭代,下面是例子:
List<String> stringList = new ArrayList<String>();
stringList.add("one");
stringList.add("two");
stringList.add("three");
Stream<String> stream = stringList.stream();
stream
.forEach( element -> { System.out.println(element); });
調(diào)用forEach()方法,將迭代Stream 內(nèi)部的所有元素,Consumer 為流中的每個(gè)元素調(diào)用作為參數(shù)傳遞給forEach()方法的使用者,更多的Stream內(nèi)容后續(xù)文章會講解,或者參考Java Stream API Tutorial.

以上,便是今天的分享,希望大家喜歡,覺得內(nèi)容不錯(cuò)的,歡迎「分享」「贊」或者點(diǎn)擊「在看」支持,謝謝各位。
