Java 8 List <V>からMap <K、V>へ


932

Java 8のストリームとラムダを使用して、オブジェクトのリストをマップに変換したい。

これは私がJava 7以下でそれを書く方法です。

private Map<String, Choice> nameMap(List<Choice> choices) {
        final Map<String, Choice> hashMap = new HashMap<>();
        for (final Choice choice : choices) {
            hashMap.put(choice.getName(), choice);
        }
        return hashMap;
}

Java 8とGuavaを使用してこれを簡単に達成できますが、Guavaなしでこれを行う方法を知りたいです。

グアバでは:

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, new Function<Choice, String>() {

        @Override
        public String apply(final Choice input) {
            return input.getName();
        }
    });
}

そしてJava 8ラムダのグアバ。

private Map<String, Choice> nameMap(List<Choice> choices) {
    return Maps.uniqueIndex(choices, Choice::getName);
}

回答:


1394

Collectorsドキュメントに基づいて、それは次のように簡単です:

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName,
                                              Function.identity()));

167
余談ですが、Java 8以降でも、JDKは簡潔さのコンテストに参加できません。Guavaの代替案は非常に読みやすく見えますMaps.uniqueIndex(choices, Choice::getName)
Bogdan Calmac

3
(静的にインポートされた)を使用してSeqからJOOLの(Iは、Java 8を使用して誰にもお勧めします)、ライブラリ、あなたもと簡潔性を向上することができます seq(choices).toMap(Choice::getName)
lukens

5
Function.identityを使用するメリットはありますか?
つまり

9
@shabunc私は何の利益も知らず、実際にit -> it自分自身を使用しています。Function.identity()ここで主に使用されているのは、参照ドキュメントで使用されているためで、執筆時点でラムダについて知っていたすべてのことです
zapl

12
@zapl、ああ、実際には、この背後にある理由がある判明- stackoverflow.com/questions/28032827/...
shabunc

307

あなたのキーがされている場合は、NOTリスト内のすべての要素に対して一意であることが保証、あなたはそれを変換する必要がありますMap<String, List<Choice>>代わりにAMap<String, Choice>

Map<String, List<Choice>> result =
 choices.stream().collect(Collectors.groupingBy(Choice::getName));

76
これは実際にはMap <String、List <Choice >>を提供します。これは一意でないキーの可能性を扱いますが、OPが要求したものではありません。グアバでは、Multimaps.index(choices、Choice :: getName)は、これがどうしても必要な場合は、おそらくより良いオプションです。
Richard Nichols、

または、GuavaのMultimap <String、Choice>を使用します。これは、同じキーが複数の値にマッピングされるシナリオで非常に便利です。むしろ、地図<文字列、リスト<選択肢を作成するよりも、このようなデータ構造を使用するためにグアバで容易に利用可能なさまざまなユーティリティメソッドがあります。>>
Neeraj B.

1
@RichardNicholsなぜグアバMultimaps法がより良い選択肢なのですか?MapObjectを返さないので不便かもしれません。
theyuv

156

getName()キーとして使用し、Choiceマップ自体の値として使用します。

Map<String, Choice> result =
    choices.stream().collect(Collectors.toMap(Choice::getName, c -> c));

19
ユーザーが理解できるように説明を書き込んでください。
Mayank Jain、2015

4
この回答が一番好きなので、ここには詳細がありません。
MadConan 2015年

Collectors.toMap(Choice::getName,c->c)(2文字短い)
Ferrybig 2015年

7
それはに等しいです choices.stream().collect(Collectors.toMap(choice -> choice.getName(),choice -> choice)); 、キーの値のための第二の機能最初の関数
waterscar

16
見たり理解したりするのc -> cがいかに簡単であるかはわかりますが、Function.identity()意味論的な情報は多く含まれています。私は通常、静的インポートを使用して、ただ使用できるようにしますidentity()
Hank D

28

Collectors.toMap()を使用したくない場合のために、もう1つ示します。

Map<String, Choice> result =
   choices.stream().collect(HashMap<String, Choice>::new, 
                           (m, c) -> m.put(c.getName(), c),
                           (m, u) -> {});

1
Collectors.toMap()上記の例で示したように、どちらを使用するか、または独自のHashMap を使用する方が良いですか?
Swapnil Gangrade 16

1
この例では、マップに何かを配置する方法の例を示しました。メソッド呼び出しで提供されない値が必要でした。ありがとう!
th3morg 2017

1
3番目の引数関数は正しくありません。次の2つのハッシュマップ、ハッシュマップ::のputAllのようなものをマージするためにいくつかの機能が提供しなければならない
jesantana

25

記載されている回答のほとんどは、リストに重複する項目がある場合はケースを逃します。その場合、答えがスローされIllegalStateExceptionます。リストの重複処理するには、以下のコード参照してください。

public Map<String, Choice> convertListToMap(List<Choice> choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName, choice -> choice,
            (oldValue, newValue) -> newValue));
  }

19

簡単な方法でもう1つのオプション

Map<String,Choice> map = new HashMap<>();
choices.forEach(e->map.put(e.getName(),e));

3
このタイプまたはJava 7タイプの使用に実行可能な違いはありません。
Ashish Lohia

3
他のメカニズムよりも何が起こっているのかを読んで理解する方が簡単だと思いました
Freiheit

2
SOはJava 8 Streamsで尋ねました。
Mehraj Malik 2018

18

たとえば、オブジェクトフィールドをマップに変換する場合:

オブジェクトの例:

class Item{
        private String code;
        private String name;

        public Item(String code, String name) {
            this.code = code;
            this.name = name;
        }

        //getters and setters
    }

そして操作はリストをマップに変換します:

List<Item> list = new ArrayList<>();
list.add(new Item("code1", "name1"));
list.add(new Item("code2", "name2"));

Map<String,String> map = list.stream()
     .collect(Collectors.toMap(Item::getCode, Item::getName));

12

サードパーティのライブラリを使用してもかまわない場合は、AOLのcyclops-react lib(私がコントリビューターである開示)に、ListMapを含むすべてのJDKコレクションタイプの拡張機能があります。

ListX<Choices> choices;
Map<String, Choice> map = choices.toMap(c-> c.getName(),c->c);

10

私はこれを試みていましたが、上記の回答を使用Functions.identity()して、マップへのキーに使用するthis::localMethodNameと、入力の問題のために、実際に機能するようなローカルメソッドの使用に問題があることがわかりました。

Functions.identity()この場合、実際には型付けに何かを行うので、このメソッドはObjectparamを返し、受け入れることによってのみ機能します。Object

これを解決するために、私は捨てFunctions.identity()s->s代わりに使用することになりました。

したがって、私のコードでは、私の場合、ディレクトリ内のすべてのディレクトリをリストし、それぞれのディレクトリの名前をマップへのキーとして使用してから、ディレクトリ名を使用してメソッドを呼び出し、アイテムのコレクションを返します。

Map<String, Collection<ItemType>> items = Arrays.stream(itemFilesDir.listFiles(File::isDirectory))
.map(File::getName)
.collect(Collectors.toMap(s->s, this::retrieveBrandItems));

10

IntStreamを使用してインデックスのストリームを作成し、それらをMapに変換できます。

Map<Integer,Item> map = 
IntStream.range(0,items.size())
         .boxed()
         .collect(Collectors.toMap (i -> i, i -> items.get(i)));

1
すべての要素に対してget()呼び出しを行うため、これは適切なオプションではありません。したがって、操作が複雑になります(アイテムがハッシュマップの場合はo(n * k))。
Nicolas Nobelis 2017

ハッシュマップO(1)のget(i)ではないですか?
イヴォファンデルヴィーケン2018

@IvovanderVeekenコードスニペットのget(i)は、マップではなくリストにあります。
Zaki 2018年

@Zakiニコラスの発言について話していました。アイテムがリストではなくハッシュマップの場合、n * kの複雑さはわかりません。
Ivo van der Veeken

8

ジェネリックコントロールの反転を使用して、リストをマップに変換する方法を説明します。まさに普遍的な方法!

多分私達は整数のリストかオブジェクトのリストを持っています。だから問題は次のとおりです:マップのキーは何であるべきですか?

インターフェースを作成する

public interface KeyFinder<K, E> {
    K getKey(E e);
}

現在、制御の反転を使用しています:

  static <K, E> Map<K, E> listToMap(List<E> list, KeyFinder<K, E> finder) {
        return  list.stream().collect(Collectors.toMap(e -> finder.getKey(e) , e -> e));
    }

たとえば、bookのオブジェクトがある場合、このクラスはマップのキーを選択することです

public class BookKeyFinder implements KeyFinder<Long, Book> {
    @Override
    public Long getKey(Book e) {
        return e.getPrice()
    }
}

6

この構文を使用します

Map<Integer, List<Choice>> choiceMap = 
choices.stream().collect(Collectors.groupingBy(choice -> choice.getName()));

11
groupingByMap<K,List<V>>ではなくを作成しますMap<K,V>
ChristofferHammarström2015年

3
ulisesの重複は答えます。そして、String getName();(整数ではない)
Barett

5
Map<String, Set<String>> collect = Arrays.asList(Locale.getAvailableLocales()).stream().collect(Collectors
                .toMap(l -> l.getDisplayCountry(), l -> Collections.singleton(l.getDisplayLanguage())));

4

これには2つの方法があります。personを、それを示すために使用するクラスにします。

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}

してみましょう人物がマップに変換する者のリストで

1.リストでの単純なforeachとラムダ式の使用

Map<Integer,List<Person>> mapPersons = new HashMap<>();
persons.forEach(p->mapPersons.put(p.getAge(),p));

2.指定されたリストで定義されたストリームでコレクターを使用します。

 Map<Integer,List<Person>> mapPersons = 
           persons.stream().collect(Collectors.groupingBy(Person::getAge));

4

これを行うためにストリームを使用することが可能です。を明示的に使用する必要をなくすために、静的CollectorsにインポートすることができますtoMap(Effective Java第3版で推奨)。

import static java.util.stream.Collectors.toMap;

private static Map<String, Choice> nameMap(List<Choice> choices) {
    return choices.stream().collect(toMap(Choice::getName, it -> it));
}


3
Map<String,Choice> map=list.stream().collect(Collectors.toMap(Choice::getName, s->s));

私にもこの目的を果たします

Map<String,Choice> map=  list1.stream().collect(()-> new HashMap<String,Choice>(), 
            (r,s) -> r.put(s.getString(),s),(r,s) -> r.putAll(s));

3

同じキー名のすべての新しい値をオーバーライドする必要がある場合:

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream()
        .collect(Collectors.toMap(Choice::getName,
            Function.identity(),
            (oldValue, newValue) - > newValue));
}

すべての選択肢を名前のリストにグループ化する必要がある場合:

public Map < String, Choice > convertListToMap(List < Choice > choices) {
    return choices.stream().collect(Collectors.groupingBy(Choice::getName));
}

1
List<V> choices; // your list
Map<K,V> result = choices.stream().collect(Collectors.toMap(choice::getKey(),choice));
//assuming class "V" has a method to get the key, this method must handle case of duplicates too and provide a unique key.


-1
String array[] = {"ASDFASDFASDF","AA", "BBB", "CCCC", "DD", "EEDDDAD"};
    List<String> list = Arrays.asList(array);
    Map<Integer, String> map = list.stream()
            .collect(Collectors.toMap(s -> s.length(), s -> s, (x, y) -> {
                System.out.println("Dublicate key" + x);
                return x;
            },()-> new TreeMap<>((s1,s2)->s2.compareTo(s1))));
    System.out.println(map);

重複キーAA {12 = ASDFASDFASDF、7 = EEDDDAD、4 = CCCC、3 = BBB、2 = AA}


2
ここで何をしようとしていますか?質問を読みましたか?
navderm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.