集合

參考資料:(https://jax-work-archive.blogspot.com/2015/02/java-setlistmap.html) 參考資料:(https://read01.com/zh-tw/mgRQ8M.html#.XPc9zIgzaUn)

比較Set、List、Map的區別,相對於陣列

陣列

大小固定

同一個陣列只能存放類型一樣的數據 (基本類型/引用類型)

集合

可以存儲、操作數目不固定的一組數據

所有Java 的集合都位於 java.util

Java 集合只能存放引用類型,不能存放基本數據類型(int、double、boolean、btye、short、long、float、char)

Java 集合主要分為三種

Set 集

List 列表

Map 映射

Collection 介面

是最基本的集合介面,聲明適用於 Java 集合的通用方法。 (只適用於 Set、List,因為這兩者都繼承了 Collection。)

Collection 介面的方法:

boolean add(Object o)

void clear()

boolean isEmpty()

boolean contains(Object o)

Iterator iterator() 返回一個 Iterator 物件,可以用來遍歷集合中的元素

boolean remove(Object o)

int size()

Object[] toArray()

關於:Iterator() 和 toArray() 方法都用於集合的所有元素

Iterator 介面中聲明了如下方法:

hasNext() 判斷是否遍歷完畢,如果沒有,return true

next() 返回下一個元素

remove() 從集合中刪除 上一個 next() 方法返回的元素

Set(集)

最簡單的集合,集合中不按照特定的方式排序,而且沒有重複物件。

主要有兩個實現類

HashSet : 按照哈希算法來存取集合中的物件,存取速度比較快

TreeSet 實現了 SortedSet 介面,能夠對集合中的物件進行排序

用法:存放的是物件的引用,沒有重複物件

List(列表)

List 的特徵是其元素以線性方式存儲,集合中可以存放重複物件。

主要的實現類包括

ArrayList : 代表長度可以改變的陣列。可以對元素進行隨機的訪問,對於插入和刪除元素的速度較慢。

LinkedList : 在實現中採用鍊表數據結構。插入和刪除速度快,訪問速度慢。

Map(映射)

Map 是一種把 key value 映射的集合,每一個元素都包含一對 key value。

Map 沒有繼承 Collection 介面,從 Map 中檢索元素時,只要給出 Key,就會返回對應的 value。

Map 的常用方法 :

Object put(Object key, Object value)

Object remove(Object key)

void putAll(Map t)

void clear()

boolean containsKey(Object key)

boolean containsValue(Object value)

int size()

boolean isEmpty()

Object get(Object key)

Map 的 key 不能重複,但是可以多個 key 映射到同一個 value 物件。

List 的功能方法

LinkedList

具有下列方法

addFirst()、addLast()

getFirst()、getLast()

removeFirst()、removeLast()

使其可以當作堆疊、佇列、雙向鏈節使用

Set 的功能方法

HashSet

存入的物件要有 hashCode()。

TreeSet

LinkedHashSet

Map 的功能方法

HashMap

LinkedHashMap

TreeMap

WeakHashMap

IdentifyHashMap

後兩者是為了解決特殊問題而設計的,詳細請看文章。

MultiValueMap

參考資料:(https://www.itread01.com/content/1534333212.html)

這個可以讓一個 key 對應多個 value,可以利用在 Entry 裡面放入 List 或是 用 StringBuffer 去拚

import com.sun.deploy.util.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

public static void main(String[] args) {
    MultiValueMap<String,Object> multiValueMap=new LinkedMultiValueMap<>();


    multiValueMap.add("早班 9:00-11:00", "周一");
    multiValueMap.add("早班 9:00-11:00", "周二");
    multiValueMap.add("中班 13:00-16:00", "周三");
    multiValueMap.add("早班 9:00-11:00", "周四");
    multiValueMap.add("測試1天2次 09:00 - 12:00", "周五");
    multiValueMap.add("測試1天2次 09:00 - 12:00", "周六");
    multiValueMap.add("中班 13:00-16:00", "周日");
    //打印所有值
    Set<String> keySet = multiValueMap.keySet();
    for (String key : keySet) {
        List<Object> values = multiValueMap.get(key);
        System.out.println(StringUtils.join(Arrays.asList(values.toArray())," ")+":"+key);

    }

}
打印結果:
周一 周二 周四:早班 9:00-11:00
周三 周日:中班 13:00-16:00
周五 周六:測試1天2次 09:00 - 12:00

HashMap vs TreeMap

參考資料:(https://www.baeldung.com/java-treemap-vs-hashmap)

Order

HashMap 不保證元素在 Map 中的排列方式 所以我們不能對其 key value 假設任何順序

而 TreeMap 的項目則會根據 自然順序 去進行排序 如果無法按照自然順序,也可以使用 Comparator || Comparable 來定義元素在 Map 中的排列順序

null

HashMap : 可以有一個為 null 的 key,value 則沒有限制,可以有許多 null

TreeMap : 不可以有為 null 的 key,value 則一樣沒有限制。

因為如果 TreeMap 的 key 為 null,因為 compareTo() & compare() 會拋出 NullPointerException。

效率、效能

HashMap :

  1. 大多數的動作都是 O(1),EX:add()、remove()、contains(),因此,它比 TreeMap 快得多。

  2. 在 Hash 表中搜索元素幾乎都是 O(1)

    • 內存開銷 - 會有許多空間沒有用到

    • 性能下降 - 碰撞次數越多,性能越低

如果存在衝突 或 兩個不同的元素 擁有相同的 hash,則將這兩個存在同一個 linked List。 所以最壞的狀況,可能需要該 linked List 的 O(n)

但隨著 JEP 180 的出現,"在 HashMap 中排列元素的方式" 的實現 發生了微妙的變化。

因此,在高 Hash 衝突 的情況下,最壞情況的性能,將從 O(n) 改善到 O(log n)

在創建 HashMap 的物件時,可以通過設置自定義初始容量和加載因子來調整 HashMap 的性能

如果出現以下狀況,我們應該選擇 HashMap :

  1. 我們知道我們大概會保留多少物品在收藏中

  2. 我們不想以自然順序提取項目

TreeMap :

TreeMap 利用元素 the help of a custom Comparator ,去將其存儲在分層樹中。

  • TreeMap 大多數提供 O(log n) 的性能,EX:add(), remove(), contains()

  • 它可以節省儲存空間,僅使用項目所需的內存,它使用儲存器的連續空間。

  • 樹應該要保持平衡。

如果出現以下狀況,我們應該選擇 TreeMap :

  • 必須考慮記憶體的限制

  • 我們不知道有多少項目必須儲存在內存中

  • 我們想以自然順序提取對象

  • 我們的項目被一致性的添加和刪除

  • 我們願意接受 O(log n) 的搜索時間

相似之處

獨特元素

兩者都不支持重複的 key,如果新增,則會覆蓋掉前一個元素 (沒有錯誤或提示)

併發訪問

這兩種 Map 實現 都不同步,我們要自己管理併發訪問。(不太懂,請自行查看參考資料)

Both Map implementations aren’t synchronized and we need to manage concurrent access on our own.

Both must be synchronized externally whenever multiple threads access them concurrently and at least one of the threads modifies them.

We have to explicitly use Collections.synchronizedMap(mapName) to obtain a synchronized view of a provided map.

失敗快速迭代器

如果在創建迭代器之後,以任何方式、任何時間修改 Map,Iterator 將拋出 ConcurrentModificationException。

判斷該使用哪一種

一般來說,兩種都有各自的優點和缺點,但是,這是關於潛在的期望和要求,這些期望和要求必須決定我們對此的選擇。

  • 如果我們想要保持我們的條目排序,我們應該使用 TreeMap

  • 如果我們優先考慮性能,而不是內存消耗,我們應該使用 HashMap

  • 由於 TreeMap 具有更重要的局部性,如果我們想要根據它們的自然順序 訪問彼此相對接近的對象,我們可能會考慮它

  • 可以使用 initialCapacity 和 loadFacotr 調整 HashMap,這對於 TreeMap 是不可能的

  • 如果我們想要保留插入順序,同時受益於恆定時間訪問,我們可以使用 LinkedHashMap

總結:

Java 集合的基本用法,都歸納了,上面這些是平常最常用的 Java 集合,具體的其他的,還要參考JDK幫助文檔了,呵呵 關於 Map的應用,還有很多,具體就是這個,Collections 提供了很多 List / Map 實用的方法,對平常開發非常有用。

List 按物件進入的順序保存物件,不做排序或編輯操作。

Set 對每個物件只接受一次,並使用自己內部的排序方法(通常,你只關心某個元素是否屬於 Set,而不關心它的順序,否則應該使用 List)。

Map 同樣對每個元素保存一份,但這是基於"key"的,Map 也有內置的排序,因而不關心元素添加的順序。如果添加元素的順序對你很重要,應該使用 LinkedHashSet 或者 LinkedHashMap.

Enum

public enum Name {
   obj1 (1),
   obj2 (2),
   obj3 (3),
   obj4 (4),
   ;

   private int testNO;

   private Name(int i){
      testNO = i;
   }

   public int isTestNO(){ return testNO; }
}

Map

KeySet & EntrySet

參考資料:(https://blog.csdn.net/yaomingyang/article/details/78748130)

keySet() 是 key 的集合,集合裡面是 key 的型別 entrySet() 是 key-value 的集合,集合裡面的型別是 Map.Entry 該型別會提供 getKey()、getValue() 的方法

Map<String,String> map = new HashMap();

//普遍使用,二次取值
for(String key : map.keySet()){
    System.out.println("key = " + key + ", value = " + map.get(key));
}

//推薦,尤其是容量大的時候
for(Map.Entry<String,String> entry : map.entrySet()){
    System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());    
}

//利用 Iterator 的作法
Iterator<Map.Entry<String,String>> it = map.entrySet().iterator();
while(it.hasNext()){
    Map.Entry<String,String> entry = it.next();
    System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}

//只會遍歷所有的 value,不會遍歷所有的 key
for(String value : map.values()){
    System.out.println("value = " + value);
}

List 排序

參考資料:(https://www.ewdna.com/2008/10/list.html)

可以透過 Collection.sort(List l, Comparator c) 方法來做排序

StringIntPair

public class StringIntPair{

    private String string;
    private int integer;

    public StringIntPair(String s, int i) {
        string = s;
        integer = i;
    }

    protected String getString() {
        return string;
    }

    protected int getInteger() {
        return integer;
    }

    public String toString() {
        return string + "\t" + integer;
    }
}

接著是執行 List Sort 的程式碼

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ListSort {

    public static void main(String[] args) {

        List<StringIntPair> list = new ArrayList<StringIntPair>();

        list.add(new StringIntPair("ab", 5));
        list.add(new StringIntPair("ca", 3));
        list.add(new StringIntPair("aa", 2));
        list.add(new StringIntPair("db", 4));
        list.add(new StringIntPair("bc", 1));

        System.out.println("排序前");
        for (Object o:list) {
            System.out.println(o);
        }

        // 依string排序
        Collections.sort(list,
        new Comparator<StringIntPair>() {
            public int compare(StringIntPair o1, StringIntPair o2) {
                return o1.getString().compareTo(o2.getString());
            }
        });
        System.out.println();
        System.out.println("依string排序");
        for (Object o:list) {
            System.out.println(o);
        }

        // 依Integer排序
        Collections.sort(list,
        new Comparator<StringIntPair>() {
            public int compare(StringIntPair o1, StringIntPair o2) {
                return o2.getInteger()-o1.getInteger();
            }
        });
        System.out.println();
        System.out.println("依Integer排序");
        for (Object o:list) {
            System.out.println(o);
        }
    }
}

執行結果:

排序前
 ab 5
 ca 3
 aa 2
 db 4
 bc 1

 依string排序
 aa 2
 ab 5
 bc 1
 ca 3
 db 4

 依Integer排序
 ab 5
 db 4
 ca 3
 aa 2
 bc 1

ArrayList vs TreeMap

參考資料:(https://stackoverflow.com/questions/16725421/treemap-vs-arraylist-perfomance-resources-while-iterating-adding-editing-val)

根據參考資料,我認為 該使用何者 取決於 我們存在這個陣列的內容為何?

第一個判斷方式

資料是否重複?

如果裡面的資料能夠有不重複的 key,那就會選 TreeMap

如果裡面的資料會重複,則選 ArrayList

第二種判斷方式

想要幾種排序?

倘若要儲存的物件內容有多種屬性,而我們可能需要用不同的屬性去做排序,則使用 ArrayList

倘若只會利用其中一種屬性進行排序 (並且為不重複的),則使用TreeMap

兩者的詳細差異

ArrayList

  • 允許重複

  • add 在最後 O(1)

  • insert 在中間 O(n)

  • access O(1)

  • remove O(n)

TreeMap

  • 不允許重複

  • 新增一筆 O(logn)

  • access O(logn)

  • remove O(logn)

因為TreeMap 在新增的時候,就已經做好排序了 而 ArrayList 要排序時,需要 O(nlogn)

Last updated