Làm cách nào để tôi lặp lại hiệu quả qua từng mục trong Bản đồ Java?


3302

Nếu tôi có một đối tượng thực hiện Map giao diện trong Java và tôi muốn lặp lại mọi cặp có trong nó, cách hiệu quả nhất để đi qua bản đồ là gì?

Việc sắp xếp các phần tử có phụ thuộc vào việc triển khai bản đồ cụ thể mà tôi có cho giao diện không?


38
Trong Java 8 bằng cách sử dụng Biểu thức Lambda: stackoverflow.com/a/25616206/1503859
Nitin Mahesh

Câu trả lời:


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

93
Nếu bạn làm điều đó, thì nó sẽ không hoạt động vì Entry là một Lớp lồng trong Bản đồ. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2

266
bạn có thể viết nhập dưới dạng "nhập java.util.Map.Entry;" và nó sẽ hoạt động.
jjujuma

55
@Pureferret Lý do duy nhất bạn có thể muốn sử dụng một trình vòng lặp là nếu bạn cần gọi removephương thức của nó . Nếu đó là trường hợp, câu trả lời khác này cho bạn thấy làm thế nào để làm điều đó. Mặt khác, vòng lặp nâng cao như trong câu trả lời ở trên là cách để đi.
assylias

102
Tôi tin rằng biểu mẫu Map.Entry rõ ràng hơn việc nhập lớp bên trong vào không gian tên hiện tại.
Josiah Yoder

31
Lưu ý rằng bạn có thể sử dụng map.values()hoặc map.keySet()nếu bạn chỉ muốn lặp qua các giá trị hoặc khóa.
dguay

1216

Để tóm tắt các câu trả lời khác và kết hợp chúng với những gì tôi biết, tôi đã tìm thấy 10 cách chính để làm điều này (xem bên dưới). Ngoài ra, tôi đã viết một số bài kiểm tra hiệu suất (xem kết quả bên dưới). Ví dụ: nếu chúng ta muốn tìm tổng của tất cả các khóa và giá trị của bản đồ, chúng ta có thể viết:

  1. Sử dụng iteratorMap.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. Sử dụng foreachMap.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
  3. Sử dụng forEach từ Java 8

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
  4. Sử dụng keysetforeach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
  5. Sử dụng keysetiterator

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
        Integer key = itr2.next();
        i += key + map.get(key);
    }
  6. Sử dụng choMap.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. Sử dụng API luồng Java 8

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
  8. Sử dụng song song API luồng Java 8

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
  9. Sử dụng IterableMap củaApache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
        i += it.next() + it.getValue();
    }
  10. Sử dụng các bộ sưu tập MutableMap của Eclipse (CS)

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

Kiểm tra độ hoàn hảo (chế độ = AverageTime, system = Windows 8.1 64-bit, Intel i7-4790 3.60 GHz, 16 GB)

  1. Đối với bản đồ nhỏ (100 yếu tố), điểm 0,388 là tốt nhất

    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. Đối với bản đồ có 10000 yếu tố, điểm 37.606 là tốt nhất

    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. Đối với bản đồ có 100000 yếu tố, điểm 1184.767 là tốt nhất

    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

Đồ thị (kiểm tra hiệu suất tùy thuộc vào kích thước bản đồ)

Nhập mô tả hình ảnh ở đây

Bảng (kiểm tra độ hoàn hảo tùy thuộc vào kích thước bản đồ)

          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ất cả các bài kiểm tra là trên GitHub .


9
@Viacheslav: câu trả lời rất hay. Chỉ cần tự hỏi làm thế nào apis Java8 bị cản trở, trong điểm chuẩn của bạn, bằng cách bắt lambdas ... (ví dụ: long sum = 0; map.forEach( /* accumulate in variable sum*/);chụp sumlâu, có thể chậm hơn nói stream.mapToInt(/*whatever*/).sumchẳng hạn. Tất nhiên bạn không thể luôn luôn tránh trạng thái bắt, nhưng đó có thể là một bổ sung hợp lý đến băng ghế dự bị.
GPI

17
Bài kiểm tra 8 của bạn là sai. nó truy cập cùng một biến từ các chủ đề khác nhau mà không cần đồng bộ hóa. Thay đổi để AtomicIntegergiải quyết vấn đề.
Talex

44
@ZhekaKozlov: nhìn vào các giá trị lỗi lớn gây chú ý. Hãy xem xét rằng một kết quả xét nghiệm của x±engụ ý rằng đã có kết quả trong khoảng thời gian từ x-eđến x+e, vì vậy kết quả nhanh nhất ( 1184.767±332.968) dao động từ 852để 1518, trong khi chậm nhất thứ hai ( 1706.676±436.867) chạy giữa 12702144, vì vậy kết quả vẫn chồng chéo đáng kể. Bây giờ nhìn vào kết quả chậm nhất, 3289.866±1445.564mà ngụ ý phân kỳ giữa 18444735và bạn biết rằng những kết quả xét nghiệm là vô nghĩa.
Holger

8
Còn việc so sánh 3 triển khai chính: HashMap, LinkedHashMap và TreeMap thì sao?
Thierry

9
# 1 và # 6 hoàn toàn giống nhau. Sử dụng whileso với forvòng lặp không phải là một kỹ thuật khác để lặp lại. Và tôi ngạc nhiên khi họ có sự khác biệt như vậy giữa chúng trong các bài kiểm tra của bạn. Điều đó cho thấy các bài kiểm tra không được cách ly đúng cách với các yếu tố bên ngoài không liên quan đến những điều bạn dự định kiểm tra.
ErikE

294

Trong Java 8, bạn có thể làm sạch và nhanh bằng các tính năng lambdas mới:

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

Kiểu kvsẽ được trình biên dịch suy ra và không cần sử dụngMap.Entry nữa.

Dễ như ăn bánh!


12
Tùy thuộc vào những gì bạn muốn làm với bản đồ, bạn cũng có thể sử dụng API luồng trên các mục được trả về bởi map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Vitalii Fedorenko

1
Điều này sẽ không hoạt động nếu bạn muốn tham chiếu các biến không phải là cuối cùng được khai báo bên ngoài biểu thức lambda của bạn từ bên trong forEach () ...
Chris

7
@Chris Đúng. Sẽ không hiệu quả nếu bạn cố gắng sử dụng các biến không cuối cùng hiệu quả từ bên ngoài lambda.
Điều phối viên

243

Có, thứ tự phụ thuộc vào việc thực hiện Bản đồ cụ thể.

@ ScArcher2 có cú pháp Java 1.5 thanh lịch hơn . Trong 1.4, tôi sẽ làm một cái gì đó như thế này:

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

41
Thích for-loop hơn while .. for (Iterator entry = myMap.entryset (). Iterator (); entry.hasNext ();) {...} Với cú pháp này, phạm vi 'mục' chỉ được giảm xuống cho vòng lặp for .
jai

8
@jpredham Bạn đúng khi sử dụng forcấu trúc như for (Entry e : myMap.entrySet)sẽ không cho phép bạn sửa đổi bộ sưu tập, nhưng ví dụ như @HanuAthena đã đề cập đến nó sẽ hoạt động, vì nó cung cấp cho bạn Iteratorphạm vi. (Trừ khi tôi là thiếu một cái gì đó ...)
pkaeding

1
IntelliJ đang đưa ra lỗi cho tôi Entry thisEntry = (Entry) entries.next();: không nhận ra Entry. Đó có phải là mã giả cho cái gì khác?
JohnK

1
@JohnK thử nhập java.util.Map.Entry.
pkaeding

1
Giải pháp này sẽ không hoạt động nếu bạn có khóa số nguyên và khóa Chuỗi.

140

Mã tiêu biểu để lặp lại trên bản đồ là:

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

HashMaplà việc thực hiện bản đồ chính tắc và không đảm bảo (hoặc mặc dù nó sẽ không thay đổi thứ tự nếu không có thao tác đột biến nào được thực hiện trên đó). SortedMapsẽ trả về các mục dựa trên thứ tự tự nhiên của các phím hoặc a Comparator, nếu được cung cấp. LinkedHashMapsẽ trả về các mục theo thứ tự chèn hoặc thứ tự truy cập tùy thuộc vào cách nó được xây dựng. EnumMaptrả về các mục theo thứ tự tự nhiên của các phím.

(Cập nhật: Tôi nghĩ rằng điều này không còn đúng nữa. ) Lưu ý, IdentityHashMap entrySetiterator hiện có một triển khai đặc biệt trả về cùng một Map.Entrythể hiện cho mọi mục trong entrySet! Tuy nhiên, mỗi khi một trình lặp mới tiến bộ Map.Entrysẽ được cập nhật.


6
EnumMap cũng có hành vi kỳ dị này cùng với IdentityHashMap
Premraj

1
"LinkedHashMap sẽ trả về các mục trong [...] thứ tự truy cập [...]" ... để bạn truy cập các phần tử theo thứ tự bạn truy cập chúng? Hoặc là tautological, hoặc một cái gì đó thú vị có thể sử dụng một hồi quy. ;-)
jpaugh

5
@jpaugh Chỉ truy cập trực tiếp vào LinkedHashMapsố đếm. Những qua iterator, spliterator, entrySet, vv, không thay đổi thứ tự.
Tom Hawtin - tackline

1
1. mặc dùnếu ? 2. Đoạn cuối có thể được hưởng lợi từ việc chải lên.
Peter Mortensen

122

Ví dụ về việc sử dụng iterator và generic:

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
Bạn nên đặt Iteratormột vòng lặp for để giới hạn phạm vi của nó.
Steve Kuo

@SteveKuo Ý bạn là gì khi "giới hạn phạm vi của nó"?
StudioWorks 3/2/2015

12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Bằng cách sử dụng cấu trúc đó, chúng tôi giới hạn phạm vi (mức độ hiển thị của biến) entriescho vòng lặp for.
ComFalet 13/03/2015

3
@ComFalet ơi, tôi hiểu rồi. Không biết rằng nó quan trọng đến thế.
StudioWorks 13/03/2015

102

Đây là một câu hỏi gồm hai phần:

Cách lặp lại các mục của Bản đồ - @ ScArcher2 đã trả lời một cách hoàn hảo.

Thứ tự lặp là gì - nếu bạn chỉ đang sử dụng Map, thì nói đúng ra, không có đảm bảo đặt hàng . Vì vậy, bạn không nên thực sự dựa vào thứ tự được đưa ra bởi bất kỳ thực hiện. Tuy nhiên, SortedMapgiao diện mở rộng Mapvà cung cấp chính xác những gì bạn đang tìm kiếm - việc triển khai sẽ luôn đưa ra một thứ tự sắp xếp nhất quán.

NavigableMaplà một tiện ích mở rộng hữu ích khác - đây là một SortedMapphương pháp bổ sung để tìm các mục theo vị trí được sắp xếp của chúng trong bộ khóa. Vì vậy, khả năng này có thể loại bỏ sự cần thiết lặp lại ở nơi đầu tiên - bạn có thể có thể tìm ra cụ thể entrybạn là sau khi sử dụng higherEntry, lowerEntry, ceilingEntry, hoặc floorEntryphương pháp. Các descendingMapphương pháp thậm chí cung cấp cho bạn một phương pháp rõ ràng của đảo ngược thứ tự traversal .


84

Có một số cách để lặp lại trên bản đồ.

Dưới đây là so sánh hiệu suất của chúng cho một tập dữ liệu chung được lưu trữ trên bản đồ bằng cách lưu trữ một triệu cặp giá trị chính trong bản đồ và sẽ lặp lại trên bản đồ.

1) Sử dụng entrySet()cho mỗi vòng lặp

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

50 mili giây

2) Sử dụng keySet()cho mỗi vòng lặp

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

76 mili giây

3) Sử dụng entrySet()và lặp

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

50 mili giây

4) Sử dụng keySet()và lặp

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

75 mili giây

Tôi đã giới thiệu this link.


1
Thời gian chạy được lấy từ bài viết, không sử dụng Khai thác Microbenchmarking của Java. Do đó, thời gian là không đáng tin cậy, ví dụ như mã có thể đã được tối ưu hóa hoàn toàn bởi trình biên dịch JIT.
AlexB

59

Cách chính xác để làm điều này là sử dụng câu trả lời được chấp nhận vì nó là hiệu quả nhất. Tôi thấy đoạn mã sau có vẻ sạch hơn một chút.

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

15
Đây không phải là cách tiếp cận tốt nhất, sử dụng entryset () hiệu quả hơn nhiều. Findbugs sẽ gắn cờ mã này (xem findbugs.sourceforge.net/, )
Jeff Olson

6
@JeffOlson meh, không hẳn vậy. tra cứu bản đồ là O (1) nên cả hai vòng đều hoạt động theo cùng một cách. thừa nhận, nó sẽ chậm hơn một chút trong một điểm chuẩn vi mô nhưng đôi khi tôi cũng làm điều này vì tôi ghét viết các đối số kiểu này nhiều lần. Ngoài ra, điều này rất có thể sẽ không bao giờ là nút cổ chai hiệu năng của bạn, vì vậy hãy tìm nó nếu nó làm cho mã dễ đọc hơn.
kritzikratzi

4
chi tiết hơn: O(1) = 2*O(1)là khá nhiều định nghĩa của ký hiệu O lớn. Bạn đúng ở chỗ nó chạy chậm hơn một chút, nhưng về độ phức tạp thì chúng giống nhau.
kritzikratzi

2
do va chạm hay không tôi có nghĩa là không có vấn đề gì nếu bạn va chạm, rõ ràng đó là một câu chuyện khác nếu bạn chỉ va chạm. vì vậy bạn khá nhỏ mọn, nhưng vâng, những gì bạn nói là đúng.
kritzikratzi

2
@Jeff Olson: các ý kiến ​​cho rằng độ phức tạp của Hồi Lớn không thay đổi, khi chỉ có một yếu tố không đổi, là chính xác. Tuy nhiên, đối với tôi, vấn đề là hoạt động mất một giờ hay hai giờ. Quan trọng hơn, phải nhấn mạnh rằng yếu tố này là không 2 , vì lặp đi lặp lại một cái entrySet()không chịu một cái nhìn nào cả; nó chỉ là một đường ngang tuyến tính của tất cả các mục. Ngược lại, lặp đi lặp lại keySet()và thực hiện tra cứu trên mỗi khóa mang một lần tra cứu cho mỗi khóa, vì vậy chúng ta đang nói về việc tra cứu bằng 0 so với n tra cứu ở đây, n là kích thước của Map. Vì vậy, các yếu tố là cách xa hơn 2...
Holger

57

FYI, bạn cũng có thể sử dụng map.keySet()map.values()nếu bạn chỉ quan tâm đến các khóa / giá trị của bản đồ chứ không phải cái khác.


42

Với Java 8 , bạn có thể lặp lại Bản đồ bằng biểu thức forEach và lambda,

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

41

Với Bộ sưu tập Eclipse , bạn sẽ sử dụng forEachKeyValuephương thức trên MapIterablegiao diện, được kế thừa bởi MutableMapvà các ImmutableMapgiao diện và các triển khai của chúng.

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

Sử dụng một lớp bên trong ẩn danh, bạn có thể viết mã như sau:

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

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse.


37

Biểu thức Lambda Java 8

Trong Java 1.8 (Java 8), điều này đã trở nên dễ dàng hơn nhiều bằng cách sử dụng phương thức forEach từ các hoạt động Tổng hợp ( Hoạt động luồng ) trông tương tự như các trình vòng lặp từ Iterable Interface.

Chỉ cần sao chép dán bên dưới câu lệnh vào mã của bạn và đổi tên biến HashMap từ hm sang biến HashMap của bạn để in ra cặp khóa-giá trị.

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.

Dưới đây là mã mẫu mà tôi đã thử sử dụng Lambda Expression . Công cụ này là rất mát mẻ. Phải thử.

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

Ngoài ra người ta có thể sử dụng Spliterator cho cùng.

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

CẬP NHẬT


Bao gồm các tài liệu liên kết đến Oracle Docs. Để biết thêm về Lambda, hãy truy cập liên kết này và phải đọc Hoạt động tổng hợp và đối với Spliterator, hãy truy cập liên kết này .


36

Về lý thuyết, cách hiệu quả nhất sẽ phụ thuộc vào việc triển khai Bản đồ. Cách chính thức để làm điều này là gọi map.entrySet(), trong đó trả về một tập hợp Map.Entry, mỗi bộ chứa một khóa và một giá trị ( entry.getKey()entry.getValue()).

Trong một triển khai idiosyncratic, nó có thể tạo ra một số khác biệt cho dù bạn sử dụng map.keySet(),map.entrySet() hay cái gì khác. Nhưng tôi không thể nghĩ ra một lý do tại sao mọi người sẽ viết nó như thế. Nhiều khả năng nó không có sự khác biệt để thực hiện những gì bạn làm.

Và có, thứ tự sẽ phụ thuộc vào việc thực hiện - cũng như (có thể) thứ tự chèn và các yếu tố khó kiểm soát khác.

[sửa] Tôi đã viết valueSet()ban đầu nhưng tất nhiên entrySet()thực sự là câu trả lời.


36

Java 8

Chúng ta có forEachphương thức chấp nhận biểu thức lambda . Chúng tôi cũng đã có API luồng . Hãy xem xét một bản đồ:

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

Lặp lại các phím:

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

Lặp lại các giá trị:

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

Lặp lại các mục (Sử dụng forEach và Luồng):

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

Ưu điểm với các luồng là chúng có thể được song song dễ dàng trong trường hợp chúng ta muốn. Chúng tôi chỉ cần sử dụng parallelStream()thay cho stream()ở trên.

forEachOrderedvs forEachvới stream? Việc forEachkhông tuân theo thứ tự gặp gỡ (nếu được xác định) và về bản chất là không xác định trong tự nhiên forEachOrdered. Vì vậy, forEachkhông đảm bảo rằng thứ tự sẽ được giữ. Cũng kiểm tra điều này để biết thêm.


33

Java 8:

Bạn có thể sử dụng biểu thức lambda:

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

Để biết thêm thông tin, hãy làm theo điều này .


@injecteer: Có vẻ như động cơ của biểu thức lambda
humblerookie

9
Bạn không cần một luồng nếu bạn chỉ muốn lặp lại trên bản đồ. myMap.forEach( (currentKey,currentValue) -> /* action */ );ngắn gọn hơn nhiều
Holger

29

Hãy thử điều này với Java 1.4:

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

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

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

  //...
}

26

Trong Bản đồ, người ta có thể Lặp lại keysvà / hoặc values/ hoặc both (e.g., entrySet) phụ thuộc vào người quan tâm đến_ Thích:

  1. Lặp lại thông qua keys -> keySet() bản đồ:

    Map<String, Object> map = ...;
    
    for (String key : map.keySet()) {
        //your Business logic...
    }
  2. Lặp lại thông qua values -> values() bản đồ:

    for (Object value : map.values()) {
        //your Business logic...
    }
  3. Lặp lại thông qua both -> entrySet() bản đồ:

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

_ Hơn nữa, có 3 cách khác nhau để lặp qua một HashMap. Họ như dưới đây__

//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

Nhỏ gọn nhất với Java 8:

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

21

Nếu bạn có một Bản đồ chưa được đánh dấu chung, bạn có thể sử dụng:

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

HOẶC LÀ

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

Nếu tôi có một đối tượng triển khai giao diện Bản đồ trong Java và tôi muốn lặp lại qua mọi cặp có trong đó, cách hiệu quả nhất để đi qua bản đồ là gì?

Nếu hiệu quả của việc lặp các khóa là ưu tiên cho ứng dụng của bạn, thì hãy chọn Maptriển khai duy trì các khóa theo thứ tự bạn muốn.

Việc sắp xếp các phần tử có phụ thuộc vào việc triển khai bản đồ cụ thể mà tôi có cho giao diện không?

Phải, chắc chắn rồi.

  • Một số Map triển khai hứa hẹn một thứ tự lặp nhất định, một số khác thì không.
  • Việc triển khai khác nhau để Mapduy trì thứ tự khác nhau của các cặp khóa-giá trị.

Xem bảng này tôi đã tạo tóm tắt các Maptriển khai khác nhau được gói cùng với Java 11. Cụ thể, hãy chú ý cột thứ tự lặp . Nhấn / chạm để phóng to.

Bảng triển khai bản đồ trong Java 11, so sánh các tính năng của chúng

Bạn có thể thấy có bốn Maptriển khai duy trì một trật tự :

  • TreeMap
  • ConcurrentSkipListMap
  • LinkedHashMap
  • EnumMap

NavigableMap giao diện

Hai trong số đó thực hiện NavigableMapgiao diện: TreeMap& ConcurrentSkipListMap.

SortedMapGiao diện cũ được thay thế một cách hiệu quả bởi NavigableMapgiao diện mới hơn . Nhưng bạn có thể thấy các triển khai của bên thứ 3 chỉ thực hiện giao diện cũ hơn.

Trật tự tự nhiên

Nếu bạn muốn một Mapcặp giữ cho các cặp của nó được sắp xếp theo thứ tự tự nhiên, thì hãy sử dụng TreeMaphoặc ConcurrentSkipListMap. Thuật ngữ thứ tự tự nhiên, có nghĩa là lớp các khóa thực hiện Comparable. Giá trị được trả về bởicompareTo phương thức được sử dụng để so sánh trong sắp xếp.

Đặt hàng tùy chỉnh

Nếu bạn muốn chỉ định một thói quen sắp xếp tùy chỉnh cho các khóa của bạn sẽ được sử dụng để duy trì thứ tự được sắp xếp, hãy chuyển một Comparatortriển khai phù hợp với lớp các khóa của bạn. Sử dụng TreeMaphoặc ConcurrentSkipListMap, vượt qua của bạn Comparator.

Thứ tự chèn gốc

Nếu bạn muốn các cặp bản đồ của mình được giữ theo thứ tự ban đầu mà bạn đã chèn chúng vào bản đồ, hãy sử dụng LinkedHashMap .

Thứ tự định nghĩa

Nếu bạn đang sử dụng một enum như DayOfWeekhoặc Monthnhư các khóa của mình, hãy sử dụng EnumMaplớp. Lớp này không chỉ được tối ưu hóa cao để sử dụng rất ít bộ nhớ và chạy rất nhanh, nó còn duy trì các cặp của bạn theo thứ tự được xác định bởi enum. Ví DayOfWeekdụ, khóa của DayOfWeek.MONDAYsẽ được tìm thấy đầu tiên khi lặp và khóa củaDayOfWeek.SUNDAY sẽ là cuối cùng.

Những ý kiến ​​khác

Trong việc lựa chọn Mapthực hiện, cũng xem xét:

  • NULL. Một số triển khai cấm / chấp nhận NULL làm khóa và / hoặc giá trị.
  • Đồng thời. Nếu bạn đang thao tác trên bản đồ trên các luồng, bạn phải sử dụng một triển khai hỗ trợ đồng thời. Hoặc bọc bản đồ với Collections::synchronizedMap(ít thích hợp hơn).

Cả hai cân nhắc này được đề cập trong bảng đồ họa ở trên.


Nhận xét muộn về một câu trả lời cũng muộn cho bữa tiệc (nhưng rất nhiều thông tin). +1 từ tôi để đề cập EnumMap, vì đây là lần đầu tiên tôi nghe về nó. Có lẽ nhiều trường hợp điều này có thể có ích.
user991710

15

Việc đặt hàng sẽ luôn phụ thuộc vào việc thực hiện bản đồ cụ thể. Sử dụng Java 8, bạn có thể sử dụng một trong hai cách sau:

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

Hoặc là:

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

Kết quả sẽ giống nhau (cùng thứ tự). Mục nhập được hỗ trợ bởi bản đồ để bạn có được thứ tự tương tự. Cách thứ hai rất tiện lợi vì nó cho phép bạn sử dụng lambdas, ví dụ nếu bạn chỉ muốn in chỉ các đối tượng Integer lớn hơn 5:

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

Mã dưới đây cho thấy sự lặp lại thông qua LinkedHashMap và HashMap bình thường (ví dụ). Bạn sẽ thấy sự khác biệt theo thứ tự:

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

Bạn có thể làm điều đó bằng cách sử dụng thuốc generic:

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

Một giải pháp lặp lại hiệu quả trên Bản đồ là một vòng lặp 'cho mỗi' từ Java 5 đến Java 7. Đây là:

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

Từ Java 8, bạn có thể sử dụng biểu thức lambda để lặp lại trên Bản đồ. Nó là một 'forEach' nâng cao

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

Nếu bạn muốn viết một điều kiện cho lambda, bạn có thể viết nó như thế này:

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

Có rất nhiều cách để làm điều này. Dưới đây là một vài bước đơn giản:

Giả sử bạn có một Bản đồ như:

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

Sau đó, bạn có thể làm một cái gì đó như dưới đây để lặp lại các yếu tố bản đồ.

// ********** 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));
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.