Java 8 Listesinde <V> Listesini <K, V>


932

Java 8'in akışlarını ve lambdaslarını kullanarak bir Nesne Listesini Haritaya çevirmek istiyorum.

Java 7 ve aşağısında böyle yazarım.

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 ve Guava kullanarak bunu kolayca başarabilirim ama bunu Guava olmadan nasıl yapacağımı bilmek istiyorum.

Guava'da:

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();
        }
    });
}

Ve Java 8 lambdas ile Guava.

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

Yanıtlar:


1394

CollectorsBelgelere dayanarak bu kadar basit:

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

167
Bir yan not olarak, Java 8'den sonra bile, JDK bir kısaltma yarışmasında hala rekabet edemez. Guava alternatif bakışlar çok okunabilir: Maps.uniqueIndex(choices, Choice::getName).
Bogdan Calmac

3
(Statik olarak ithal) kullanarak Seqgelen JOOL : (Java 8 kullanan herkese tavsiye ederim) kütüphanesinde, ayrıca ile kısalık artırabilir seq(choices).toMap(Choice::getName)
Lukens

5
Function.identity kullanmanın herhangi bir faydası var mı? Yani, bu -> daha kısa
shabunc

9
@shabunc Hiçbir fayda bilmiyorum ve aslında it -> itkendimi kullanıyorum. Function.identity()burada çoğunlukla başvurulan belgelerde kullanıldığından ve yazma sırasında lambdas hakkında bildiğimden dolayı kullanılır
zapl

12
@zapl, oh, aslında bunun arkasında nedenler var - stackoverflow.com/questions/28032827/…
shabunc

307

Anahtarınızın listedeki tüm öğeler için benzersiz olduğu GARANTİ EDİLMİYORSA , anahtarı Map<String, List<Choice>>birMap<String, Choice>

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

76
Bu aslında size benzersiz olmayan anahtarların olasılığı ile ilgilenen ancak OP'nin istediği gibi olmayan Harita <Dize, Liste <Seçim>> verir. Guava'da, Multimaps.index (seçimler, Choice :: getName), yine de istediğiniz şeyse, muhtemelen daha iyi bir seçenektir.
Richard Nichols

ya da daha doğrusu, Guava'nın aynı anahtarın birden çok değerle eşleştiği senaryolarda oldukça kullanışlı olan Multimap <String, Choice> kullanın. Guava'da bu tür veri yapılarını kullanmak için bir Harita <Dize, Liste <Seçim>>
Neeraj B.

1
@RichardNichols guava Multimapsyöntemi neden daha iyi bir seçenek? Bir MapNesne döndürmediğinden rahatsızlık verebilir .
theyuv

156

Haritanın değeri olarak getName()anahtar ve Choicekendisini kullanın :

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

19
Lütfen kullanıcının anlayabilmesi için bir açıklama yazın.
Mayank Jain

4
Burada daha fazla ayrıntı olmaması gerçekten çok kötü, çünkü bu cevabı en çok seviyorum.
MadConan

Collectors.toMap(Choice::getName,c->c)(2 karakter daha kısa)
Ferrybig

7
Bu eşit olduğunu choices.stream().collect(Collectors.toMap(choice -> choice.getName(),choice -> choice)); değer için anahtarın, ikinci fonksiyon için ilk fonksiyonu
waterscar

16
Görmenin ve anlamanın ne kadar kolay olduğunu biliyorum c -> cama Function.identity()daha semantik bilgi taşır. Genellikle kullanabileceğim statik bir içe aktarma kullanıyorumidentity()
Hank D

28

Collectors.toMap () kullanmak istemiyorsanız, başka bir örnek

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

1
Collectors.toMap()Yukarıdaki örnekte gösterdiğiniz gibi hangisini veya kendi HashMap'ımızı kullanmak daha iyidir ?
Swapnil Gangrade

1
Bu örnek, haritaya başka bir şeyin nasıl yerleştirileceğine ilişkin bir örnek sağlamıştır. Bir yöntem çağrısı tarafından sağlanmayan bir değer istedim. Teşekkürler!
th3morg

1
Üçüncü argüman işlevi doğru değil. Orada Hashmap :: putAll
jesantana

25

Listelenen cevapların çoğu, listede yinelenen öğeler olduğunda bir vakayı kaçırır . Bu durumda cevap atar IllegalStateException. Liste kopyalarını da işlemek için aşağıdaki koda bakın :

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

19

Basit bir şekilde bir seçenek daha

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

3
Bu veya java 7 tipinin kullanımında geçerli bir fark yoktur.
Ashish Lohia

3
Bunun neler olduğunu okumak ve anlamak için diğer mekanizmalardan daha kolay buldum
Freiheit

2
SO, Java 8 Akışları ile sordu.
Mehraj Malik

18

Örneğin, nesne alanlarının eşlenmesini istiyorsanız:

Örnek nesne:

class Item{
        private String code;
        private String name;

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

        //getters and setters
    }

Ve operasyon Listesi Haritaya dönüştürmek:

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

Üçüncü taraf kitaplıklarını kullanmanın bir sakıncası yoksa, AOL'nin cyclops-tepki tepkisi (açıklama ben katkıda bulunuyorum) Liste ve Harita dahil tüm JDK Koleksiyonu türleri için uzantılara sahiptir .

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

10

Bunu yapmaya çalışıyordum ve yukarıdaki cevapları kullanarak Functions.identity(), Harita anahtarını kullanırken, o zaman this::localMethodNameyazım sorunları nedeniyle gerçekten çalışmak gibi bir yerel yöntem kullanmayla ilgili sorunlar yaşadım .

Functions.identity()aslında bu durumda yazmak için bir şeyler yapar, böylece yöntem sadece Objectbir param döndürerek ve kabul ederek çalışırObject

Bunu çözmek için hendek Functions.identity()ve s->sbunun yerine kullandım.

Benim kodum, benim durumumda bir dizin içindeki tüm dizinleri listelemek ve her biri için dizinin adını haritanın anahtarı olarak kullanın ve sonra dizin adı ile bir yöntem çağırmak ve bir öğe koleksiyonu döndürmek gibi:

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

10

Bir IntStream kullanarak bir indeks akışı oluşturabilir ve ardından bunları Haritaya dönüştürebilirsiniz:

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

1
Bu iyi bir seçenek değildir, çünkü her eleman için bir get () çağrısı yaparsınız, bu nedenle işlemin karmaşıklığını arttırırsınız (o öğeler bir hashmap ise o (n * k)).
Nicolas Nobelis

(İ) hashmap O (1) almıyor mu?
Ivo van der Veeken

@IvovanderVe kod snippet'indeki get (i) haritada değil, listede.
Zaki

@Zaki Nicolas'ın sözünden bahsediyordum. Öğeler bir liste yerine bir hashmap ise n * k karmaşıklığını görmüyorum.
Ivo van der Veeken

8

Listeyi jenerikler ve kontrolün tersini kullanarak nasıl haritaya dönüştüreceğim yazacağım . Sadece evrensel yöntem!

Belki elimizdeki Tamsayılar listesini veya listesini nesneler. Yani soru şudur: haritanın anahtarı ne olmalı ?

arayüz oluştur

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

Şimdi kontrolün tersini kullanıyor:

  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));
    }

Örneğin, kitap nesnelerimiz varsa, bu sınıf harita için anahtar seçmektir

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

6

Bu sözdizimini kullanıyorum

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

11
groupingByMap<K,List<V>>değil, oluşturur Map<K,V>.
Christoffer Hammarström

3
Ülser cevap. Ve, String getName();(Tamsayı değil)
Barett

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

4

Bu 2 şekilde yapılabilir. Kişi bunu göstermek için kullanacağımız sınıf olsun.

public class Person {

    private String name;
    private int age;

    public String getAge() {
        return age;
    }
}

Let kişiler Kişilerin listesini olmak haritasına dönüştürülecek

1.Basit foreach ve Listede Lambda İfadesi Kullanma

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

2. Verilen Listede tanımlanan Akışta Toplayıcıları Kullanma.

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

4

Bunu yapmak için akışları kullanmak mümkündür. Açıkça kullanma ihtiyacını ortadan kaldırmak için Collectors, toMapstatik olarak içe aktarmak mümkündür (Effective Java, üçüncü baskı tarafından önerildiği gibi).

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));

Bana bu amaca hizmet ediyor,

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

3

Aynı anahtar adı için her yeni değerin geçersiz kılınması gerekiyorsa:

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

Tüm seçeneklerin bir ad için bir listede gruplanması gerekiyorsa:

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.

0

Alternatif olarak kotlin-stdlibguava kullanabilirsiniz

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

-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);

Genel anahtar AA {12 = ASDFASDFASDF, 7 = EEDDDAD, 4 = CCCC, 3 = BBB, 2 = AA}


2
Burada ne yapmaya çalışıyorsun ?? Soruyu okudun mu?
navderm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.