Java 集合 -- List
Java Collection 簡介
Java標準庫自帶的java.util包提供了集合類:Collection,它是除Map外所有其他集合類的根介面。
Java的java.util包主要提供了以下三種類型的集合:
-
List:一種有序列表的集合,例如,按索引排列的Student的List;
-
Set:一種保證沒有重複元素的集合,例如,所有無重複名稱的Student的Set;
-
Map:一種通過鍵值(key-value)查詢的對映表集合,例如,根據Student的name查詢對應Student的Map。
Java Collection 介面的繼承關係如下圖:
Java集合的設計有幾個特點:
-
一是實現了介面和實現類相分離,例如,有序表的介面是List,具體的實現類有ArrayList,LinkedList等,
-
二是支援泛型,我們可以限制在一個集合中只能放入同一種資料型別的元素,例如:
List<String> list = new ArrayList<>(); // 只能放入String型別
最後,Java訪問集合總是通過統一的方式——迭代器(Iterator)來實現,它最明顯的好處在於無需知道集合內部元素是按什麼方式儲存的。
部分遺留的集合類(不建議使用):
-
Hashtable:一種執行緒安全的Map實現;
-
Vector:一種執行緒安全的List實現;
-
Stack:基於Vector實現的LIFO的棧。
還有一小部分介面是遺留介面,也不應該繼續使用:
- Enumeration
:已被Iterator 取代。
使用 List
在集合類中,List是最基礎的一種集合:它是一種有序列表。List的索引和陣列一樣,從0開始。
List 的實現類 ArrayList
在實際應用中,需要增刪元素的有序列表,我們使用最多的是ArrayList。實際上,ArrayList在內部使用了陣列來儲存所有元素。
ArrayList把新增和刪除的操作封裝起來,讓我們操作List類似於運算元組,卻不用關心內部元素如何移動。
List 的實現類 LinkedLsit
LinkedList通過“連結串列”也實現了List介面。在LinkedList中,它的內部每個元素都指向下一個元素.
比較一下ArrayList和LinkedList:
通常情況下,我們總是優先使用ArrayList。
List 的特點
List介面允許我們新增重複的元素,即List內部的元素可以重複:
public class ListMain {
public static void main(String[] args){
// 例項化一個 ArrayList 列表,命名為 list
// 其中只能存放String 型別元素
List<String> list = new ArrayList<>();
list.add("apple"); // 使用 add()方法新增元素
list.add("pear");
list.add("apple");// 允許重複新增元素
System.out.println(list.size()); // 使用size()方法獲取列表長度
System.out.println(list);
}
}
List 允許新增 null:
public class ListMain {
public static void main(String[] args){
// 例項化一個 ArrayList 列表,命名為 list
// 其中只能存放String 型別元素
List<String> list = new ArrayList<>();
list.add("apple"); // 使用 add()方法新增元素
list.add(null);
list.add("apple");// 允許重複新增元素
String second = list.get(1); // 獲取列表元素,下標從0開始
System.out.println(second);
System.out.println(list);
}
}
建立 List
除了使用ArrayList和LinkedList,我們還可以通過List介面提供的of()方法,根據給定元素快速建立List:
List<Integer> list = List.of(1, 2, 5);
但是List.of()方法不接受null值,如果傳入null,會丟擲NullPointerException異常。
注意:只有JDK9及以上,才支援 of()方法快速建立List,JDK8不支援。
遍歷 List
可以用for迴圈根據索引配合get()方法遍歷:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
for (int i=0; i<list.size(); i++){
String s = list.get(i);
System.out.println(s);
}
}
}
因為get(int)方法只有ArrayList的實現是高效的,換成LinkedList後,索引越大,訪問速度越慢。
所以要始終堅持使用迭代器Iterator來訪問List。
Iterator本身也是一個物件,但它是由List的例項呼叫iterator()方法的時候建立的。
Iterator<String> it = list.iterator()
Iterator物件知道如何遍歷一個List,並且不同的List型別,返回的Iterator物件實現也是不同的,但總是具有最高的訪問效率。
Iterator物件有兩個方法:
-
boolean hasNext()判斷是否有下一個元素,
-
next()返回下一個元素。
因此,使用Iterator遍歷List程式碼如下:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
Iterator<String> iterator = list.iterator();
for (;iterator.hasNext();){
String s = iterator.next();
System.out.println(s);
}
}
}
記住,通過Iterator遍歷List永遠是最高效的方式。
並且,由於Iterator遍歷是如此常用,所以,Java的增強for迴圈本身就可以幫我們使用Iterator遍歷。
把上面的程式碼再改寫如下:
public class ListMain {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("apple");
list.add("null");
list.add("apple");
// Iterator<String> iterator = list.iterator();
for (String s:list){ // 增強for迴圈
// String s = iterator.next();
System.out.println(s);
}
}
}
上述程式碼就是我們編寫遍歷List的常見程式碼。
實際上,只要實現了Iterable介面的集合類都可以直接用增強for迴圈來遍歷.
Java編譯器本身並不知道如何遍歷集合物件,但它會自動把增強for迴圈變成Iterator的呼叫,原因就在於Iterable介面定義了一個Iterator
List 與 Array 的轉換
array 表示陣列。
把List變為Array有三種方法,
-
第一種是呼叫toArray()方法直接返回一個Object[]陣列
-
第二種方式是給toArray(T[])傳入一個型別相同的Array,List內部自動把元素複製到傳入的Array中
-
最後一種更簡潔的寫法是通過List介面定義的T[] toArray(IntFunction<T[]> generator)方法
每天學習一點點,每天進步一點點。