實戰小技巧19:List轉Map List的幾種姿勢

小灰灰Blog發表於2022-06-07

今天介紹一個實用的小知識點,如何將List轉為Map<Object, List<Object>>

<!-- more -->

1. 基本寫法

最開始介紹的當然是最常見、最直觀的寫法,當然也是任何限制的寫法

// 比如將下面的列表,按照字串長度進行分組
List<String> list = new ArrayList<>();
list.add("hello");
list.add("word");
list.add("come");
list.add("on");
Map<Integer, List<String>> ans = new HashMap<>();

for(String str: list) {
    List<String> sub = ans.get(str.length());
    if(sub == null) {
        sub = new ArrayList<>();
        ans.put(str.length(), sub);
    }
    sub.add(str);
}
System.out.println(ans);

對於jdk8+,上面for迴圈中的內容可以利用Map.computeIfAbsent來替換,具體寫法如下

for (String str : list) {
    ans.computeIfAbsent(str.length(), k -> new ArrayList<>()).add(str);
}

當然既然已經是jdk1.8了,藉助Stream的流處理,可以將上面的更一步進行簡化,如下

Map<Integer, List<String>> ans = list.stream().collect(Collectors.groupingBy(String::length));

2. 通用方法

上面是針對特定的列表,針對業務進行開發轉換,那麼我們接下來嘗試構建一個通用的工具類

這裡我們主要藉助的知識點就是泛型,一個重要的點就是如何獲取Map中的key

對於jdk < 1.8的寫法,通過介面來定義實現key的獲取姿勢

public static <K, V> Map<K, List<V>> toMapList(List<V> list, KeyFunc<V, K> keyFunc) {
    Map<K, List<V>> result = new HashMap<>();
    for (V item: list) {
        K key = keyFunc.getKey(item);
        if (!result.containsKey(key)) {
            result.put(key, new ArrayList<>());
        }
        result.get(key).add(item);
    }
    return result;
}

public static interface KeyFunc<T, K> {
    K getKey(T t);
}

使用demo如下

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("hello");
    list.add("word");
    list.add("come");
    list.add("on");
    Map<Integer, List<String>> res = toMapList(list, new KeyFunc<String, Integer>() {
        @Override
        public Integer getKey(String s) {
            return s.length();
        }
    });
    System.out.println(res);
}

接下來再看一下jdk1.8之後的寫法,結合stream + 函式方法來實現

public static <K, V> Map<K, List<V>> toMapList(List<V> list, Function<V, K> func) {
    return list.stream().collect(Collectors.groupingBy(func));
}

其對應的使用方式則如下

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("hello");
    list.add("word");
    list.add("come");
    list.add("on");
    Map<Integer, List<String>> res = toMapList(list, (Function<String, Integer>) String::length);
    System.out.println(res);
}
  1. 工具類

上一節介紹了基於泛型 + jdk8 Stream + 函式方法來實現通用轉換工具類的實現姿勢,接下來我們小結一下,輸出一個適用於1.8之後的工具類

/**
 * List<V>轉換為Map<K, List<V>> 特點在於Map中的value,是個列表,且列表中的元素就是從原列表中的元素
 *
 * @param list
 * @param func 基於list#item生成Map.key的函式方法
 * @param <K>
 * @param <V>
 * @return
 */
public static <K, V> Map<K, List<V>> toMapList(List<V> list, Function<V, K> func) {
    return list.stream().collect(Collectors.groupingBy(func));
}

/**
 * List<I>轉換為Map<K, List<V>> 特點在於Map中的value是個列表,且列表中的元素是由list.item轉換而來
 *
 * @param list
 * @param keyFunc 基於list#item生成的Map.key的函式方法
 * @param valFunc 基於list#item轉換Map.value列表中元素的函式方法
 * @param <K>
 * @param <I>
 * @param <V>
 * @return
 */
public static <K, I, V> Map<K, List<V>> toMapList(List<I> list, Function<I, K> keyFunc, Function<I, V> valFunc) {
    return list.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valFunc, Collectors.toList())));
}

4.guava HashMultimap擴充套件知識點

最後再介紹一個擴充套件知識點,Gauva工具包中提供了一個HashMultimap的工具類,他的使用姿勢和我們平常的Map並無差別,但是需要在注意的是,它的value是個集合

List<String> list = new ArrayList<>();
list.add("hello");
list.add("word");
list.add("come");
list.add("on");
list.add("on");
HashMultimap<Integer, String> map = HashMultimap.create();
for (String item: strList) {
    map.put(item.length(), item);
}
System.out.println(map);

實際輸出如下,驗證了value實際上是個集合(on只有一個,如果是我們上面的工具類,會輸出兩個)

{2=[on], 4=[word, come], 5=[hello]}

一灰灰的聯絡方式

盡信書則不如無書,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

QrCode

相關文章