ฉันจะวนซ้ำแต่ละรายการอย่างมีประสิทธิภาพใน Java Map ได้อย่างไร


3302

หากฉันมีวัตถุที่ใช้Mapอินเทอร์เฟซใน Java และฉันต้องการย้ำผ่านทุกคู่ที่อยู่ในนั้นวิธีที่มีประสิทธิภาพที่สุดในการผ่านแผนที่คืออะไร?

การเรียงลำดับองค์ประกอบจะขึ้นอยู่กับการใช้แผนที่เฉพาะที่ฉันมีสำหรับอินเทอร์เฟซหรือไม่


38
ใน Java 8 โดยใช้ Lambda Expression: stackoverflow.com/a/25616206/1503859
Nitin Mahesh

คำตอบ:


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

93
ถ้าคุณทำเช่นนั้นมันจะไม่ทำงานเนื่องจาก Entry เป็น Class ที่ซ้อนกันใน Map java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2

266
คุณสามารถเขียนการนำเข้าเป็น "import java.util.Map.Entry;" และมันจะทำงาน
jjujuma

55
@Pureferret เหตุผลเดียวที่คุณอาจต้องการใช้ตัววนซ้ำคือถ้าคุณจำเป็นต้องเรียกremoveใช้เมธอด หากเป็นกรณีนี้คำตอบอื่น ๆจะแสดงวิธีการใช้งาน มิฉะนั้นการวนซ้ำที่เพิ่มขึ้นตามที่แสดงในคำตอบข้างต้นเป็นวิธีที่จะไป
assylias

102
ฉันเชื่อว่ารูปแบบ Map.Entry ชัดเจนกว่าการนำเข้าชั้นในสู่ namespace ปัจจุบัน
Josiah Yoder

31
โปรดทราบว่าคุณสามารถใช้map.values()หรือmap.keySet()หากคุณต้องการวนซ้ำค่าหรือปุ่มเท่านั้น
dguay

1215

เพื่อสรุปคำตอบอื่น ๆ และรวมกับสิ่งที่ฉันรู้ฉันพบ 10 วิธีหลักในการทำเช่นนี้ (ดูด้านล่าง) นอกจากนี้ฉันเขียนการทดสอบประสิทธิภาพ (ดูผลลัพธ์ด้านล่าง) ตัวอย่างเช่นหากเราต้องการหาผลรวมของปุ่มและค่าทั้งหมดของแผนที่เราสามารถเขียน:

  1. ใช้ตัววนซ้ำและMap.Entry

    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. ใช้foreachและMap.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
  3. ใช้forEachจาก Java 8

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
  4. การใช้ชุดคีย์และforeach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. การใช้ชุดคีย์และตัววนซ้ำ

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
  6. ใช้สำหรับและ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

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. การใช้ Java 8 Stream API แบบขนาน

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
  9. ใช้IterableMapของApache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. การใช้MutableMapของ Eclipse (CS)

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

การทดสอบประสิทธิภาพ (โหมด = AverageTime, system = Windows 8.1 64- บิต, Intel i7-4790 3.60 GHz, 16 GB)

  1. สำหรับแผนที่ขนาดเล็ก (100 องค์ประกอบ) คะแนน 0.308 ดีที่สุด

    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. สำหรับแผนที่ที่มีองค์ประกอบ 10,000 รายการคะแนน 37.606 นั้นดีที่สุด

    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. สำหรับแผนที่ที่มีองค์ประกอบ 100,000 คะแนน 1184.767 ดีที่สุด

    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

กราฟ (การทดสอบประสิทธิภาพขึ้นอยู่กับขนาดแผนที่)

ป้อนคำอธิบายภาพที่นี่

ตาราง (การทดสอบประสิทธิภาพขึ้นอยู่กับขนาดแผนที่)

          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

การทดสอบทั้งหมดที่อยู่บนGitHub


9
@Viacheslav: คำตอบที่ดีมาก เพียงแค่สงสัยว่า Java8 apis ถูกขัดขวางในเกณฑ์มาตรฐานของคุณโดยการจับ lambdas ... (เช่นlong sum = 0; map.forEach( /* accumulate in variable sum*/);จับsumยาวซึ่งอาจช้ากว่าพูดstream.mapToInt(/*whatever*/).sumตัวอย่างแน่นอนคุณไม่สามารถหลีกเลี่ยงการจับรัฐ แต่นั่นอาจเป็นเหตุผลที่นอกจากนี้ ถึงม้านั่ง
GPI

17
การทดสอบ 8 ข้อของคุณผิด มันเข้าถึงตัวแปรเดียวกันจากกระทู้ที่แตกต่างกันโดยไม่มีการประสาน เปลี่ยนเป็นAtomicIntegerแก้ปัญหา
talex

44
@ZhekaKozlov: ดูที่ค่าความผิดพลาดที่ใหญ่มาก พิจารณาว่าผลการทดสอบของการx±eบอกเป็นนัยว่ามีผลภายในช่วงเวลาจากx-eถึงx+eดังนั้นผลลัพธ์ที่เร็วที่สุด ( 1184.767±332.968) ช่วงจาก852ไปถึง1518ในขณะที่ช้าที่สุดที่สอง ( 1706.676±436.867) ทำงานระหว่าง1270และ2144ดังนั้นผลยังคงทับซ้อนกันอย่างมีนัยสำคัญ ตอนนี้ดูที่ผลที่ช้าที่สุด, 3289.866±1445.564ซึ่งหมายถึงการแยกทางระหว่าง1844และ4735และคุณรู้ว่าผลการทดสอบเหล่านี้มีความหมาย
Holger

8
สิ่งที่เกี่ยวกับการเปรียบเทียบการใช้งานหลัก 3 แบบ: HashMap, LinkedHashMap และ TreeMap
ธีรี่ร์

9
# 1 และ # 6 เหมือนกันทุกประการ การใช้whilevs. การforวนซ้ำไม่ใช่เทคนิคที่แตกต่างกันสำหรับการวนซ้ำ และฉันประหลาดใจที่พวกเขามีการเปลี่ยนแปลงดังกล่าวระหว่างการทดสอบของคุณ - ซึ่งแสดงว่าการทดสอบนั้นไม่ได้แยกออกจากปัจจัยภายนอกที่ไม่เกี่ยวข้องกับสิ่งที่คุณตั้งใจจะทำการทดสอบอย่างเหมาะสม
ErikE

294

ใน Java 8 คุณสามารถทำความสะอาดและรวดเร็วด้วยฟีเจอร์ lambdas ใหม่:

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

ประเภทของkและvจะสรุปโดยคอมไพเลอร์และไม่จำเป็นต้องใช้Map.Entryอีกต่อไป

ง่าย peasy!


12
ขึ้นอยู่กับสิ่งที่คุณต้องการทำกับแผนที่คุณยังสามารถใช้ stream API ในรายการที่ส่งคืนโดยmap.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko

1
สิ่งนี้จะไม่ทำงานหากคุณต้องการอ้างอิงตัวแปรที่ไม่ใช่ค่าสุดท้ายที่ประกาศนอกนิพจน์แลมบ์ดาของคุณจากภายใน forEach () ...
Chris

7
@Chris ถูกต้อง มันจะไม่ทำงานถ้าคุณพยายามใช้ตัวแปรที่ไม่ใช่ขั้นสุดท้ายอย่างมีประสิทธิภาพจากนอกแลมบ์ดา
ผู้ประสานงาน

243

ใช่ลำดับขึ้นอยู่กับการใช้งานแผนที่เฉพาะ

@ ScArcher2 มีไวยากรณ์ Java 1.5 ที่หรูหรากว่า ใน 1.4 ฉันจะทำสิ่งนี้:

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

41
ต้องการ for-loop มากกว่าในขณะที่ .. สำหรับ (Iterator รายการ = myMap.entrySet (). iterator (); entry.hasNext ();) {... } ด้วยไวยากรณ์นี้ขอบเขต 'entry' จะลดลงเป็น for loop เท่านั้น .
ใจ

8
@jpredham คุณมีสิทธิ์ที่จะใช้การforสร้างตามที่for (Entry e : myMap.entrySet)จะไม่อนุญาตให้คุณแก้ไขการเก็บ แต่ตัวอย่างเช่น @HanuAthena กล่าวถึงมันควรจะทำงานได้เพราะมันให้คุณIteratorในขอบเขต (ยกเว้นกรณีที่ฉันขาดอะไรบางอย่าง ... )
pkaeding

1
IntelliJ จะให้ข้อผิดพลาดของฉันเกี่ยวกับEntry thisEntry = (Entry) entries.next();: Entryไม่รู้จัก pseudocode นั้นมีอย่างอื่นหรือไม่?
JohnK

1
@JohnK java.util.Map.Entryลองนำเข้า
pkaeding

1
วิธีนี้จะไม่ทำงานหากคุณมีคีย์จำนวนเต็มและคีย์สตริง

140

รหัสทั่วไปสำหรับวนซ้ำบนแผนที่คือ:

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

HashMapเป็นการใช้งานแผนที่แบบบัญญัติและไม่รับประกัน (หรือแม้ว่าจะไม่ควรเปลี่ยนลำดับหากไม่มีการดำเนินการกลายพันธุ์ใด ๆ ก็ตาม) SortedMapจะส่งคืนรายการตามลำดับโดยธรรมชาติของคีย์หรือ a Comparatorหากมีการจัดเตรียมไว้ LinkedHashMapจะส่งคืนรายการในลำดับการแทรกหรือคำสั่งการเข้าถึงโดยขึ้นอยู่กับวิธีการสร้างEnumMapส่งคืนรายการตามลำดับคีย์โดยธรรมชาติ

(อัปเดต: ฉันคิดว่านี่ไม่เป็นความจริงอีกต่อไป ) หมายเหตุIdentityHashMap entrySetขณะนี้ตัววนซ้ำมีการใช้งานที่แปลกประหลาดซึ่งจะส่งคืนMap.Entryอินสแตนซ์เดียวกันสำหรับทุกรายการในentrySet! อย่างไรก็ตามทุกครั้งที่ตัววนซ้ำตัวใหม่เลื่อนไปที่การMap.Entryอัพเดต


6
EnumMap ยังมีพฤติกรรมที่แปลกประหลาดนี้พร้อมกับ IdentityHashMap
Premraj

1
"LinkedHashMap จะส่งคืนรายการใน [... ] access-order [... ]" ... เพื่อให้คุณเข้าถึงองค์ประกอบตามลำดับที่คุณเข้าถึงหรือไม่ ไม่ว่าจะเป็นเรื่องธรรมดาหรืออะไรที่น่าสนใจซึ่งอาจใช้การพูดนอกเรื่อง ;-)
jpaugh

5
@jpaugh เข้าถึงเฉพาะการLinkedHashMapนับโดยตรง ผ่านผู้iterator, spliterator, entrySetฯลฯ ไม่แก้ไขคำสั่ง
Tom Hawtin - tackline

1
1. แม้ว่าถ้า ? 2. ย่อหน้าสุดท้ายอาจได้รับประโยชน์จากการปัดขึ้น
Peter Mortensen

121

ตัวอย่างการใช้ตัววนซ้ำและชื่อสามัญ:

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
คุณควรใส่Iteratorใน for for loop เพื่อ จำกัด ขอบเขต
Steve Kuo

@SteveKuo คุณหมายความว่าอย่างไร "จำกัด ขอบเขต"?
StudioWorks

12
for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }@StudioWorks โดยการใช้โครงสร้างนั้นเรา จำกัด ขอบเขตของ (การมองเห็นของตัวแปร) entriesไปยัง for for loop
ComFreek

3
@ComFreek โอ้ฉันเห็นแล้ว ไม่รู้ว่ามันสำคัญมากขนาดนั้น
StudioWorks

102

นี่เป็นคำถามสองส่วน:

วิธีการวนซ้ำในรายการแผนที่ - @ ScArcher2 ได้ตอบว่าสมบูรณ์แบบ

คำสั่งของการทำซ้ำคืออะไร - ถ้าคุณเป็นเพียงการใช้Mapแล้วพูดอย่างเคร่งครัดมีการค้ำประกันไม่มีการสั่งซื้อ ดังนั้นคุณไม่ควรพึ่งพาการสั่งซื้อที่ได้รับจากการใช้งานจริง ๆ อย่างไรก็ตามSortedMapอินเทอร์เฟซจะขยายMapและให้สิ่งที่คุณกำลังมองหาอยู่ - การใช้งานจะเป็นการเรียงลำดับที่สอดคล้องกัน

NavigableMapเป็นอีกหนึ่งส่วนขยายที่มีประโยชน์ - นี่คือSortedMapวิธีการเพิ่มเติมสำหรับการค้นหารายการตามตำแหน่งที่สั่งในชุดคีย์ ดังนั้นอาจเกิดขึ้นนี้สามารถลบความจำเป็นในการทำซ้ำในสถานที่แรก - คุณอาจจะสามารถที่จะหาเฉพาะentryคุณหลังจากใช้higherEntry, lowerEntry, ceilingEntryหรือfloorEntryวิธีการ descendingMapวิธีแม้จะช่วยให้คุณมีวิธีการที่ชัดเจนของการย้อนกลับเพื่อข้ามผ่าน


84

มีหลายวิธีในการย้ำบนแผนที่

นี่คือการเปรียบเทียบการแสดงของพวกเขาสำหรับชุดข้อมูลทั่วไปที่จัดเก็บในแผนที่โดยจัดเก็บคู่ค่าคีย์ล้านในแผนที่และจะวนซ้ำบนแผนที่

1) ใช้entrySet()ในสำหรับแต่ละวง

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

50 มิลลิวินาที

2) ใช้keySet()ในสำหรับแต่ละวง

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

76 มิลลิวินาที

3) การใช้entrySet()และตัววนซ้ำ

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

50 มิลลิวินาที

4) การใช้keySet()และตัววนซ้ำ

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

75 มิลลิวินาที

this linkฉันได้เรียกว่า


1
เวลารันถูกนำมาจากบทความซึ่งไม่ได้ใช้ Java Microbenchmarking Harness เวลาจึงไม่น่าเชื่อถือตัวอย่างเช่นโค้ดได้รับการปรับให้เหมาะสมโดยคอมไพเลอร์ JIT
AlexB

59

วิธีที่ถูกต้องในการทำเช่นนี้คือใช้คำตอบที่ยอมรับเนื่องจากมีประสิทธิภาพมากที่สุด ฉันพบว่ารหัสต่อไปนี้ดูสะอาดขึ้นเล็กน้อย

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

15
นี่ไม่ใช่วิธีที่ดีที่สุด แต่มีประสิทธิภาพมากกว่าที่จะใช้ entrySet () Findbugs จะตั้งค่าสถานะรหัสนี้ (ดูfindbugs.sourceforge.net/ … )
Jeff Olson

6
@JeffOlson meh ไม่ได้จริงๆ การค้นหาแผนที่คือ O (1) ดังนั้นลูปทั้งสองจะทำงานในลักษณะเดียวกัน ยอมรับว่ามันจะช้าลงเล็กน้อยในเกณฑ์มาตรฐานขนาดเล็ก แต่บางครั้งฉันก็ทำเช่นนี้เพราะฉันเกลียดการเขียนอาร์กิวเมนต์ประเภทซ้ำแล้วซ้ำอีก นอกจากนี้มันอาจจะไม่เป็นปัญหาคอขวดของคุณเลยดังนั้นลองทำดูถ้ามันทำให้โค้ดอ่านง่ายขึ้น
kritzikratzi

4
รายละเอียดเพิ่มเติม: O(1) = 2*O(1)สวยมากนิยามของสัญกรณ์ O ใหญ่ คุณพูดถูกมันช้าลงเล็กน้อย แต่ในแง่ของความซับซ้อนมันเหมือนกัน
kritzikratzi

2
โดยการชนหรือไม่ฉันหมายความว่ามันไม่สำคัญว่าคุณจะมีการชนกันเพียงเล็กน้อยแน่นอนว่ามันเป็นเรื่องที่แตกต่างถ้าคุณมีการชน ดังนั้นคุณเป็นคนใจจดใจจ่อ แต่อ๋อสิ่งที่คุณพูดนั้นเป็นความจริง
kritzikratzi

2
@ Jeff Olson: ความคิดเห็นที่ความซับซ้อน“ บิ๊กโอ” ไม่เปลี่ยนแปลงเมื่อมีเพียงปัจจัยคงที่ถูกต้อง สำหรับฉันแล้วมันสำคัญว่าการผ่าตัดจะใช้เวลาหนึ่งชั่วโมงหรือสองชั่วโมง ที่สำคัญกว่านั้นจะต้องมีการเน้นว่าปัจจัยนั้นไม่ได้ 2เนื่องจากการวนซ้ำไปมาentrySet()นั้นไม่ได้มีการค้นหาเลย มันเป็นแค่การข้ามเส้นตรงของทุกรายการ ในทางตรงกันข้ามการทำซ้ำกว่าkeySet()และประสิทธิภาพการค้นหาต่อหมีที่สำคัญหนึ่งในการค้นหาต่อคีย์ดังนั้นเรากำลังพูดถึงการค้นหาศูนย์เทียบกับnการค้นหาที่นี่nMapเป็นขนาดของ ดังนั้นปัจจัยจึงเกินกว่าจะเป็นเช่นนั้น2...
Holger

57

FYI คุณสามารถใช้map.keySet()และmap.values()หากคุณสนใจเฉพาะคีย์ / ค่าของแผนที่เท่านั้น


42

ด้วยJava 8คุณสามารถทำซ้ำ Map โดยใช้ forEach และ lambda expression

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

41

ด้วยEclipse Collectionsคุณจะใช้forEachKeyValueเมธอดบนMapIterableอินเตอร์เฟสซึ่งสืบทอดโดยMutableMapและImmutableMapอินเตอร์เฟสและการนำไปใช้งาน

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

การใช้คลาสภายในที่ไม่ระบุชื่อคุณสามารถเขียนรหัสได้ดังนี้:

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

หมายเหตุ:ฉันเป็นคอมมิชชันสำหรับ Eclipse Collections


37

แลมบ์ดา Expression Java 8

ใน Java 1.8 (Java 8) สิ่งนี้กลายเป็นเรื่องง่ายขึ้นมากโดยใช้วิธีforEachจากการดำเนินการโดยรวม ( การทำงานแบบสตรีม ) ที่มีลักษณะคล้ายกับตัววนซ้ำจากIterable Interface

เพียงคัดลอกคำสั่งวางด้านล่างไปยังรหัสของคุณและเปลี่ยนชื่อตัวแปรHashMapจากhmเป็น HashMap ของคุณเพื่อพิมพ์คู่ของคีย์ - ค่า

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.

ด้านล่างนี้เป็นตัวอย่างรหัสที่ฉันพยายามใช้Lambda Expression สิ่งนี้เท่ห์มาก ต้องลอง

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

นอกจากนี้ยังสามารถใช้Spliteratorสำหรับเดียวกัน

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

UPDATE


รวมถึงลิงค์เอกสารไปยัง Oracle Docs สำหรับข้อมูลเพิ่มเติมเกี่ยวกับแลมบ์ดาไปที่ลิงค์นี้และต้องอ่านการดำเนินการโดยรวมและสำหรับ Spliterator ไปที่ลิงค์นี้


36

ในทางทฤษฎีวิธีที่มีประสิทธิภาพที่สุดจะขึ้นอยู่กับการใช้งานแผนที่ วิธีที่เป็นทางการในการทำเช่นนี้คือการโทรmap.entrySet()ซึ่งจะส่งคืนชุดMap.Entryซึ่งแต่ละชุดมีคีย์และค่า ( entry.getKey()และentry.getValue())

ในการดำเนินงานที่มีนิสัยแปลกก็อาจจะสร้างความแตกต่างบางอย่างว่าคุณจะใช้map.keySet(), map.entrySet()หรือสิ่งอื่นใด แต่ฉันไม่สามารถคิดได้ว่าทำไมมีใครเขียนมันแบบนั้น เป็นไปได้มากว่ามันไม่ได้ทำให้ประสิทธิภาพการทำงานของคุณแตกต่างไป

และใช่คำสั่งซื้อจะขึ้นอยู่กับการนำไปใช้ - รวมถึงลำดับการแทรกและปัจจัยอื่น ๆ ที่ควบคุมได้ยาก

[แก้ไข] ฉันเขียนvalueSet()ตอนแรก แต่แน่นอนว่าentrySet()คำตอบคือจริง


36

Java 8

เราได้มีforEachวิธีการที่ยอมรับการแสดงออกแลมบ์ดา เรายังได้รับAPI สตรีมด้วย พิจารณาแผนที่:

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

ทำซ้ำคีย์:

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

ทำซ้ำมากกว่าค่า:

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

ทำซ้ำมากกว่ารายการ (ใช้ forEach และ Streams):

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

ข้อดีของการสตรีมคือสามารถขนานกันได้อย่างง่ายดายในกรณีที่เราต้องการ เราเพียงแค่ต้องใช้parallelStream()แทนstream()ข้างบน

forEachOrderedเทียบforEachกับลำธาร? forEachไม่ปฏิบัติตามคำสั่งการเผชิญหน้า (ถ้ากำหนด) และโดยเนื้อแท้ไม่ใช่กำหนดขึ้นในธรรมชาติที่เป็นforEachOrderedไม่ ดังนั้นforEachไม่รับประกันว่าคำสั่งจะถูกเก็บไว้ ตรวจสอบนี้เพิ่มเติม


33

Java 8:

คุณสามารถใช้แลมบ์ดานิพจน์:

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

สำหรับข้อมูลเพิ่มเติมให้ทำตามนี้


@injecteer: ดูเหมือนแรงจูงใจของการแสดงออกแลมบ์ดา
humblerookie

9
คุณไม่จำเป็นต้องสตรีมหากคุณต้องการวนซ้ำแผนที่ myMap.forEach( (currentKey,currentValue) -> /* action */ );กระชับยิ่งขึ้น
Holger

29

ลองสิ่งนี้กับ Java 1.4:

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

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

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

  //...
}

26

ในแผนที่เราสามารถพูดซ้ำkeysและ / หรือvaluesและ / หรือboth (e.g., entrySet) ขึ้นอยู่กับคนที่สนใจ _ ชอบ:

  1. ย้ำผ่านkeys -> keySet()แผนที่:

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. ย้ำผ่านvalues -> values()แผนที่:

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. ย้ำผ่านboth -> entrySet()แผนที่:

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

_ ยิ่งไปกว่านั้นมี 3 วิธีที่แตกต่างในการทำซ้ำผ่าน HashMap พวกเขามีดังนี้

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


21

หากคุณมีแผนที่ที่ไม่มีการพิมพ์ทั่วไปคุณสามารถใช้:

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

หรือ

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 และฉันต้องการวนซ้ำทุกคู่ที่อยู่ในนั้นวิธีที่มีประสิทธิภาพที่สุดในการผ่านแผนที่คืออะไร

หากประสิทธิภาพของการวนลูปเป็นสิ่งสำคัญสำหรับแอปของคุณให้เลือกMapการนำไปปฏิบัติที่รักษาคีย์ตามลำดับที่คุณต้องการ

การเรียงลำดับองค์ประกอบจะขึ้นอยู่กับการใช้แผนที่เฉพาะที่ฉันมีสำหรับอินเทอร์เฟซหรือไม่

ใช่แล้ว

  • Mapการใช้งานบางอย่างสัญญาคำสั่งการทำซ้ำบางอย่างอื่น ๆ ทำไม่ได้
  • การใช้งานที่แตกต่างกันของการMapบำรุงรักษาการเรียงลำดับที่แตกต่างกันของคู่คีย์ - ค่า

ดูตารางนี้ฉันสร้างการสรุปการMapใช้งานต่างๆที่มาพร้อมกับ Java 11 โดยเฉพาะสังเกตเห็นคอลัมน์ลำดับการทำซ้ำ คลิก / แตะเพื่อซูม

สารบัญการนำแผนที่ไปใช้ใน Java 11 เปรียบเทียบคุณสมบัติ

คุณสามารถเห็นการใช้งานสี่รายการที่Mapยังคงรักษาคำสั่งซื้ออยู่ :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap อินเตอร์เฟซ

สองอย่างนี้ใช้NavigableMapอินเตอร์เฟส: TreeMap& ConcurrentSkipListMap.

SortedMapอินเทอร์เฟซที่เก่ากว่าถูกแทนที่อย่างมีประสิทธิภาพโดยNavigableMapอินเทอร์เฟซที่ใหม่กว่า แต่คุณอาจพบว่าการใช้งานของบุคคลที่สามใช้งานอินเทอร์เฟซที่เก่ากว่าเท่านั้น

ระเบียบธรรมชาติ

ถ้าคุณต้องการMapที่ช่วยให้คู่ของตนจัดโดย“ธรรมชาติ” ของคีย์ใช้หรือTreeMap ConcurrentSkipListMapคำว่า“ธรรมชาติ” Comparableหมายถึงระดับของการดำเนินการคีย์ ค่าที่ส่งคืนโดยcompareToเมธอดใช้สำหรับการเปรียบเทียบในการเรียงลำดับ

สั่งที่กำหนดเอง

หากคุณต้องการระบุรูทีนการเรียงลำดับแบบกำหนดเองสำหรับคีย์ของคุณเพื่อใช้ในการรักษาลำดับการเรียงให้ผ่านการComparatorใช้งานที่เหมาะสมกับคลาสของคีย์ของคุณ ใช้อย่างใดอย่างหนึ่งTreeMapหรือผ่านของคุณConcurrentSkipListMapComparator

คำสั่งแทรกต้นฉบับ

LinkedHashMapหากคุณต้องการคู่ของแผนที่ของคุณจะถูกเก็บไว้ในคำสั่งเดิมของพวกเขาในที่ที่คุณแทรกพวกเขาเข้าไปในแผนที่การใช้งาน

คำสั่งกำหนด Enum

หากคุณใช้ enum เช่นDayOfWeekหรือMonthเป็นกุญแจให้ใช้EnumMapคลาส ไม่เพียงแค่คลาสนี้ได้รับการปรับแต่งให้ใช้หน่วยความจำน้อยมากและทำงานได้อย่างรวดเร็ว แต่มันยังรักษาคู่ของคุณตามลำดับที่กำหนดโดย enum ยกDayOfWeekตัวอย่างเช่นกุญแจของDayOfWeek.MONDAYจะถูกพบครั้งแรกเมื่อซ้ำแล้วและกุญแจของDayOfWeek.SUNDAYจะมีอายุ

ข้อควรพิจารณาอื่น ๆ

ในการเลือกMapใช้งานให้พิจารณาด้วย:

  • NULLs การใช้งานบางอย่างห้าม / ยอมรับ NULL เป็นคีย์และ / หรือค่า
  • เห็นพ้องด้วย หากคุณกำลังจัดการแผนที่ข้ามเธรดคุณต้องใช้การใช้งานที่รองรับการทำงานพร้อมกัน หรือห่อแผนที่ด้วยCollections::synchronizedMap(ดีกว่าน้อยกว่า)

ข้อควรพิจารณาทั้งสองนี้กล่าวถึงในตารางกราฟิกด้านบน


แสดงความคิดเห็นล่าช้าเกี่ยวกับคำตอบที่สายไปงานเลี้ยง (แต่มีข้อมูลมาก) +1 จากฉันสำหรับการกล่าวถึงEnumMapเนื่องจากเป็นครั้งแรกที่ฉันได้ยิน อาจมีหลายกรณีที่อาจมีประโยชน์
user991710

15

การสั่งซื้อจะขึ้นอยู่กับการใช้งานแผนที่เฉพาะเสมอ การใช้ Java 8 คุณสามารถใช้สิ่งใดสิ่งหนึ่งต่อไปนี้:

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

หรือ:

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

ผลลัพธ์จะเหมือนกัน (ลำดับเดียวกัน) รายการ setSet ได้รับการสนับสนุนจากแผนที่เพื่อให้คุณได้รับสินค้าที่เหมือนกัน อันที่สองนั้นมีประโยชน์เพราะคุณสามารถใช้ lambdas ได้เช่นหากคุณต้องการพิมพ์เฉพาะวัตถุจำนวนเต็มที่มากกว่า 5:

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

รหัสด้านล่างแสดงการวนซ้ำผ่าน LinkedHashMap และ HashMap ปกติ (ตัวอย่าง) คุณจะเห็นความแตกต่างในการสั่งซื้อ:

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


13

คุณสามารถทำได้โดยใช้ยาชื่อสามัญ:

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

โซลูชันที่มีประสิทธิภาพซ้ำ ๆ บน Map คือ 'วนรอบ' จาก Java 5 ถึง Java 7 นี่คือ:

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

จาก Java 8 คุณสามารถใช้การแสดงออกแลมบ์ดาเพื่อย้ำผ่านแผนที่ มันเป็น 'forEach' ที่ได้รับการปรับปรุง

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

หากคุณต้องการเขียนแลมบ์ดาเงื่อนไขคุณสามารถเขียนมันได้ดังนี้

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

มีหลายวิธีในการทำเช่นนี้ ด้านล่างเป็นขั้นตอนง่ายๆ:

สมมติว่าคุณมีหนึ่งแผนที่ที่ชอบ:

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

จากนั้นคุณสามารถทำบางสิ่งเช่นด้านล่างเพื่อทำซ้ำองค์ประกอบต่างๆของแผนที่

// ********** 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));
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.