Java、Kotlin、Flutter、HarmonyOS集合數據類型

上一章節主要是基本數據類型的使用Java、Kotlin、Flutter、HarmonyOS基本類型,這一章節總結集合類的使用以及對比。

一. Java 數據類型

1.1 String

String 是一個類,用于表示字符串。

String message = "Hello, String!";

問題:String 為什么是不可變的?

由于效率和安全性問題 String 被設計為不可變的。不可變就是第二次給一個 String 變量賦值的時候,不是在原內存地址上修改數據,而是重新指向一個新對象,新地址。

從效率角度看:

  1. 從內存角度看

字符串常量池的要求:創建字符串時,如果該字符串已經存在于池中,則將返回現有字符串的引用,而不是創建新對象。多個String變量引用指向同一個內存地址。如果字符串是可變的,用一個引用更改字符串將導致其他引用的值錯誤。這是很危險的。

  1. 緩存 hashCode

字符串的Hashcode在java中經常配合基于散列的集合一起正常運行,這樣的散列集合包括HashSet、HashMap以及HashTable。不可變的特性保證了hashcode 永遠是相同的。不用每次使用hashcode就需要計算hashcode。這樣更有效率。因為當向集合中插入對象時,是通過hashcode判別在集合中是否已經存在該對象了(不是通過equals方法逐個比較,效率低)。

  1. 方便其它類使用

其他類的設計基于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

ArrayListjava.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

LinkedListjava.util 包下的一個雙向鏈表實現的類,它實現了 ListDeque 接口。與 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

Vectorjava.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

LinkedHashSetHashSet 類的一個子類,它是基于哈希表和鏈表實現的集合,同時保留了元素的插入順序。因此,它具備了 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

TreeSetjava.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 中常用于存儲和操作一系列元素。

特點和功能:

  1. 可變性ArrayList 是可變的,可以向其中添加、刪除和更新元素。
  2. 動態大小ArrayList 的大小可以根據需要動態增長或縮減,不需要預先指定大小。
  3. 索引訪問:可以通過索引來訪問 ArrayList 中的元素,索引從 0 開始。
  4. 迭代器:可以使用迭代器來遍歷 ArrayList 中的元素,也可以使用 for 循環進行遍歷。
  5. Null 元素ArrayList 中可以包含 null 元素。
  6. 泛型支持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 的內部實現是基于雙向鏈表的,這意味著它具有一些與鏈表相關的特性,比如插入和刪除元素的性能較高,但隨機訪問的性能較差。

特點和功能:

  1. 可變性LinkedList 是可變的,可以向其中添加、刪除和更新元素。
  2. 基于鏈表的實現LinkedList 的內部實現是基于雙向鏈表的,這意味著插入和刪除元素的性能較好,時間復雜度為 O(1),而隨機訪問的性能較差,時間復雜度為 O(n)。
  3. 迭代器:可以使用迭代器來遍歷 LinkedList 中的元素,也可以使用 for 循環進行遍歷。
  4. Null 元素LinkedList 中可以包含 null 元素。
  5. 泛型支持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 基于哈希表實現,它不允許包含重復元素,并且不保證元素的順序,即元素的插入順序不會被保留。

特點和功能:

  1. 可變性HashSet 是可變的,可以向其中添加、刪除和更新元素。
  2. 哈希表實現HashSet 的內部實現是基于哈希表的,這使得元素的查找、添加和刪除等操作具有常數時間復雜度,即 O(1) 的時間復雜度(平均情況下)。
  3. 不允許重復元素HashSet 不允許包含重復的元素,如果嘗試添加重復的元素,那么 HashSet 不會進行任何操作。
  4. 迭代器:可以使用迭代器來遍歷 HashSet 中的元素,也可以使用 for 循環進行遍歷。由于 HashSet 不保證元素的順序,因此遍歷的順序可能是不確定的。
  5. 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 中,LinkedHashSetjava.util.LinkedHashSet 的 Kotlin 接口,它實現了 MutableSet 接口。LinkedHashSet 繼承自 HashSet,但與 HashSet 不同的是,它保留了元素的插入順序。這意味著當遍歷 LinkedHashSet 時,元素的順序與它們被插入的順序相同。

特點和功能:

  1. 可變性LinkedHashSet 是可變的,可以向其中添加、刪除和更新元素。
  2. 插入順序LinkedHashSet 保留了元素的插入順序,即遍歷 LinkedHashSet 時,元素的順序與它們被插入的順序相同。
  3. 基于鏈表的實現LinkedHashSet 的內部實現是基于鏈表和哈希表的,它使用哈希表來實現元素的快速查找,并使用鏈表來維護元素的插入順序。
  4. 迭代器:可以使用迭代器來遍歷 LinkedHashSet 中的元素,也可以使用 for 循環進行遍歷。由于 LinkedHashSet 保留了插入順序,因此遍歷時元素的順序與它們被插入的順序相同。
  5. 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 使用哈希表來存儲鍵值對,并且不保證鍵值對的順序,即鍵值對的插入順序不會被保留。

特點和功能:

  1. 可變性HashMap 是可變的,可以向其中添加、刪除和更新鍵值對。
  2. 哈希表實現HashMap 的內部實現是基于哈希表的,這使得鍵值對的查找、添加和刪除等操作具有常數時間復雜度,即 O(1) 的時間復雜度(平均情況下)。
  3. 不保證順序HashMap 不保證鍵值對的順序,即鍵值對的插入順序不會被保留。如果需要保留插入順序,可以使用 LinkedHashMap
  4. 迭代器:可以使用迭代器來遍歷 HashMap 中的鍵值對,也可以使用 for 循環進行遍歷。由于 HashMap 不保證順序,因此遍歷時鍵值對的順序可能是不確定的。
  5. 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 中,LinkedHashMapjava.util.LinkedHashMap 的 Kotlin 接口,它實現了 MutableMap 接口。LinkedHashMap 繼承自 HashMap,但與 HashMap 不同的是,LinkedHashMap 保留了鍵值對的插入順序。這意味著當遍歷 LinkedHashMap 時,鍵值對的順序與它們被插入的順序相同。

特點和功能:

  1. 可變性LinkedHashMap 是可變的,可以向其中添加、刪除和更新鍵值對。
  2. 保留插入順序LinkedHashMap 保留了鍵值對的插入順序,即遍歷 LinkedHashMap 時,鍵值對的順序與它們被插入的順序相同。
  3. 基于鏈表的實現LinkedHashMap 的內部實現是基于鏈表和哈希表的,它使用哈希表來實現鍵值對的快速查找,并使用鏈表來維護鍵值對的插入順序。
  4. 迭代器:可以使用迭代器來遍歷 LinkedHashMap 中的鍵值對,也可以使用 for 循環進行遍歷。由于 LinkedHashMap 保留了插入順序,因此遍歷時鍵值對的順序與它們被插入的順序相同。
  5. 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 的數組可以包含任意類型的元素,且可以動態增加或刪除元素。

特點和功能:

  1. TypeScript 的數組支持各種方法來進行操作,如 push()pop()shift()unshift()splice() 等,用于在數組中添加、刪除或替換元素,以及其他各種操作。
  2. 遍歷數組: 可以使用 for 循環、forEach() 方法、for...of 循環等方式遍歷數組中的元素。
  3. 泛型數組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)是一種有序的、固定長度的數組,它可以包含不同類型的元素。與普通數組不同,元組可以指定每個位置的元素類型,并且元組中的元素數量是固定的。

特點和功能:

  1. 元組的使用場景: 元組通常用于表示固定數量和類型的數據集合,例如表示坐標、表示日期、表示返回多個值的函數等場景。
  2. 訪問越界元素: 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 中的元素沒有順序。

特點和功能:

  1. Set 的操作方法

    • add(value: T): 向 Set 中添加一個值。
    • delete(value: T): 從 Set 中刪除一個值。
    • has(value: T): 檢查 Set 中是否存在某個值。
    • clear(): 清空 Set 中的所有值。
  2. Set 的遍歷

    • 可以使用 for...of 循環遍歷 Set 中的元素。
    • 可以使用 forEach() 方法遍歷 Set 中的元素。
  3. 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 是一種鍵值對的集合,其中的鍵是唯一的,而值可以重復。

特點和功能:

  1. Map 的操作方法

    • set(key: K, value: V): 向 Map 中添加一個鍵值對。
    • get(key: K): 獲取指定鍵對應的值。
    • has(key: K): 檢查 Map 中是否存在指定的鍵。
    • delete(key: K): 刪除指定鍵對應的鍵值對。
    • clear(): 清空 Map 中的所有鍵值對。
    • size: 返回 Map 中鍵值對的數量。
  2. Map 的遍歷

    • 可以使用 for...of 循環遍歷 Map 中的鍵值對。
    • 可以使用 forEach() 方法遍歷 Map 中的鍵值對。
  3. 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}`);
});
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容