上一章節主要是基本數據類型的使用Java、Kotlin、Flutter、HarmonyOS基本類型,這一章節總結集合類的使用以及對比。
一. Java 數據類型
1.1 String
String
是一個類,用于表示字符串。
String message = "Hello, String!";
問題:String 為什么是不可變的?
由于效率和安全性問題 String 被設計為不可變的。不可變就是第二次給一個 String 變量賦值的時候,不是在原內存地址上修改數據,而是重新指向一個新對象,新地址。
從效率角度看:
- 從內存角度看
字符串常量池的要求:創建字符串時,如果該字符串已經存在于池中,則將返回現有字符串的引用,而不是創建新對象。多個String變量引用指向同一個內存地址。如果字符串是可變的,用一個引用更改字符串將導致其他引用的值錯誤。這是很危險的。
- 緩存 hashCode
字符串的Hashcode在java中經常配合基于散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable。不可變的特性保證了hashcode 永遠是相同的。不用每次使用hashcode就需要計算hashcode。這樣更有效率。因為當向集合中插入對象時,是通過hashcode判別在集合中是否已經存在該對象了(不是通過equals方法逐個比較,效率低)。
- 方便其它類使用
其他類的設計基于String不可變,如set存儲String,改變該string后set包含了重復值。
安全性:
String被廣泛用作許多java類的參數,例如網絡連接、打開文件等。如果對String的某一處改變一不小心就影響了該變量所有引用的表現,則連接或文件將被更改,這可能導致嚴重的安全威脅。 不可變對象不能被寫,所以不可變對象自然是線程安全的,因為不可變對象不能更改,它們可以在多個線程之間自由共享。
1.2 集合
集合(Collections)是一組用于存儲和操作一組對象的數據結構。Java 提供了一系列集合類來處理不同類型的數據集合。常見的集合類位于 java.util
包下,其中最常用的集合類有以下幾種:
List 接口:
-
ArrayList
:線程不安全 ,基于數組實現的動態數組,支持快速隨機訪問和增刪操作。 -
LinkedList
:線程不安全,基于鏈表實現的雙向鏈表,適合頻繁插入、刪除操作。 -
Vector
:線程安全,類似于 ArrayList,但性能相對較差,較少被使用。 -
CopyOnWriteArrayList
:線程安全,與普通的ArrayList
不同,CopyOnWriteArrayList
內部采用了一種稱為 "Copy-On-Write"(寫時復制)的機制來實現線程安全。適用于讀多寫少的場景,如果寫操作非常頻繁,則性能可能不佳。
Set 接口:
-
HashSet
:線程不安全,基于哈希表實現的集合,不保證元素順序,不允許重復元素。 -
LinkedHashSet
:線程不安全,繼承自 HashSet 類,它按照元素插入的順序維護元素順序。內部使用鏈表維護元素的順序。 -
TreeSet
:線程不安全,基于紅黑樹實現的有序集合,保證元素有序且不重復。
Map 接口:
-
HashMap
:線程不安全,基于哈希表實現的鍵值對集合,提供快速的查找性能。不保證順序,允許鍵和值為null
-
LinkedHashMap
:線程不安全,基于哈希表和雙向鏈表的實現。它保留了插入順序,也可以選擇按照訪問順序進行排序。LinkedHashMap
允許null鍵和null值。 -
HashTable
:線程安全 -
ConcurrentHashMap
:線程安全的HashMap
實現。它采用分段鎖機制,使得多個線程可以并發地讀取和修改ConcurrentHashMap
,而不需要同步整個map。它不保證順序,允許null鍵和null值。 -
TreeMap
:線程不安全,基于紅黑樹的實現,按照鍵的自然順序或者自定義順序進行排序。因此,它的鍵是有序的。TreeMap
不允許null鍵,但允許null值。
1.2.1 ArrayList
ArrayList
是 java.util
包下的一個動態數組實現的類,它實現了 List
接口,可以根據需要動態增長和縮減大小
特點和功能:
-
動態數組:
ArrayList
內部是一個數組,可以動態增長和縮減大小,根據需要自動擴容。 - 快速隨機訪問:通過索引可以快速隨機訪問元素,時間復雜度為 O(1)。
-
允許重復元素:
ArrayList
允許存儲重復元素。
示例:
// 創建一個 ArrayList
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("Java");
list.add("Kotlin");
list.add("Dart");
String info = list.get(1); // 獲取索引為 1 的元素(返回 "Kotlin")
list.remove("Dart");
list.remove(0); // 刪除索引為 0 的元素
// 遍歷集合
for (String info : list) {
System.out.println(info);
}
面試題1:ArrayList 的擴容機制
ArrayList擴容的本質就是計算出新的擴容數組的size后實例化,并將原有數組內容復制到新數組中去。(不是原數組,而是新數組然后給予數組對象地址)。
默認情況下,新的容量會是原容量的 1.5 倍。 新容量=舊容量右移一位(相當于除于2)+ 舊容量
ArrayList 的底層是用動態數組來實現的。我們初始化一個ArrayList 集合還沒有添加元素時,其實它是個空數組,只有當我們添加第一個元素時,內部會調用擴容方法并返回最小容量10,也就是說ArrayList 初始化容量為10
。 當前數組長度小于最小容量的長度時(前期容量是10,當添加第11個元素時就就擴容),便開始可以擴容了,ArrayList 擴容的真正計算是在一個grow()里面,新數組大小是舊數組的1.5倍,如果擴容后的新數組大小還是小于最小容量,那新數組的大小就是最小容量的大小,后面會調用一個Arrays.copyof方法,這個方法是真正實現擴容的步驟。
1.2.2 LinkedList
LinkedList
是 java.util
包下的一個雙向鏈表實現的類,它實現了 List
和 Deque
接口。與 ArrayList
不同,LinkedList
使用鏈表實現,每個元素都存儲了對前一個和后一個元素的引用,因此在插入和刪除操作時更加高效。
特點和功能:
-
雙向鏈表:
LinkedList
內部使用雙向鏈表實現,支持雙向遍歷。 - 插入和刪除:在鏈表中進行插入和刪除操作效率較高,因為只需要改變節點的指針指向即可,不需要像數組那樣移動大量元素。
-
不支持隨機訪問:
LinkedList
不支持像ArrayList
那樣的快速隨機訪問,獲取指定位置的元素需要遍歷鏈表。
示例:
// 創建一個 LinkedList
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("Java");
list.add("Kotlin");
list.add("Dart");
String info = list.get(1); // 獲取索引為 1 的元素(返回 "Kotlin")
list.remove("Dart");
list.remove(0); // 刪除索引為 0 的元素
// 遍歷集合
for (String info : list) {
System.out.println(info);
}
LinkedList
是 Java 中另一個常用的集合類,使用雙向鏈表實現,適合于對插入和刪除操作較多的場景,但不適合需要頻繁隨機訪問的場景。
面試題2: ArrayList 和 LinkedList 的區別?
ArrayList和LinkedList,前者是Array(動態數組)的數據結構,后者是Link(鏈表)的數據結構,此外他們兩個都是對List接口的實現
當隨機訪問List時(get和set操作),ArrayList和LinkedList的效率更高,因為LinkedList是線性的數據存儲方式,所以需要移動指針從前往后查找。
當對數據進行增刪的操作時(add和remove),LinkedList比ArrayList的效率更高,因為ArrayList是數組,所以在其中進行增刪操作時,會對操作點之后的所有數據的下標索引造成影響,需要進行數據的移動
從利用效率來看,ArrayList自由性較低,因為需要手動的設置固定大小的容量,但是他的使用比較方便,只需要創建,然后添加數據,通過調用下標進行使用,而LinkedList自由性交給,能夠動態的隨數據量的變化而變化,但是它不便于使用。
面試題3: 數組和鏈表的區別
數組:是將元素在內存中連續的存儲的,因為數據是連續存儲的,內存地址連續,所以在查找數據的時候效率比較高,但在存儲之前,需要申請一塊連續的內存空間,并且在編譯的時候就必須確定好他的空間大小。在運行的時候空間的大小是無法隨著需要進行增加和減少的,當數據比較大時,有可能出現越界的情況,當數據比較小時,有可能浪費內存空間,在改變數據個數時,增加、插入、刪除數據效率比較低。
鏈表:是動態申請內存空間的,不需要像數組需要提前申請好內存的大小,鏈表只需在使用的時候申請就可以了,根據需要動態的申請或刪除內存空間,對于數據增加和刪除以及插入比數組靈活,鏈表中數據在內存中可以在任意的位置,通過應用來關聯數據。
1.2.3 Vector
Vector
是 java.util
包下的一個同步(線程安全)的動態數組實現的類,類似于 ArrayList
,但 Vector
的所有方法都是同步的,即保證線程安全,適合在多線程環境下使用。
特點和功能:
-
動態數組:
Vector
內部是一個數組,可以動態增長和縮減大小,根據需要自動擴容。 -
同步性:
Vector
的所有方法都是同步的,保證線程安全,在多線程環境下使用時不需要額外的同步措施(如加鎖)。 -
允許重復元素:
Vector
允許存儲重復元素。
// 創建一個 Vector
Vector<String> vector = new Vector<>();
// 添加元素
vector.add("Java");
vector.add("Kotlin");
vector.add("Dart");
String info = vector.get(1); // 獲取索引為 1 的元素(返回 "Kotlin")
vector.remove("Dart");
vector.remove(0); // 刪除索引為 0 的元素
// 遍歷集合
for (String fruit : vector) {
System.out.println(fruit);
}
Vector
是 Java 中同步的動態數組實現類,在多線程環境下使用較為安全,但由于同步操作可能會降低性能,因此在單線程環境下,通常使用 ArrayList
更為常見。
1.2.4 CopyOnWriteArrayList
CopyOnWriteArrayList
是 Java 中的一個并發集合類,它提供了一種線程安全的 List 實現。與普通的 ArrayList
不同,CopyOnWriteArrayList
內部采用了一種稱為 "Copy-On-Write"(寫時復制)的機制來實現線程安全。
特點和工作原理:
-
線程安全性:
CopyOnWriteArrayList
是線程安全的,支持并發訪問,無需額外的同步措施(如加鎖)。 - 寫時復制:當對集合進行修改(如添加、修改、刪除操作)時,不直接在原始集合上進行操作,而是在修改時創建集合的副本,然后進行修改。這樣可以保證修改操作不影響其他線程并發讀取集合。
-
讀取性能:
CopyOnWriteArrayList
的讀取操作是不加鎖的,因此適合讀多寫少的場景,對讀取性能要求較高的場景。 - 適用場景:適用于對集合進行頻繁讀取,但修改操作較少的情況。
示例:
// 創建一個 CopyOnWriteArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 添加元素
list.add("Java");
list.add("Kotlin");
list.add("Dart");
// 使用迭代器遍歷集合(讀取操作)
for (String info : list) {
System.out.println(info);
}
// 修改操作(寫操作),不會影響迭代器的遍歷
list.add("ArkTs");
list.remove("Dart");
雖然 CopyOnWriteArrayList
提供了一種并發安全的方式來處理集合,但它并不適用于所有場景。由于每次修改都會復制一份集合,因此適用于讀多寫少的場景,如果寫操作非常頻繁,則性能可能不佳。因此,在選擇使用 CopyOnWriteArrayList
時需要根據實際場景和需求來權衡使用。
1.2.5 HashSet
HashSet
是 Set 接口的一個實現類,它基于哈希表實現,不保證元素的順序,并且不允許集合中存在重復元素。
特點和功能:
-
不允許重復元素:
HashSet
不允許包含重復元素,如果向HashSet
中添加重復元素,不會產生錯誤,但不會將重復的元素添加到集合中。 -
基于哈希表:
HashSet
使用哈希表實現,具有 O(1) 的時間復雜度進行添加、刪除和查找操作。 -
無序性:
HashSet
中的元素沒有特定的順序,不保證元素的存儲順序。
示例:
// 創建一個 HashSet
Set<String> set = new HashSet<>();
// 添加元素
set.add("Java");
set.add("Kotlin");
set.add("Dart");
set.add("Java"); // 添加重復元素,不會被添加到集合中
// 遍歷集合
for (String info : set) {
System.out.println(info);
}
在此示例中,盡管我們添加了兩次 "Java",但由于 HashSet
的特性,重復元素不會被添加到集合中,因此只會輸出一次 "Java"。`HashSet
是常用的集合實現類之一,適合需要高效查找和去重的場景。
1.2.6 LinkedHashSet
LinkedHashSet
是 HashSet
類的一個子類,它是基于哈希表和鏈表實現的集合,同時保留了元素的插入順序。因此,它具備了 HashSet 的快速查找和不允許重復元素的特性,同時又能夠按照元素插入的順序進行迭代遍歷。
特點和功能:
-
不允許重復元素:與
HashSet
類似,LinkedHashSet
不允許包含重復元素。 -
有序性:
LinkedHashSet
內部使用哈希表和雙向鏈表來維護元素的順序,保留了元素的插入順序。因此,遍歷LinkedHashSet
時可以按照插入順序獲取元素。 -
基于哈希表和鏈表:
LinkedHashSet
采用哈希表來實現集合的查找和刪除等操作,并使用鏈表維護元素的順序。
// 創建一個 LinkedHashSet
Set<String> linkedHashSet = new LinkedHashSet<>();
// 添加元素
linkedHashSet.add("Java");
linkedHashSet.add("Kotlin");
linkedHashSet.add("Dart");
linkedHashSet.add("Java"); // 添加重復元素,不會被添加到集合中
// 遍歷集合
for (String info : linkedHashSet) {
System.out.println(info);
}
在此示例中,盡管添加了兩次 "Java",但由于 LinkedHashSet
的特性,重復元素不會被添加到集合中,因此只會輸出一次 "Java"。同時,遍歷 LinkedHashSet
時會按照元素的插入順序進行迭代。LinkedHashSet
是保持插入順序并具備 HashSet
查找效率的一種集合實現。
1.2.7 TreeSet
TreeSet
是 java.util
包中的一個集合類,它實現了 SortedSet
接口,基于紅黑樹(自平衡二叉搜索樹)實現,能夠保持元素的自然順序或根據自定義的比較器進行排序。
特點和功能:
-
有序性:
TreeSet
保持元素的排序狀態,可以根據元素的自然順序(例如,整數的升序、字符串的字典序等)或者自定義的比較器進行排序。 -
不允許重復元素:
TreeSet
不允許集合中包含重復元素,如果嘗試添加重復的元素,不會產生錯誤,但不會將重復的元素添加到集合中。 -
基于紅黑樹:
TreeSet
使用紅黑樹數據結構來實現集合,這使得插入、刪除和查找等操作具有較好的性能(時間復雜度為O(log n)
)。
示例:
// 創建一個 TreeSet
Set<String> treeSet = new TreeSet<>();
// 添加元素
treeSet.add("Java");
treeSet.add("Kotlin");
treeSet.add("Dart");
treeSet.add("Java"); // 添加重復元素,不會被添加到集合中
// 遍歷集合
for (String info : treeSet) {
System.out.println(info);
}
在此示例中,盡管添加了兩次 "Java",但由于 TreeSet
的特性,重復元素不會被添加到集合中,因此只會輸出一次 "Java"。另外,TreeSet
會按照元素的自然順序(字典序)進行排序,默認是升序
。
1.2.8 HashMap
HashMap
是 Java 中用于存儲鍵值對的數據結構,它實現了 Map
接口,允許鍵和值都為 null
,并且不保證順序。
特點和功能:
-
HashMap
不是線程安全的,如果需要在多線程環境中使用,可以考慮使用ConcurrentHashMap
。 -
HashMap
的鍵和值可以為null
,但需要注意空指針異常的風險。 -
HashMap
不保證元素的順序,如果需要有序的鍵值對集合,可以考慮使用LinkedHashMap
。
示例:
Map<Integer,String> map = new HashMap<>();
// 向 map 中存放數據
map.put(0,"java");
map.put(1,"kotlin");
map.put(2,"dart");
//檢查鍵 是否存在
boolean containsKey = map.containsKey(1);
//刪除鍵值對
map.remove(1);
// 第一種遍歷:遍歷key
for(int num: map.keySet()){
// map 的 key
int key = num;
//map 的 value
String value = map.get(key);
}
// 第二種遍歷:遍歷 value
for(String info : map.values()){
//map 的 value
String value = info;
}
// 第三種遍歷:遍歷鍵值對
for (Map.Entry<Integer, String> entry : map.entrySet()) {
// map 的 key
int key = entry.getKey();
//map 的 value
String val = entry.getValue();
}
面試題4:HashMap和HashTable的區別
HashMap是基于哈希表實現的,每一個元素是一個key—value對,其內部通過單鏈表解決沖突的問題HashMap是非線程安全的,只適用于單線程的環境下。多線程的環境下可以采用concurrent并發包下的concurrentHashMap,HsahMap實現了serializable接口,支持序列化,實現了cloneable接口,能被克隆。HashMap內部維持了一個存儲數據的Entry數組,HashMap采用鏈表解決沖突,HashMap中的key和value都允許為null,key為null的鍵值對永遠都放在以table[0]為節點的鏈表中。
HashTable同樣是基于哈希表實現的,同樣每個元素是一個key-value對,其內部也是通過單鏈表解決沖突問題,容量不足時,同樣會自動增大,HashTble是線程安全的,能用在多線程的環境下,HashTable實現了serializable接口,它支持序列化,實現了cloneable接口,能被克隆。
HashMap和HashTable之間的區別有以下幾點
繼承的父類不同,hashTable繼承自Dictionary類,而HashMap繼承自AbstractMap類,但二者都實現了Map接口。
線程安全性不同,HashTable中的方法是synchronized的,而HashMap中的方法在缺省的情況下是非ynchronized的,在多線程的環境下,可以直接使用HsahTable,不需要為他的方法實現同步,但使用HashMap時就必須自己增加同步處理。
key和value是否允許為null值:關于其中的key和value都是對象,并且不能包含重復的key,但可以包含重復的value,hashtable中,key和value都不允許出現null值,但在hashmap中,null可以作為鍵,這樣的鍵只有一個,可以有多個鍵對應的值為null.
面試題5:HashMap和HashSet的區別
HashMap:其實現了Map接口,HashMap存儲鍵值對,使用put( )方法將元素放入到Map中,HashMap使用鍵對象來計算hashcode值,HashMap比較快,因為是使用唯一的鍵來獲取對象。
HashSet:實現了Set接口,hashSet僅僅存儲對象,使用add()方法將元素放入到set中,hashset使用成員對象來計算hashcode值,對于兩個對象來說,hashcode可能相同,所以equal方法用來判斷對象的相等性,如果兩個對象不同的話,那么返回false,hashSet較hashMap來說較慢。
1.2.9 LinkedHashMap
LinkedHashMap
是 Java 中的一種哈希表實現的 Map 接口的具體實現類,它繼承自 HashMap
類,除了具有 HashMap
的特性外,還保持了鍵值對的插入順序。這意味著 LinkedHashMap
在遍歷時會按照插入順序返回鍵值對,而不是按照鍵的哈希值的順序返回。
特點和功能:
-
LinkedHashMap
繼承自HashMap
,因此具有與HashMap
相同的性能特性。在大多數情況下,LinkedHashMap
的性能與HashMap
相似。 -
LinkedHashMap
會保持鍵值對的插入順序,因此在遍歷時會按照插入順序返回鍵值對。 -
LinkedHashMap
具有與HashMap
相同的容量和負載因子限制。
// 創建 LinkedHashMap 實例
LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
// 添加鍵值對
linkedHashMap.put("java", 10);
linkedHashMap.put("kotlin", 20);
linkedHashMap.put("dart", 30);
// 獲取值
int value = linkedHashMap.get("java");
// 檢查鍵是否存在
boolean containsKey = linkedHashMap.containsKey("kotlin");
// 刪除鍵值對
linkedHashMap.remove("dart");
// 遍歷鍵值對
for (Map.Entry<String, Integer> entry : linkedHashMap.entrySet()) {
// LinkedHashMap 的 key
String key = entry.getKey();
//LinkedHashMap 的 value
int val = entry.getValue();
}
1.2.10 HashTable
HashTable
是 Java 中用于存儲鍵值對的數據結構,它實現了 Map
接口,類似于 HashMap
,但是線程安全。HashTable
是早期 Java 集合框架中的一部分,在 Java 1.0 中就已經存在,但現在已經不推薦使用,因為它的性能相對較差,并且有一些限制。
特點和功能:
-
HashTable
是線程安全的,但性能相對較差,并且有一些限制。在多線程環境下,你可能更傾向于使用ConcurrentHashMap
。 -
HashTable
不允許鍵或值為null
,否則會拋出NullPointerException
。 -
HashTable
的方法都是同步的,這意味著在進行迭代時,其他線程不能修改集合。
示例:
// 創建 HashTable 實例
Hashtable<Integer, String> hashTable = new Hashtable<>();
// 添加鍵值對
hashTable.put(0, "java");
hashTable.put(1, "kotlin");
hashTable.put(2, "dart");
// 獲取值
String value = hashTable.get(0);
// 檢查鍵是否存在
boolean containsKey = hashTable.containsKey(1);
// 刪除鍵值對
hashTable.remove(2);
// 遍歷鍵值對
Enumeration<Integer> keys = hashTable.keys();
while (keys.hasMoreElements()) {
// hashTable 的 key
int key = keys.nextElement();
// hashTable 的 value
String val = hashTable.get(key);
}
1.2.11 ConcurrentHashMap
ConcurrentHashMap
是 Java 并發包中提供的線程安全的哈希表實現,它是 HashMap
的線程安全版本。與 HashTable
相比,ConcurrentHashMap
提供了更好的并發性能,因為它使用了分段鎖的機制,允許多個線程同時讀取,而寫操作會鎖定特定的段,從而提高了并發讀寫的效率。
特點和功能:
-
ConcurrentHashMap
是線程安全的,并且在大多數情況下具有比HashTable
更好的性能。 -
ConcurrentHashMap
使用分段鎖機制來提高并發讀寫的性能,因此多個線程可以同時讀取,而寫操作會鎖定特定的段,以確保線程安全。 -
ConcurrentHashMap
不允許鍵或值為null
,否則會拋出NullPointerException
。
示例:
Map<Integer,String> map = new ConcurrentHashMap<>();
// 向 map 中存放數據
map.put(0,"java");
map.put(1,"kotlin");
map.put(2,"dart");
//檢查鍵 是否存在
boolean containsKey = map.containsKey(1);
//刪除鍵值對
map.remove(1);
// 第一種遍歷:遍歷key
for(int num: map.keySet()){
// map 的 key
int key = num;
//map 的 value
String value = map.get(key);
}
// 第二種遍歷:遍歷 value
for(String info : map.values()){
//map 的 value
String value = info;
}
// 第三種遍歷:遍歷鍵值對
for (Map.Entry<Integer, String> entry : map.entrySet()) {
// map 的 key
int key = entry.getKey();
//map 的 value
String val = entry.getValue();
}
1.2.12 TreeMap
TreeMap
是 Java 中的一種基于紅黑樹的有序映射表實現,它實現了 NavigableMap
接口,可以保持鍵值對的自然順序或自定義順序。TreeMap
的特點是按鍵的順序進行排序,因此遍歷時可以按照鍵的順序返回鍵值對。
特點和功能:
-
TreeMap
是基于紅黑樹的實現,因此插入、刪除、查找等操作的時間復雜度為 O(log n)。 -
TreeMap
會根據鍵的自然順序或自定義的比較器進行排序。如果鍵的類型實現了Comparable
接口,則按照自然順序排序;如果指定了自定義的比較器,則按照比較器的順序排序。 -
TreeMap
不允許鍵為null
,但允許值為null
。
示例:
Map<Integer,String> map = new TreeMap<>();
// 向 map 中存放數據
map.put(0,"java");
map.put(1,"kotlin");
map.put(2,"dart");
//檢查鍵 是否存在
boolean containsKey = map.containsKey(1);
//刪除鍵值對
map.remove(1);
// 第一種遍歷:遍歷key
for(int num: map.keySet()){
// map 的 key
int key = num;
//map 的 value
String value = map.get(key);
}
// 第二種遍歷:遍歷 value
for(String info : map.values()){
//map 的 value
String value = info;
}
// 第三種遍歷:遍歷鍵值對
for (Map.Entry<Integer, String> entry : map.entrySet()) {
// map 的 key
int key = entry.getKey();
//map 的 value
String val = entry.getValue();
}
二. Kotlin 集合
在 Kotlin
中,集合是一種常用的數據結構,用于存儲多個元素。Kotlin
標準庫提供了豐富的集合類和函數,以支持各種常見的集合操作。
Kotlin 集合類型:
List:
-
List
接口表示一個有序的集合,允許重復元素。 有不可變和可變兩種類型。- 不可變列表:
List<T>
- 可變列表:
MutableList<T>
- 不可變列表:
ArrayList
:是 Kotlin 中的一個可變列表實現,基于數組實現,支持動態增長。它提供了高效的隨機訪問和修改操作。LinkedList
:是 Kotlin 中的雙向鏈表實現,支持快速的插入和刪除操作,但隨機訪問性能較差。
Set:
-
Set
是 Kotlin 中用于表示不重復元素的集合的接口。它不保證元素的順序,但保證不包含重復元素。Set
有不可變和可變兩種類型。- 不可變集合:
Set<T>
- 可變集合:
MutableSet<T>
- 不可變集合:
HashSet
是基于哈希表實現的 Set 集合,它提供了常數時間的平均性能用于添加、刪除和查找操作。由于它是無序的,因此不保證元素的順序。LinkedHashSet
是基于鏈表和哈希表的混合實現的 Set 集合,它保持了元素的插入順序。因此,遍歷LinkedHashSet
時會按照插入的順序返回元素。-
SortedSet
是有序集合的接口,它要求集合中的元素按照某種自然順序或者自定義順序進行排序。SortedSet
有不可變和可變兩種類型。- 不可變集合:
SortedSet<T>
- 可變集合:
MutableSortedSet<T>
- 不可變集合:
Map:
-
Map
是 Kotlin 中用于表示鍵值對的集合的接口。每個鍵都是唯一的,但不保證鍵的順序。Map
有不可變和可變兩種類型。- 不可變映射:
Map<K, V>
- 可變映射:
MutableMap<K, V>
- 不可變映射:
HashMap
是基于哈希表實現的 Map 集合,它提供了常數時間的平均性能用于添加、刪除和查找操作。由于它是無序的,因此不保證鍵值對的順序。LinkedHashMap
是基于鏈表和哈希表的混合實現的 Map 集合,它保持了鍵值對的插入順序。因此,遍歷LinkedHashMap
時會按照插入的順序返回鍵值對。-
SortedMap
是有序映射的接口,它要求映射中的鍵按照某種自然順序或者自定義順序進行排序。SortedMap
有不可變和可變兩種類型。- 不可變映射:
SortedMap<K, V>
- 可變映射:
MutableSortedMap<K, V>
- 不可變映射:
2.1 ArrayList
在 Kotlin 中,ArrayList
是一個可變列表,它實現了 MutableList
接口。它提供了動態數組的功能,可以根據需要動態增長或縮減其大小,并且可以通過索引訪問列表中的元素。ArrayList
在 Kotlin 中常用于存儲和操作一系列元素。
特點和功能:
-
可變性:
ArrayList
是可變的,可以向其中添加、刪除和更新元素。 -
動態大小:
ArrayList
的大小可以根據需要動態增長或縮減,不需要預先指定大小。 -
索引訪問:可以通過索引來訪問
ArrayList
中的元素,索引從 0 開始。 -
迭代器:可以使用迭代器來遍歷
ArrayList
中的元素,也可以使用for
循環進行遍歷。 -
Null 元素:
ArrayList
中可以包含 null 元素。 -
泛型支持:
ArrayList
支持泛型,因此可以指定存儲的元素類型。如果不指定元素類型,則默認為Any?
,即可以存儲任意類型的元素。
示例:
// 創建一個空的 ArrayList
val arrayList = ArrayList<String>()
// 添加元素到 ArrayList
arrayList.add("Java")
arrayList.add("Kotlin")
arrayList.add("Dart")
// 訪問元素
println("第一個元素: ${arrayList[0]}")
// 更新元素
arrayList[1] = "ArkTs"
// 刪除元素
arrayList.remove("Dart")
// 遍歷 ArrayList
for (item in arrayList) {
println(item)
}
2.2 LinkedList
在 Kotlin 中,LinkedList
是一個可變的鏈表實現,它實現了 MutableList
接口。與 ArrayList
不同,LinkedList
的內部實現是基于雙向鏈表的,這意味著它具有一些與鏈表相關的特性,比如插入和刪除元素的性能較高,但隨機訪問的性能較差。
特點和功能:
-
可變性:
LinkedList
是可變的,可以向其中添加、刪除和更新元素。 -
基于鏈表的實現:
LinkedList
的內部實現是基于雙向鏈表的,這意味著插入和刪除元素的性能較好,時間復雜度為 O(1),而隨機訪問的性能較差,時間復雜度為 O(n)。 -
迭代器:可以使用迭代器來遍歷
LinkedList
中的元素,也可以使用for
循環進行遍歷。 -
Null 元素:
LinkedList
中可以包含 null 元素。 -
泛型支持:
LinkedList
支持泛型,因此可以指定存儲的元素類型。如果不指定元素類型,則默認為Any?
,即可以存儲任意類型的元素。
示例:
// 創建一個空的 LinkedList
val linkedList = LinkedList<String>()
// 添加元素到 LinkedList
linkedList.add("Java")
linkedList.add("Kotlin")
linkedList.add("Dart")
// 訪問元素
println("第一個元素:${linkedList.first}")
// 更新元素
linkedList[1] = "ArkTs"
// 刪除元素
linkedList.remove("Dart")
// 遍歷 LinkedList
for (item in linkedList) {
println(item)
}
2.3 HashSet
在 Kotlin 中,HashSet
是一個可變集合,它實現了 MutableSet
接口。HashSet
基于哈希表實現,它不允許包含重復元素,并且不保證元素的順序,即元素的插入順序不會被保留。
特點和功能:
-
可變性:
HashSet
是可變的,可以向其中添加、刪除和更新元素。 -
哈希表實現:
HashSet
的內部實現是基于哈希表的,這使得元素的查找、添加和刪除等操作具有常數時間復雜度,即 O(1) 的時間復雜度(平均情況下)。 -
不允許重復元素:
HashSet
不允許包含重復的元素,如果嘗試添加重復的元素,那么HashSet
不會進行任何操作。 -
迭代器:可以使用迭代器來遍歷
HashSet
中的元素,也可以使用for
循環進行遍歷。由于HashSet
不保證元素的順序,因此遍歷的順序可能是不確定的。 -
Null 元素:
HashSet
中可以包含 null 元素,但只能包含一個。
示例:
// 創建一個空的 HashSet
val hashSet = HashSet<String>()
// 添加元素到 HashSet
hashSet.add("Java")
hashSet.add("Kotlin")
hashSet.add("Dart")
hashSet.add("Kotlin") // 嘗試添加重復元素,不會進行任何操作
// 刪除元素
hashSet.remove("Dart")
// 遍歷 HashSet
for (item in hashSet) {
println(item)
}
2.4 LinkedHashSet
在 Kotlin 中,LinkedHashSet
是 java.util.LinkedHashSet
的 Kotlin 接口,它實現了 MutableSet
接口。LinkedHashSet
繼承自 HashSet
,但與 HashSet
不同的是,它保留了元素的插入順序。這意味著當遍歷 LinkedHashSet
時,元素的順序與它們被插入的順序相同。
特點和功能:
-
可變性:
LinkedHashSet
是可變的,可以向其中添加、刪除和更新元素。 -
插入順序:
LinkedHashSet
保留了元素的插入順序,即遍歷LinkedHashSet
時,元素的順序與它們被插入的順序相同。 -
基于鏈表的實現:
LinkedHashSet
的內部實現是基于鏈表和哈希表的,它使用哈希表來實現元素的快速查找,并使用鏈表來維護元素的插入順序。 -
迭代器:可以使用迭代器來遍歷
LinkedHashSet
中的元素,也可以使用for
循環進行遍歷。由于LinkedHashSet
保留了插入順序,因此遍歷時元素的順序與它們被插入的順序相同。 -
Null 元素:
LinkedHashSet
中可以包含 null 元素,但只能包含一個。
示例:
// 創建一個空的 LinkedHashSet
val linkedHashSet = LinkedHashSet<String>()
// 添加元素到 LinkedHashSet
linkedHashSet.add("Java")
linkedHashSet.add("Kotlin")
linkedHashSet.add("Dart")
// 刪除元素
linkedHashSet.remove("Kotlin")
// 遍歷 LinkedHashSet
for (item in linkedHashSet) {
println(item)
}
2.5 HashMap
在 Kotlin 中,HashMap
是一個可變的哈希表實現,它實現了 MutableMap
接口。HashMap
使用哈希表來存儲鍵值對,并且不保證鍵值對的順序,即鍵值對的插入順序不會被保留。
特點和功能:
-
可變性:
HashMap
是可變的,可以向其中添加、刪除和更新鍵值對。 -
哈希表實現:
HashMap
的內部實現是基于哈希表的,這使得鍵值對的查找、添加和刪除等操作具有常數時間復雜度,即 O(1) 的時間復雜度(平均情況下)。 -
不保證順序:
HashMap
不保證鍵值對的順序,即鍵值對的插入順序不會被保留。如果需要保留插入順序,可以使用LinkedHashMap
。 -
迭代器:可以使用迭代器來遍歷
HashMap
中的鍵值對,也可以使用for
循環進行遍歷。由于HashMap
不保證順序,因此遍歷時鍵值對的順序可能是不確定的。 -
Null 鍵和值:
HashMap
中可以包含 null 鍵和值,但只能包含一個 null 鍵和多個 null 值。
示例:
// 創建一個空的 HashMap
val hashMap = HashMap<String, Int>()
// 添加鍵值對到 HashMap
hashMap["Java"] = 10
hashMap["Kotlin"] = 20
hashMap["Dart"] = 30
// 訪問鍵值對
println("'Java': ${hashMap["Java"]}")
// 更新鍵值對
hashMap["Kotlin"] = 25
// 刪除鍵值對
hashMap.remove("Dart")
// 遍歷 HashMap
for ((key, value) in hashMap) {
println("$key -> $value")
}
2.6 LinkedHashMap
在 Kotlin 中,LinkedHashMap
是 java.util.LinkedHashMap
的 Kotlin 接口,它實現了 MutableMap
接口。LinkedHashMap
繼承自 HashMap
,但與 HashMap
不同的是,LinkedHashMap
保留了鍵值對的插入順序。這意味著當遍歷 LinkedHashMap
時,鍵值對的順序與它們被插入的順序相同。
特點和功能:
-
可變性:
LinkedHashMap
是可變的,可以向其中添加、刪除和更新鍵值對。 -
保留插入順序:
LinkedHashMap
保留了鍵值對的插入順序,即遍歷LinkedHashMap
時,鍵值對的順序與它們被插入的順序相同。 -
基于鏈表的實現:
LinkedHashMap
的內部實現是基于鏈表和哈希表的,它使用哈希表來實現鍵值對的快速查找,并使用鏈表來維護鍵值對的插入順序。 -
迭代器:可以使用迭代器來遍歷
LinkedHashMap
中的鍵值對,也可以使用for
循環進行遍歷。由于LinkedHashMap
保留了插入順序,因此遍歷時鍵值對的順序與它們被插入的順序相同。 -
Null 鍵和值:
LinkedHashMap
中可以包含 null 鍵和值,但只能包含一個 null 鍵和多個 null 值。
示例:
// 創建一個空的 LinkedHashMap
val linkedHashMap = LinkedHashMap<String, Int>()
// 添加鍵值對到 LinkedHashMap
linkedHashMap["Java"] = 10
linkedHashMap["Kotlin"] = 20
linkedHashMap["Dart"] = 30
// 訪問鍵值對
println(" 'Java': ${linkedHashMap["Java"]}")
// 更新鍵值對
linkedHashMap["Kotlin"] = 25
// 刪除鍵值對
linkedHashMap.remove("Dart")
// 遍歷 LinkedHashMap
for ((key, value) in linkedHashMap) {
println("$key -> $value")
}
三. Dart 集合
在 Dart
編程語言中,有幾種主要的集合類型,包括列表(List
)、集(Set
)和映射(Map
)。以下是它們的簡要介紹:
List
:列表是有序的集合,可以包含重復的元素。列表中的元素可以通過索引訪問,索引從 0 開始。
Set
:Set 是無序的集合,不允許包含重復的元素;
Map
:以鍵值對的形式存儲元素,鍵(key)是唯一的
3.1 List
List
是一種有序的集合,可以包含重復的元素。
var list = ["java", "kotlin", "dart"];
//訪問list,輸出 java
print(list[0]);
//修改第一個元素為 Hello Java
list[0] = "Hello Java";
//向list 尾部添加元素 arkTs
list.add("arkTs");
// 第一種方式遍歷: 使用 for 循環遍歷
for (var i = 0; i < list.length; i++) {
print(list[i]);
}
// 第二種方式遍歷: 使用 for .. in 遍歷
for (var x in list) {
print(x);
}
// 第三種遍歷方式:使用 forEach 遍歷
list.forEach((element) {
print(element);
});
3.2 Set
Set
是一種無序的集合,它不允許包含重復的元素。Set
提供了高效的查找和插入操作。Set
集合中允許 null
存在,但是一個Set
中只能包含一個null
.
Set set = Set();
//添加單個元素
set.add("java");
//添加一些元素
set.addAll(["kotlin", "dart", "arkTs"]);
//判斷集合中是否包含指定的元素
set.contains("dart");
//判斷集合中是否包含一些元素
set.containsAll(["java", "dart"]);
//第一種方式遍歷: 使用 for .. in 遍歷
for (var info in set) {
print(info);
}
//第二種遍歷方式:使用 forEach 遍歷
set.forEach((element) {
print(element);
});
3.3 Map
Map
是一種鍵值對的集合,其中每個鍵對應一個值。Map
是無序的,但是它允許您根據鍵來查找和訪問相應的值。
var mMap = {"name": "Bob", "age": 18};
//獲取 Map 中的值,輸出結果: Bob
print(mMap["name"]);
//從 Map 中移除鍵值對
mMap.remove('age');
//檢查 Map 中是否包含某個鍵 ,輸出結果 :false
print(mMap.containsKey('city'));
//使用 forEach 方法遍歷 Map 中的鍵值對:
mMap.forEach((key, value) {
print("$key:$value");
});
四. ArkTs集合
4.1 數組Arraay
在 TypeScript 中,數組(Array)是一種數據結構,用于存儲多個元素。與 JavaScript 類似,TypeScript 的數組可以包含任意類型的元素,且可以動態增加或刪除元素。
特點和功能:
-
TypeScript
的數組支持各種方法來進行操作,如push()
、pop()
、shift()
、unshift()
、splice()
等,用于在數組中添加、刪除或替換元素,以及其他各種操作。 -
遍歷數組: 可以使用
for
循環、forEach()
方法、for...of
循環等方式遍歷數組中的元素。 -
泛型數組:
TypeScript
支持泛型,因此可以創建泛型數組,例如Array<number>
表示元素類型為數字的數組。
示例:
// 聲明一個字符類型的數組
let string: string[] = ["Java", "Kotlin", "Dart"];
// 聲明一個數字類型的數組
let numbers: number[] = [1, 2, 3, 4, 5];
// 添加字符到數組
string.push("TypeScript");
// 添加元素到數組
numbers.push(6);
// 刪除數組中的字符
string.pop();
// 刪除數組中的元素
numbers.pop();
// 遍歷數組
for (let i = 0; i < string.length; i++) {
console.log(string[i]);
}
// 使用 forEach 方法遍歷數
string.forEach(character => {
console.log(character);
});
// 使用 for...of 遍歷數組
for (let character of string) {
console.log(character);
}
4.2 Tuple
在 TypeScript 中,元組(Tuple)是一種有序的、固定長度的數組,它可以包含不同類型的元素。與普通數組不同,元組可以指定每個位置的元素類型,并且元組中的元素數量是固定的。
特點和功能:
- 元組的使用場景: 元組通常用于表示固定數量和類型的數據集合,例如表示坐標、表示日期、表示返回多個值的函數等場景。
- 訪問越界元素: TypeScript 允許訪問元組中的越界元素,但需要謹慎使用,因為這可能會導致運行時錯誤。
示例:
// 聲明一個元組并初始化
let bookInfo: [number, string,number] = [1, "Java",55];
// 訪問元組中的元素
console.log("bookInfo ID:", bookInfo[0]); // 輸出 1
console.log("bookInfo Name:", bookInfo[1]); // 輸出 "Java"
console.log("bookInfo Price:", bookInfo[2]); // 輸出 55
// 使用解構獲取元組中的元素
let [id, name,price] = bookInfo;
console.log("bookInfo ID:", id); // 輸出 1
console.log("bookInfo Name:", name); // 輸出 "Java"
console.log("bookInfo Price:", price); // 輸出 55
4.3 Set
在 TypeScript
中,可以使用原生的 JavaScript
Set 類型來表示集合。Set 是一種集合數據結構,它存儲一組唯一的值,且不允許重復。與數組不同,Set 中的元素沒有順序。
特點和功能:
-
Set 的操作方法:
-
add(value: T)
: 向 Set 中添加一個值。 -
delete(value: T)
: 從 Set 中刪除一個值。 -
has(value: T)
: 檢查 Set 中是否存在某個值。 -
clear()
: 清空 Set 中的所有值。
-
-
Set 的遍歷:
- 可以使用
for...of
循環遍歷 Set 中的元素。 - 可以使用
forEach()
方法遍歷 Set 中的元素。
- 可以使用
Set 中的元素唯一性: Set 中的元素是唯一的,不允許重復。當向 Set 中添加重復的元素時,Set 不會添加重復的值。
示例:
// 聲明并初始化一個 Set
let infoSet: Set<string> = new Set(["Java", "Kotlin", "Dart"]);
// 添加元素到 Set
infoSet.add("TypeScript");
// 刪除 Set 中的元素
infoSet.delete("Dart");
// 檢查 Set 中是否存在某個值
console.log("是否存在:", infoSet.has("Dart")); // 輸出 true
// 遍歷 Set
for (let num of infoSet) {
console.log(num);
}
// 使用 forEach 方法遍歷 Set
infoSet.forEach(element => {
console.log(element);
});
4.4 Map
在 TypeScript 中,可以使用原生的 JavaScript Map 類型來表示映射或字典數據結構。Map 是一種鍵值對的集合,其中的鍵是唯一的,而值可以重復。
特點和功能:
-
Map 的操作方法:
-
set(key: K, value: V)
: 向 Map 中添加一個鍵值對。 -
get(key: K)
: 獲取指定鍵對應的值。 -
has(key: K)
: 檢查 Map 中是否存在指定的鍵。 -
delete(key: K)
: 刪除指定鍵對應的鍵值對。 -
clear()
: 清空 Map 中的所有鍵值對。 -
size
: 返回 Map 中鍵值對的數量。
-
-
Map 的遍歷:
- 可以使用
for...of
循環遍歷 Map 中的鍵值對。 - 可以使用
forEach()
方法遍歷 Map 中的鍵值對。
- 可以使用
Map 中的鍵的唯一性: Map 中的鍵是唯一的,如果向 Map 中添加已存在的鍵,它會更新對應的值。
示例:
// 聲明并初始化一個 Map
let infoMaps: Map<number, string> = new Map([
[0, "Java"],
[10, "Kotlin"],
[1, "Dart"]
]);
// 添加鍵值對到 Map
infoMaps.set(2, "TypeScript");s
// 獲取 Map 中的值
console.log("獲取 Map 中的值:", infoMaps.get(0));
// 檢查 Map 中是否存在某個鍵
console.log("檢查 Map 中是否存在某個鍵:", infoMaps.has(1)); // 輸出 true
// 刪除 Map 中的鍵值對
infoMaps.delete(10);
// 遍歷 Map
for (let [num, name] of infoMaps) {
console.log(`${num} -> ${name}`);
}
// 使用 forEach 方法遍歷 Map
infoMaps.forEach((value, key) => {
console.log(`${key} -> ${value}`);
});