2016-11-08 18 views
9

ile Harita için, Harita > düzleştirin. stream ve lambda ile mümkün ve yararlı olduğunu düşünüyorum. Ben tuş eşleme kaybetmeden <code>String</code> bir listeye bir <code>Integer</code> anahtarı ilişkilendiren bir <code>Map</code> düzleştirmek istiyorum dere ve lambda

Biz böyle bir şey ile başlar: en de listelerde değerleri benzersiz olmasını varsayalım

1: a,b,c 
2: d,e,f 
etc. 

:

Map<Integer, List<String>> mapFrom = new HashMap<>(); 

en o mapFrom yerde nüfuslu, ve gibi görünüyor varsayalım. ( foreach kullanarak, çok benzer şekilde veya)

a: 1 
b: 1 
c: 1 
d: 2 
e: 2 
f: 2 
etc. 

böyle yapabileceğini:

Şimdi, bunun gibi ikinci harita almak için "açılmak" isteyen

Map<String, Integer> mapTo = new HashMap<>(); 
for (Map.Entry<Integer, List<String>> entry: mapFrom.entrySet()) { 
    for (String s: entry.getValue()) { 
     mapTo.put(s, entry.getKey()); 
    } 
} 

Şimdi İç içe for döngüler yerine lambda kullanmak istediğimizi varsayalım. benim boyutluluk sorunu kurtulmak yardımcı olsa da, baz, ben de flatMap bir deneyin verdi

Map<String, Integer> mapTo = mapFrom.entrySet().stream().map(e -> { 
    e.getValue().stream().? 
    // Here I can iterate on each List, 
    // but my best try would only give me a flat map for each key, 
    // that I wouldn't know how to flatten. 
}).collect(Collectors.toMap(/*A String value*/,/*An Integer key*/)) 

, ama gitmek için doğru yol olduğunu sanmıyorum: Ben muhtemelen böyle bir şey yapacağını Bu süreçte anahtarı kaybediyorum. Özetle

, benim iki soru vardır:

  • o bunu başarmak için streams ve lambda kullanmak mümkün mü?
  • Böyle yapmak (performans, okunabilirlik) yararlı mıdır?

    entrySet.stream() 
         .flatMap(e -> e.getValue().stream() 
             .map(s -> new SimpleImmutableEntry(e.getKey(), s))); 
    

    SimpleImmutableEntryAbstractMap bir iç içe sınıftır şu şekildedir:

+4

Kesinlikle 'flatMap': 'entrySet.stream(). Isterseniz flatMap (e -> e.getValue(). Stream(). Map (s -> new SimpleImmutableEntry (e.getKey(), s))' –

+2

@MarkoTopolnik yorumunuz cevap olmalıdır – Mena

+1

@MarkoTopolnik bir cevap olmalıdır – Eugene

cevap

11

Yeni akışına değerleri düzleştirmek flatMap kullanmak gerekir, ama yine de bir Map içine toplamak için orijinal anahtarlarına ihtiyacım beri, anahtar ve değer tutan geçici nesneye haritasına zorunda , Örneğin

Map<String, Integer> mapTo = mapFrom.entrySet().stream() 
     .flatMap(e->e.getValue().stream() 
        .map(v->new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v))) 
     .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); 

Map.Entry

bir stand-varolmayan demet türü için, farklı türdeki iki nesne tutabilen herhangi bir tür yeterlidir.

bu geçici nesneler gerektirmeyen bir alternatif özel koleksiyoncu:

Map<String, Integer> mapTo = mapFrom.entrySet().stream().collect(
    HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v, e.getKey())), Map::putAll); 

Bu toMap farklı bir yinelenen varsa, bir istisna durumu bir birleşme fonksiyonu olmadan toMap ise sessizce yinelenen anahtarlar üzerine yazmaktan tuşuna basın. Temel olarak, bu özel kolektör

Map<String, Integer> mapTo = new HashMap<>(); 
mapFrom.forEach((k, l) -> l.forEach(v -> mapTo.put(v, k))); 

paralel yetenekli çeşididir Ama bu görev bile çok büyük bir girdi harita ile paralel işleme yarar olmaz unutmayın. Akım boru hattında SMP'den yararlanabilecek ek sayısal yoğun iş varsa, paralel akışlardan faydalanma şansı vardı. Bu nedenle, kısa ve sıralı Koleksiyon API'sı çözümü tercih edilebilir.

+0

Bu bilgilendirici ve eksiksiz cevap için çok teşekkür ederim. 360 perspektifleri kapsayan – Vongo

+0

güzel cevap :)) –

5

Sen flatMap kullanmalıdır.

+0

Hızlı (ve iyi) cevabınız için teşekkür ederiz. Sonunda eksik bir parantezin olduğuna dikkat edin. İkinci soruda bir fikir edinmiş olursunuz – Vongo

1

Çalışması gerekir. Listeden bazı anahtarları kaybettiğinizi lütfen unutmayın.

Map<Integer, List<String>> mapFrom = new HashMap<>(); 
Map<String, Integer> mapTo = mapFrom.entrySet().stream() 
     .flatMap(integerListEntry -> integerListEntry.getValue() 
       .stream() 
       .map(listItem -> new AbstractMap.SimpleEntry<>(listItem, integerListEntry.getKey()))) 
     .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); 
+0

Bu işler, teşekkür ederim. Benim durumumdaki Listeden anahtarları kaybetmediğimizi, çünkü soruda belirttiğim gibi Liste değerlerinin benzersiz olduğunu (benzersiz olarak, İki Liste arasında asla çoğaltılamaz bir değer yoktur, belki bu ifade açık değildi). İkinci soruyla ilgili bir fikir de olsa çok takdir edilmiş olurdu! – Vongo

1

Bunu en basit şekilde yaparsınız. :))

mapFrom.forEach((key, values) -> values.forEach(value -> mapTo.put(value, key))); 
+0

Bu işler, teşekkür ederim. Ne yazık ki, aradığım şey bu değildi. Belki de daha önce “foreach” ile nasıl yapılacağını bildiğim ve “stream” i kullanmak istediğimi daha net bir şekilde açıklamalıydım. – Vongo

+0

@Vongo daha iyi söylediniz :)) –

İlgili konular