Bir Java Haritasındaki her bir girişi nasıl verimli bir şekilde yineleyebilirim?


3302

MapJava'da arabirimi uygulayan bir nesnem varsa ve içindeki her çift üzerinde yineleme yapmak istiyorsam, haritadan geçmenin en etkili yolu nedir?

Elemanların sıralaması arayüz için sahip olduğum harita uygulamasına bağlı olacak mı?


38
Java 8'de Lambda İfadesini kullanarak: stackoverflow.com/a/25616206/1503859
Nitin Mahesh

Yanıtlar:


5029
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

93
Bunu yaparsanız, Giriş Haritadaki iç içe bir Sınıf olduğu için çalışmaz. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2

266
içe aktarmayı "import java.util.Map.Entry;" olarak yazabilirsiniz. ve işe yarayacak.
jjujuma

55
@ Purferret Yineleyici kullanmak isteyebilmenizin tek nedeni, removeyöntemini çağırmanız gerektiğidir . Bu durumda, bu diğer cevap size nasıl yapılacağını gösterir. Aksi takdirde, yukarıdaki cevapta gösterildiği gibi gelişmiş döngü gitmenin yoludur.
assylias

102
Map.Entry formunun, iç sınıfı geçerli ad alanına aktarmaktan daha net olduğuna inanıyorum.
Josiah Yoder

31
Kullanabileceğinizi map.values()veya map.keySet()yalnızca değerler veya anahtarlar arasında geçiş yapmak istediğinizi unutmayın.
dguay

1215

Diğer cevapları özetlemek ve bildiklerimle birleştirmek için bunu yapmanın 10 ana yolunu buldum (aşağıya bakınız). Ayrıca, bazı performans testleri yazdım (aşağıdaki sonuçlara bakın). Örneğin, bir haritanın tüm anahtarlarının ve değerlerinin toplamını bulmak istiyorsak şunu yazabiliriz:

  1. Yineleyici ve Harita'yı kullanma

    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
  2. Kullanılması foreach ve Map.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
  3. Java 8'den forEach kullanma

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
  4. KeySet ve foreach öğelerini kullanma

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. KeySet ve yineleyiciyi kullanma

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
  6. Kullanılması için ve Map.Entry

    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
  7. Java 8 Stream API'sini kullanma

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Java 8 Stream API'yi paralel kullanma

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
  9. Kullanılması IterableMap arasındaApache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. Eclipse (CS) koleksiyonlarının MutableMap kullanma

    final long[] i = {0};
    mutableMap.forEachKeyValue((key, value) -> {
        i[0] += key + value;
    });

Performans testleri (mod = AverageTime, sistem = Windows 8.1 64 bit, Intel i7-4790 3.60 GHz, 16 GB)

  1. Küçük bir harita (100 element) için 0.308 puanı en iyisidir

    Benchmark                          Mode  Cnt  Score    Error  Units
    test3_UsingForEachAndJava8         avgt  10   0.308 ±  0.021  µs/op
    test10_UsingEclipseMap             avgt  10   0.309 ±  0.009  µs/op
    test1_UsingWhileAndMapEntry        avgt  10   0.380 ±  0.014  µs/op
    test6_UsingForAndIterator          avgt  10   0.387 ±  0.016  µs/op
    test2_UsingForEachAndMapEntry      avgt  10   0.391 ±  0.023  µs/op
    test7_UsingJava8StreamApi          avgt  10   0.510 ±  0.014  µs/op
    test9_UsingApacheIterableMap       avgt  10   0.524 ±  0.008  µs/op
    test4_UsingKeySetAndForEach        avgt  10   0.816 ±  0.026  µs/op
    test5_UsingKeySetAndIterator       avgt  10   0.863 ±  0.025  µs/op
    test8_UsingJava8StreamApiParallel  avgt  10   5.552 ±  0.185  µs/op
  2. 10000 elementli bir harita için 37.606 puanı en iyisidir

    Benchmark                           Mode   Cnt  Score      Error   Units
    test10_UsingEclipseMap              avgt   10    37.606 ±   0.790  µs/op
    test3_UsingForEachAndJava8          avgt   10    50.368 ±   0.887  µs/op
    test6_UsingForAndIterator           avgt   10    50.332 ±   0.507  µs/op
    test2_UsingForEachAndMapEntry       avgt   10    51.406 ±   1.032  µs/op
    test1_UsingWhileAndMapEntry         avgt   10    52.538 ±   2.431  µs/op
    test7_UsingJava8StreamApi           avgt   10    54.464 ±   0.712  µs/op
    test4_UsingKeySetAndForEach         avgt   10    79.016 ±  25.345  µs/op
    test5_UsingKeySetAndIterator        avgt   10    91.105 ±  10.220  µs/op
    test8_UsingJava8StreamApiParallel   avgt   10   112.511 ±   0.365  µs/op
    test9_UsingApacheIterableMap        avgt   10   125.714 ±   1.935  µs/op
  3. 100000 elementli bir harita için skor 1184.767 en iyisidir

    Benchmark                          Mode   Cnt  Score        Error    Units
    test1_UsingWhileAndMapEntry        avgt   10   1184.767 ±   332.968  µs/op
    test10_UsingEclipseMap             avgt   10   1191.735 ±   304.273  µs/op
    test2_UsingForEachAndMapEntry      avgt   10   1205.815 ±   366.043  µs/op
    test6_UsingForAndIterator          avgt   10   1206.873 ±   367.272  µs/op
    test8_UsingJava8StreamApiParallel  avgt   10   1485.895 ±   233.143  µs/op
    test5_UsingKeySetAndIterator       avgt   10   1540.281 ±   357.497  µs/op
    test4_UsingKeySetAndForEach        avgt   10   1593.342 ±   294.417  µs/op
    test3_UsingForEachAndJava8         avgt   10   1666.296 ±   126.443  µs/op
    test7_UsingJava8StreamApi          avgt   10   1706.676 ±   436.867  µs/op
    test9_UsingApacheIterableMap       avgt   10   3289.866 ±  1445.564  µs/op

Grafikler (harita boyutuna bağlı olarak performans testleri)

Resim açıklamasını buraya girin

Tablo (harita boyutuna bağlı olarak performans testleri)

          100     600      1100     1600     2100
test10    0.333    1.631    2.752    5.937    8.024
test3     0.309    1.971    4.147    8.147   10.473
test6     0.372    2.190    4.470    8.322   10.531
test1     0.405    2.237    4.616    8.645   10.707
test2     0.376    2.267    4.809    8.403   10.910
test7     0.473    2.448    5.668    9.790   12.125
test9     0.565    2.830    5.952   13.220   16.965
test4     0.808    5.012    8.813   13.939   17.407
test5     0.810    5.104    8.533   14.064   17.422
test8     5.173   12.499   17.351   24.671   30.403

Tüm testler GitHub'da .


9
@Viacheslav: çok güzel bir cevap. Sadece Java8 apilerinin benchmarkınızda lambdaları yakalayarak nasıl engellendiğini merak ediyorum ... (örneğin uzun süreleri long sum = 0; map.forEach( /* accumulate in variable sum*/);yakalar, örneğin sumsöyleyenden daha yavaş olabilir stream.mapToInt(/*whatever*/).sum. tezgaha.
GPI

17
8 testiniz yanlış. senkronize olmadan farklı değişkenlerden aynı değişkene erişir. AtomicIntegerSorunu çözmek için değiştirin .
talex

44
@ZhekaKozlov: şaşırtıcı derecede büyük hata değerlerine bakın. Bir test sonucu olduğu göz önünde x±egelen aralığı içinde bir sonuç olduğunu ima x-eiçin x+e(en hızlı sonuç, yani 1184.767±332.968arasında değişir) 852için 1518, ikinci yavaş (oysa 1706.676±436.867) arasında çalışan 1270ve 2144, sonuç yani hala örtüşme anlamlı. Şimdi, en yavaş sonuca bakın 3289.866±1445.564, bu da 1844ve birbirinden ayrılma anlamına geliyor ve bu test sonuçlarının anlamsız 4735olduğunu biliyorsunuz .
Holger

8
3 ana uygulamayı karşılaştırmaya ne dersiniz: HashMap, LinkedHashMap ve TreeMap?
Thierry

9
# 1 ve # 6 tam olarak aynı. whileBir fordöngüye karşı bir döngü kullanmak , yineleme için farklı bir teknik değildir. Ve testlerinizde aralarında böyle bir varyasyona sahip olduklarına şaşırdım - bu, testlerin test etmeyi planladığınız şeylerle ilgili olmayan dış faktörlerden düzgün bir şekilde izole edilmediğini gösterir.
ErikE

294

Java 8'de yeni lambdas özelliklerini kullanarak temiz ve hızlı bir şekilde yapabilirsiniz:

 Map<String,String> map = new HashMap<>();
 map.put("SomeKey", "SomeValue");
 map.forEach( (k,v) -> [do something with key and value] );

 // such as
 map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));

Derleyicinin türü kve vçıkarımı yapılacaktır ve kullanmaya gerek yokturMap.Entry artık.

Çantada keklik!


12
Bir harita ile ne yapmak istediğinize bağlı olarak, map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko

1
Lambda ifadeniz dışında bildirilen son olmayan değişkenlere forEach () içinden başvurmak istiyorsanız bu işe yaramaz ...
Chris

7
@Chris Doğru. Lambda dışından nihai olmayan değişkenleri etkili bir şekilde kullanmaya çalışırsanız çalışmaz .
Koordinatör

243

Evet, sipariş belirli Harita uygulamasına bağlıdır.

@ ScArcher2 daha zarif Java 1.5 sözdizimine sahiptir . 1.4'te böyle bir şey yapardım:

Iterator entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Entry thisEntry = (Entry) entries.next();
  Object key = thisEntry.getKey();
  Object value = thisEntry.getValue();
  // ...
}

41
(Iterator entry = myMap.entrySet (). İterator (); entry.hasNext ();) {...} Bu sözdizimiyle 'girişler' kapsamı yalnızca for döngüsüne indirilir .
jai

8
@jpredham kullanarak doğru olduğunu vardır forolarak yapı for (Entry e : myMap.entrySet)sen koleksiyonunu değiştirmesine izin vermez, ama @HanuAthena olarak örnek size verir çünkü, çalışması gerekir belirtilen Iteratorkapsamda. (Bir şeyi kaçırmadıkça ...)
12'de

1
IntelliJ bana hata veriyor Entry thisEntry = (Entry) entries.next();: tanımıyor Entry. Bu sahte kod başka bir şey için mi?
JohnK

1
@JohnK içe aktarmayı deneyin java.util.Map.Entry.
15'te

1
Bir tamsayı anahtarınız ve String anahtarınız varsa bu çözüm çalışmaz.

140

Bir harita üzerinden yineleme yapmak için tipik kod:

Map<String,Thing> map = ...;
for (Map.Entry<String,Thing> entry : map.entrySet()) {
    String key = entry.getKey();
    Thing thing = entry.getValue();
    ...
}

HashMapkanonik harita uygulamasıdır ve garanti vermez (veya üzerinde herhangi bir mutasyon işlemi yapılmazsa düzeni değiştirmemelidir). SortedMapanahtarların doğal sırasına göre girişleri veya Comparatorsağlanmışsa a döndürür . LinkedHashMapnasıl oluşturulduğuna bağlı olarak girişleri ekleme veya erişim sırasına göre döndürür. EnumMapgirişleri doğal tuş sırasına göre döndürür.

(Güncelleme: Bunun artık doğru olmadığını düşünüyorum. ) Not, IdentityHashMap entrySetyineleyici şu anda Map.Entryher öğede aynı örneği döndüren tuhaf bir uygulamaya sahip entrySet! Ancak, yeni bir yineleyici her geliştiğinde Map.Entrygüncellenir.


6
EnumMap ayrıca IdentityHashMap ile birlikte bu tuhaf davranışa sahiptir
Premraj

1
"LinkedHashMap, [...] erişim sırası [...] 'daki girdileri döndürür. Böylece öğelere eriştiğiniz sırayla erişir misiniz? Ya totolojik ya da kazı yapabilecek ilginç bir şey. ;-)
jpaugh

5
@jpaugh Yalnızca sayıma doğrudan erişim LinkedHashMap. Bunlar sayesinde iterator, spliterator, entrySetvb sırasını değiştirmez.
Tom Hawtin - çakmak hattı

1
1. olsaeğer ? 2. Son paragraf bir fırçalama işleminden yararlanabilir.
Peter Mortensen

122

Yineleyici ve jenerikler kullanma örneği:

Iterator<Map.Entry<String, String>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
  Map.Entry<String, String> entry = entries.next();
  String key = entry.getKey();
  String value = entry.getValue();
  // ...
}

14
IteratorKapsamını sınırlamak için bir for döngüsü koymalısınız .
Steve Kuo

@SteveKuo "Kapsamını sınırla" ile ne demek istiyorsun?
StudioWorks

12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Bu yapıyı kullanarak (değişkenin görünürlüğü) entrieskapsamını for döngüsüyle sınırlandırırız .
ComFreek

3
@ComFreek Oh, anlıyorum. Bunun çok önemli olduğunu bilmiyordum.
StudioWorks

102

Bu iki bölümlü bir soru:

Bir Harita'nın girişleri üzerinde nasıl yinelenir - @ ScArcher2 bunu mükemmel bir şekilde yanıtladı .

Yineleme sırası nedir - sadece kullanıyorsanız Map, sonra kesinlikle konuşursanız, sipariş garantisi yoktur . Bu nedenle, herhangi bir uygulamanın verdiği sıralamaya gerçekten güvenmemelisiniz. Ancak, SortedMaparayüz Maptam olarak aradığınızı genişletir ve sağlar - uygulamalar her zaman tutarlı bir sıralama düzeni sağlar.

NavigableMapbaşka bir yararlı uzantıdır - bu, SortedMapanahtar kümesindeki sıralı konumlarına göre girişleri bulmak için ek yöntemlerle birdir. Yani potansiyel olarak bu ilk etapta yineleme ihtiyacını kaldırabilir - Eğer özgü bulmak mümkün olabilir entrykullandığınız peşinde higherEntry, lowerEntry, ceilingEntry, veya floorEntryyöntemler. descendingMapYöntem bile size açık bir yöntem verir geçişi sırasını tersine çevirmek .


84

Harita üzerinde yineleme yapmanın birkaç yolu vardır.

Harita üzerinde bir milyon anahtar / değer çifti saklayarak harita üzerinde depolanan ortak bir veri seti için performanslarının karşılaştırılması ve harita üzerinde yinelenmesi.

1) entrySet()Her döngü için

for (Map.Entry<String,Integer> entry : testMap.entrySet()) {
    entry.getKey();
    entry.getValue();
}

50 milisaniye

2) keySet()Her döngü için

for (String key : testMap.keySet()) {
    testMap.get(key);
}

76 milisaniye

3) kullanma entrySet()ve yineleyici

Iterator<Map.Entry<String,Integer>> itr1 = testMap.entrySet().iterator();
while(itr1.hasNext()) {
    Map.Entry<String,Integer> entry = itr1.next();
    entry.getKey();
    entry.getValue();
}

50 milisaniye

4) kullanma keySet()ve yineleyici

Iterator itr2 = testMap.keySet().iterator();
while(itr2.hasNext()) {
    String key = itr2.next();
    testMap.get(key);
}

75 milisaniye

Referans verdim this link.


1
Çalışma süreleri, Java Microbenchmarking Kablo Demeti'ni kullanmayan makaleden alınır. Kodlar örneğin JIT derleyicisi tarafından tamamen optimize edilebildiğinden, bu nedenle süreler güvenilir değildir.
AlexB

59

Bunu yapmanın doğru yolu, kabul edilen cevabı en verimli olduğu şekilde kullanmaktır. Aşağıdaki kodu biraz daha temiz görünüyor.

for (String key: map.keySet()) {
   System.out.println(key + "/" + map.get(key));
}

15
Bu en iyi yaklaşım değildir, entrySet () yöntemini kullanmak çok daha verimlidir. Findbugs bayrak Bu kodu (bkz will findbugs.sourceforge.net/... )
Jeff Olson

6
@JeffOlson meh, pek değil. harita arama O (1) 'dir, bu nedenle her iki döngü de aynı şekilde davranır. Kuşkusuz, bir mikro kıyaslama biraz daha yavaş olacak ama bazen de bunu yapmak çünkü ben tekrar tekrar tür argümanları yazmaktan nefret ediyorum. Ayrıca bu muhtemelen performans darboğunuz olmayacaktır, bu yüzden kodu daha okunabilir hale getirirse gidin.
kritzikratzi

4
daha ayrıntılı olarak: O(1) = 2*O(1)büyük O gösteriminin tanımıdır. Biraz daha yavaş çalıştığı için haklısınız, ancak karmaşıklık açısından aynılar.
kritzikratzi

2
Çarpışma ya da değil, birkaç çarpışmanın önemli olmadığı anlamına geliyordu, sadece çarpışmalarınız varsa açıkçası farklı bir hikaye. yani oldukça küçük oluyorsun, ama evet, söylediğin doğru.
kritzikratzi

2
@Jeff Olson: “Büyük O” karmaşıklığının değişmeyen yorumlar, sadece sabit bir faktör olduğunda doğrudur. Yine de, benim için bir operasyonun bir saat mi yoksa iki saat mi sürdüğü önemli. Daha da önemlisi, bir faktörün yinelenmesi hiç bir arama yapmadığından faktörün olmadığı vurgulanmalıdır ; bu sadece tüm girdilerin doğrusal bir geçişidir. Buna karşılık, anahtar üzerinde yineleme yapmak ve anahtar başına bir arama yapmak, anahtar başına bir arama gerektirir, bu yüzden burada sıfır aramalara karşı n aramalardan bahsediyoruz , n boyutu . Yani faktör çok ötesinde …2entrySet()keySet()Map2
Holger

57

FYI, ayrıca kullanabilirsiniz map.keySet()ve map.values()sadece haritanın anahtarları / değerleri ile ilgileniyorsanız, diğeriyle değil.


42

İle Java 8 , Harita forEach ve lambda ifadesi kullanarak yineleme yapabilirsiniz,

map.forEach((k, v) -> System.out.println((k + ":" + v)));

41

İle Eclipse Koleksiyonları şunları kullanırsınız forEachKeyValueüzerinde yöntemi MapIterableile devralınan arayüzü MutableMapve ImmutableMaparayüzleri ve bunların uygulamaları.

MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue((key, value) -> result.add(key + value));
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Anonim bir iç sınıf kullanarak, kodu aşağıdaki gibi yazabilirsiniz:

final MutableBag<String> result = Bags.mutable.empty();
MutableMap<Integer, String> map = Maps.mutable.of(1, "One", 2, "Two", 3, "Three");
map.forEachKeyValue(new Procedure2<Integer, String>()
{
    public void value(Integer key, String value)
    {
        result.add(key + value);
    }
});
Assert.assertEquals(Bags.mutable.of("1One", "2Two", "3Three"), result);

Not: Eclipse Collections için bir komisyoncuyum.


37

Lambda İfade Java 8

Java 1.8'de (Java 8) , Yinelenebilir Arabirimdeki yineleyicilere benzeyen Toplama işlemlerinden ( Akış işlemleri ) forEach yöntemi kullanılarak bu çok daha kolay hale geldi .

Sadece koduna açıklamada altında yapıştırın ve yeniden adlandırmak kopya HashMap değişkeni hm anahtar-değer çifti dışarı yazdırmak için HashMap değişkene.

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
 *     Logic to put the Key,Value pair in your HashMap hm
 */

// Print the key value pair in one line.

hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));

// Just copy and paste above line to your code.

Aşağıda Lambda Expression kullanarak denedim örnek kod . Bu şeyler çok havalı. Denemek gerekir.

HashMap<Integer, Integer> hm = new HashMap<Integer, Integer>();
    Random rand = new Random(47);
    int i = 0;
    while(i < 5) {
        i++;
        int key = rand.nextInt(20);
        int value = rand.nextInt(50);
        System.out.println("Inserting key: " + key + " Value: " + value);
        Integer imap = hm.put(key, value);
        if( imap == null) {
            System.out.println("Inserted");
        } else {
            System.out.println("Replaced with " + imap);
        }               
    }

    hm.forEach((k, v) -> System.out.println("key: " + k + " value:" + v));

Output:

Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11

Ayrıca biri için Spliterator kullanabilirsiniz .

Spliterator sit = hm.entrySet().spliterator();

GÜNCELLEME


Oracle Docs belgelerine bağlantılar dahil. Lambda hakkında daha fazla bilgi için bu bağlantıya gidin ve Toplam İşlemleri okumalı ve Ayırıcı için bu bağlantıya gidin .


36

Teorik olarak, en etkili yol haritanın hangi uygulamasına bağlı olacaktır. Bunu yapmanın resmi yolu , her biri bir anahtar ve bir değer ( ve ) içeren bir map.entrySet()dizi döndüren çağırmaktır .Map.Entryentry.getKey()entry.getValue()

Özyinelemeli bir uygulamada, kullansanız da map.keySet(), map.entrySet()başka bir şeyde fark yaratabilir . Ama kimsenin böyle yazmasının bir sebebini düşünemiyorum. Büyük olasılıkla yaptığınız performansla hiçbir fark yaratmaz.

Ve evet, emir uygulamaya bağlıdır - ayrıca (muhtemelen) yerleştirme sırası ve diğer kontrol edilmesi zor faktörler.

valueSet()Orijinal olarak yazdım ama elbette entrySet()cevap.


36

Java 8

Lambda ifadesiniforEach kabul eden bir yöntemimiz var . Akış API'larımız da var . Bir haritayı düşünün:

Map<String,String> sample = new HashMap<>();
sample.put("A","Apple");
sample.put("B", "Ball");

Anahtarlar üzerinde yineleme:

sample.keySet().forEach((k) -> System.out.println(k));

Değerler üzerinde yineleme:

sample.values().forEach((v) -> System.out.println(v));

Girişler üzerinde yineleme (forEach ve Streams kullanma):

sample.forEach((k,v) -> System.out.println(k + ":" + v)); 
sample.entrySet().stream().forEach((entry) -> {
            Object currentKey = entry.getKey();
            Object currentValue = entry.getValue();
            System.out.println(currentKey + ":" + currentValue);
        });

Akarsuların avantajı, istediğimiz takdirde kolayca paralelleştirilebilmeleridir. Sadece yukarıdakilerin parallelStream()yerine kullanmamız gerekiyor stream().

forEachOrderedforEachakışlarla vs ? forEach(Tanımlı ise) karşılaşma talimatına uymaya olarak doğada doğal olarak belirli olmayan vermezse forEachOrderedyok. Bu yüzden forEachsiparişin korunacağını garanti etmez. Ayrıca daha fazlası için bunu kontrol edin .


33

Java 8:

Lambda ifadelerini kullanabilirsiniz:

myMap.entrySet().stream().forEach((entry) -> {
    Object currentKey = entry.getKey();
    Object currentValue = entry.getValue();
});

Daha fazla bilgi için bunu takip edin .


@injecteer: Lambda ifadelerinin güdüsü görünüyor
humblerookie

9
Bir haritayı tekrarlamak istiyorsanız bir akışa ihtiyacınız yoktur. myMap.forEach( (currentKey,currentValue) -> /* action */ );çok daha özlü.
Holger

29

Java 1.4 ile bunu deneyin:

for( Iterator entries = myMap.entrySet().iterator(); entries.hasNext();){

  Entry entry = (Entry) entries.next();

  System.out.println(entry.getKey() + "/" + entry.getValue());

  //...
}

26

Haritada kişi keysve / veya valuesve / veya yineleme both (e.g., entrySet) ilgilendiğine bağlıdır.

  1. keys -> keySet()Haritanın içinden tekrarlayın :

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. values -> values()Haritanın içinden tekrarlayın :

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. both -> entrySet()Haritanın içinden tekrarlayın :

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        //your Business logic...
    }

Dahası, HashMap ile Yinelemenin 3 fark yolu vardır. Onlar aşağıdaki gibidir__

//1.
for (Map.Entry entry : hm.entrySet()) {
    System.out.print("key,val: ");
    System.out.println(entry.getKey() + "," + entry.getValue());
}

//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
    Integer key = (Integer)iter.next();
    String val = (String)hm.get(key);
    System.out.println("key,val: " + key + "," + val);
}

//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    Integer key = (Integer)entry.getKey();
    String val = (String)entry.getValue();
    System.out.println("key,val: " + key + "," + val);
}

24

Java 8 ile en kompakt:

map.entrySet().forEach(System.out::println);

21

Genel bir türlenmemiş Haritanız varsa kullanabilirsiniz:

Map map = new HashMap();
for (Map.Entry entry : ((Set<Map.Entry>) map.entrySet())) {
    System.out.println(entry.getKey() + "/" + entry.getValue());
}

20
public class abcd{
    public static void main(String[] args)
    {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Integer key:testMap.keySet()) {
            String value=testMap.get(key);
            System.out.println(value);
        }
    }
}

VEYA

public class abcd {
    public static void main(String[] args)
    {
       Map<Integer, String> testMap = new HashMap<Integer, String>();
        testMap.put(10, "a");
        testMap.put(20, "b");
        testMap.put(30, "c");
        testMap.put(40, "d");
        for (Entry<Integer, String> entry : testMap.entrySet()) {
            Integer key=entry.getKey();
            String value=entry.getValue();
        }
    }
}

17

Java'da Harita arayüzünü uygulayan bir nesnem varsa ve içerdiği her bir çifti yinelemek istersem, haritadan geçmenin en etkili yolu nedir?

Anahtarları döngü etkinliği uygulamanız için bir öncelikse, anahtarları Mapistediğiniz sırada tutan bir uygulama seçin .

Elemanların sıralaması arayüz için sahip olduğum harita uygulamasına bağlı olacak mı?

Evet kesinlikle.

  • Bazı Mapuygulamalar belirli bir yineleme sırası vaat ederken, diğerleri bunu önermez.
  • MapAnahtar / değer çiftlerinin farklı düzenini korumak için farklı uygulamalar .

MapJava 11 ile birlikte gelen çeşitli uygulamaları özetleyerek oluşturduğum bu tabloya bakın. Özel olarak, yineleme sırası sütununa dikkat edin . Yakınlaştırmak için tıklayın / dokunun.

Java 11'deki harita uygulamalarının özelliklerini karşılaştırma tablosu

Bir siparişi koruyan dört Mapuygulama olduğunu görebilirsiniz :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap arayüz

Bunlardan ikisi NavigableMaparayüzü uyguluyor : TreeMap& ConcurrentSkipListMap.

Eski SortedMaparayüz, yeni NavigableMaparayüz tarafından etkili bir şekilde yerini alır . Ancak, yalnızca eski arayüzü uygulayan üçüncü taraf uygulamaları bulabilirsiniz.

Doğal düzen

MapÇiftlerini anahtarın “doğal düzeni” ile düzenlenmiş tutmak istiyorsanız , TreeMapveya tuşlarını kullanın ConcurrentSkipListMap. “Doğal düzen” terimi, uygulanan anahtarların sınıfı anlamına gelir Comparable. compareToSıralamada karşılaştırma için yöntem tarafından döndürülen değer kullanılır.

Özel sipariş

Anahtarlarınızın sıralı bir sıralamanın korunmasında kullanılması için özel bir sıralama rutini belirtmek istiyorsanız, anahtarlarınızın Comparatorsınıfına uygun bir uygulama geçirin. Ya TreeMapda ConcurrentSkipListMap, ya geçerek kullanın Comparator.

Orijinal kampanya siparişi

Haritanızın çiftlerinin, haritaya eklediğiniz orijinal sıralarında tutulmasını istiyorsanız, düğmesini kullanın LinkedHashMap.

Numaralandırma tanımı sırası

Anahtarlarınız gibi DayOfWeekya Monthda anahtarlarınız gibi bir numaralandırma kullanıyorsanız, EnumMapsınıfı kullanın . Bu sınıf çok az bellek kullanmak ve çok hızlı çalışmak için son derece optimize edilmiş olmakla kalmaz , aynı zamanda çiftlerinizi numaralandırma tarafından tanımlanan sırayla korur. Örneğin DayOfWeek, anahtarı DayOfWeek.MONDAYilk kez yinelendiğinde DayOfWeek.SUNDAYbulunacak ve anahtarı son olacaktır.

Dikkat edilecek diğer noktalar

Bir Mapuygulama seçerken aşağıdakileri de göz önünde bulundurun:

  • NULL. Bazı uygulamalar NULL değerini anahtar ve / veya değer olarak yasaklar / kabul eder.
  • Eşzamanlılık. Haritayı iş parçacıkları arasında değiştiriyorsanız, eşzamanlılığı destekleyen bir uygulama kullanmalısınız. Veya haritayı Collections::synchronizedMap(daha az tercih edilir) ile sarın .

Bu hususların her ikisi de yukarıdaki grafik tablosunda ele alınmıştır.


Partiye de geç gelen bir cevap için geç yorum (ama çok bilgilendirici). Bahsettiğim için +1 benden EnumMap, çünkü ilk kez duydum. Muhtemelen bunun işe yarayabileceği birçok durum vardır.
user991710

15

Sıralama her zaman belirli harita uygulamasına bağlı olacaktır. Java 8'i kullanarak aşağıdakilerden birini kullanabilirsiniz:

map.forEach((k,v) -> { System.out.println(k + ":" + v); });

Veya:

map.entrySet().forEach((e) -> {
            System.out.println(e.getKey() + " : " + e.getValue());
        });

Sonuç aynı olacaktır (aynı sıralama). Aynı sırayı almak için harita tarafından desteklenen inputSet. İkincisi lambdas kullanmanıza izin verdiği için kullanışlıdır, örneğin yalnızca 5'ten büyük Integer nesnelerini yazdırmak istiyorsanız:

map.entrySet()
    .stream()
    .filter(e-> e.getValue() > 5)
    .forEach(System.out::println);

Aşağıdaki kod, LinkedHashMap ve normal HashMap (örnek) üzerinden yinelemeyi gösterir. Sırada fark göreceksiniz:

public class HMIteration {


    public static void main(String[] args) {
        Map<Object, Object> linkedHashMap = new LinkedHashMap<>();
        Map<Object, Object> hashMap = new HashMap<>();

        for (int i=10; i>=0; i--) {
            linkedHashMap.put(i, i);
            hashMap.put(i, i);
        }

        System.out.println("LinkedHashMap (1): ");
        linkedHashMap.forEach((k,v) -> { System.out.print(k + " (#="+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nLinkedHashMap (2): ");

        linkedHashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });


        System.out.println("\n\nHashMap (1): ");
        hashMap.forEach((k,v) -> { System.out.print(k + " (#:"+k.hashCode() + "):" + v + ", "); });

        System.out.println("\nHashMap (2): ");

        hashMap.entrySet().forEach((e) -> {
            System.out.print(e.getKey() + " : " + e.getValue() + ", ");
        });
    }
}

LinkedHashMap (1):

10 (# = 10): 10, 9 (# = 9): 9, 8 (# = 8): 8, 7 (# = 7): 7, 6 (# = 6): 6, 5 (# = 5 ): 5, 4 (# = 4): 4, 3 (# = 3): 3, 2 (# = 2): 2, 1 (# = 1): 1, 0 (# = 0): 0,

LinkedHashMap (2):

10: 10, 9: 9, 8: 8, 7: 7, 6: 6, 5: 5, 4: 4, 3: 3, 2: 2, 1: 1, 0: 0,

HashMap (1):

0 (#: 0): 0, 1 (#: 1): 1, 2 (#: 2): 2, 3 (#: 3): 3, 4 (#: 4): 4, 5 (#: 5 ): 5, 6 (#: 6): 6, 7 (#: 7): 7, 8 (#: 8): 8, 9 (#: 9): 9, 10 (#: 10): 10,

HashMap (2):

0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10,


14
    Iterator iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry element = (Map.Entry)it.next();
        LOGGER.debug("Key: " + element.getKey());
        LOGGER.debug("value: " + element.getValue());    
    }

14

Java 8 kullanın:

map.entrySet().forEach(entry -> System.out.println(entry.getValue()));

13

Bunu jenerikler kullanarak yapabilirsiniz:

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<Integer, Integer> entry = entries.next();
    System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}

12

Bir Harita üzerinde etkili bir yinelemeli çözüm, Java 5'ten Java 7'ye bir 'her biri için' döngüsüdür.

for (String key : phnMap.keySet()) {
    System.out.println("Key: " + key + " Value: " + phnMap.get(key));
}

Java 8'den bir Harita üzerinde yineleme yapmak için bir lambda ifadesi kullanabilirsiniz. Geliştirilmiş bir 'forEach'

phnMap.forEach((k,v) -> System.out.println("Key: " + k + " Value: " + v));

Eğer lambda için bir koşul yazmak istiyorsanız, bunu şöyle yazabilirsiniz:

phnMap.forEach((k,v)->{
    System.out.println("Key: " + k + " Value: " + v);
    if("abc".equals(k)){
        System.out.println("Hello abc");
    }
});

10
           //Functional Oprations
            Map<String, String> mapString = new HashMap<>();
            mapString.entrySet().stream().map((entry) -> {
                String mapKey = entry.getKey();
                return entry;
            }).forEach((entry) -> {
                String mapValue = entry.getValue();
            });

            //Intrator
            Map<String, String> mapString = new HashMap<>();
            for (Iterator<Map.Entry<String, String>> it = mapString.entrySet().iterator(); it.hasNext();) {
                Map.Entry<String, String> entry = it.next();
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();
            }

            //Simple for loop
            Map<String, String> mapString = new HashMap<>();
            for (Map.Entry<String, String> entry : mapString.entrySet()) {
                String mapKey = entry.getKey();
                String mapValue = entry.getValue();

            }

10

Bunu yapmanın birçok yolu var. Aşağıda birkaç basit adım bulunmaktadır:

Bir Haritanız olduğunu varsayalım:

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

Ardından, harita öğeleri üzerinde yineleme yapmak için aşağıdaki gibi bir şey yapabilirsiniz.

// ********** Using an iterator ****************
Iterator<Entry<String, Integer>> me = m.entrySet().iterator();
while(me.hasNext()){
    Entry<String, Integer> pair = me.next();
    System.out.println(pair.getKey() + ":" + pair.getValue());
}

// *********** Using foreach ************************
for(Entry<String, Integer> me : m.entrySet()){
    System.out.println(me.getKey() + " : " + me.getValue());
}

// *********** Using keySet *****************************
for(String s : m.keySet()){
    System.out.println(s + " : " + m.get(s));
}

// *********** Using keySet and iterator *****************
Iterator<String> me = m.keySet().iterator();
while(me.hasNext()){
    String key = me.next();
    System.out.println(key + " : " + m.get(key));
}

9
package com.test;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Test {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("ram", "ayodhya");
        map.put("krishan", "mathura");
        map.put("shiv", "kailash");

        System.out.println("********* Keys *********");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key);
        }

        System.out.println("********* Values *********");
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        System.out.println("***** Keys and Values (Using for each loop) *****");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out.println("***** Keys and Values (Using while loop) *****");
        Iterator<Entry<String, String>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
                    .next();
            System.out.println("Key: " + entry.getKey() + "\t Value: "
                    + entry.getValue());
        }

        System.out
                .println("** Keys and Values (Using java 8 using lambdas )***");
        map.forEach((k, v) -> System.out
                .println("Key: " + k + "\t value: " + v));
    }
}
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.