Java 8 - リストをマップに変換する

オブジェクトの List`を Map`に変換する方法と、複製されたキーを扱う方法を示すJava 8の例はほとんどありません。

Hosting.java

package com.mkyong.java8

public class Hosting {

    private int Id;
    private String name;
    private long websites;

    public Hosting(int id, String name, long websites) {
        Id = id;
        this.name = name;
        this.websites = websites;
    }

   //getters, setters and toString()
}

1. List to Map - Collectors.toMap()

Hosting`オブジェクトのリストを作成し、 Collectors.toMap`を使ってマップに変換します。

TestListMap.java

package com.mkyong.java8

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestListMap {

    public static void main(String[]args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));

       //key = id, value - websites
        Map<Integer, String> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getId, Hosting::getName));

        System.out.println("Result 1 : " + result1);

       //key = name, value - websites
        Map<String, Long> result2 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 2 : " + result2);

       //Same with result1, just different syntax
       //key = id, value = name
        Map<Integer, String> result3 = list.stream().collect(
                Collectors.toMap(x -> x.getId(), x -> x.getName()));

        System.out.println("Result 3 : " + result3);
    }
}

出力

Result 1 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}
Result 2 : {liquidweb.com=80000, mkyong.com=1, digitalocean.com=120000, aws.amazon.com=200000, linode.com=90000}
Result 3 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}

2.マップへのリスト - 重複したキー!

2.1以下のコードを実行すると、重複したキーエラーがスローされます!

TestDuplicatedKey.java

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestDuplicatedKey {

    public static void main(String[]args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));

        list.add(new Hosting(6, "linode.com", 100000));//new line

       //key = name, value - websites , but the key 'linode' is duplicated!?
        Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 1 : " + result1);

    }
}

出力 - 以下のエラーメッセージは少し誤解を招く、キーの値の代わりに "linode"を表示する必要があります。

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 90000
    at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
    at java.util.HashMap.merge(HashMap.java:1245)
   //...

2.2上記の重複したキーの問題を解決するには、次のように3番目のmergeFunction引数を渡します。

    Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites,
                        (oldValue, newValue) -> oldValue
                )
        );

出力

Result 1 : {..., aws.amazon.com=200000, linode.com=90000}

3.3新しい値を試す

    Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites,
                        (oldValue, newValue) -> newvalue
                )
        );

出力

Result 1 : {..., aws.amazon.com=200000, linode.com=100000}

3.マップに一覧表示 - ソート&収集

TestSortCollect.java

package com.mkyong.java8;

import java.util.** ;
import java.util.stream.Collectors;

public class TestSortCollect {

    public static void main(String[]args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));
        list.add(new Hosting(6, "linode.com", 100000));

       //example 1
        Map result1 = list.stream()
                .sorted(Comparator.comparingLong(Hosting::getWebsites).reversed())
                .collect(
                        Collectors.toMap(
                                Hosting::getName, Hosting::getWebsites,//key = name, value = websites
                                (oldValue, newValue) -> oldValue,      //if same key, take the old key
                                LinkedHashMap::new                     //returns a LinkedHashMap, keep order
                        ));

        System.out.println("Result 1 : " + result1);

    }
}

出力

Result 1 : {aws.amazon.com=200000, digitalocean.com=120000, linode.com=100000, liquidweb.com=80000, mkyong.com=1}

__P.S上記の例では、ストリームは収集前にソートされているので、 "linode.com = 100000"は "oldValue"になりました。

参考文献

8つのコレクターJavaDoc]。リンク://java8/java-8-how-to-sort-a-map/[Java 8 - マップのソート方法]

  1. リンク://java8/java-8-lambda-comparator-example/[Java 8 Lambda:

コンパレータの例]

コレクター duplicated key java 8 list リンク://タグ/マップ/[マップ]リンク://タグ/ソート/[ソート]リンク://タグ/ストリーム/[ストリーム]