Як я ефективно повторюю кожен запис на Java-карті?


3301

Якщо у мене є об'єкт, що реалізує Mapінтерфейс в Java, і я хочу переглядати кожну пару, що міститься в ньому, який найефективніший спосіб пройти через карту?

Чи буде впорядкованість елементів залежати від конкретної реалізації карти для інтерфейсу?


38
На Java 8 за допомогою Lambda Expression: stackoverflow.com/a/25616206/1503859
Нітін Махеш,

Відповіді:


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

93
Якщо ви це зробите, то він не працюватиме, оскільки Entry - це вкладений клас у Map. java.sun.com/javase/6/docs/api/java/util/Map.html
ScArcher2

266
ви можете написати імпорт як "import java.util.Map.Entry;" і воно спрацює.
джуджума

55
@Pureferret Єдина причина, з якої ви можете скористатися ітератором, - це якщо вам потрібно викликати його removeметод. Якщо це так, ця інша відповідь показує, як це зробити. Інакше розширений цикл, як показано у відповіді вище, - це шлях.
assylias

102
Я вважаю, що форма Map.Entry зрозуміліша, ніж імпорт внутрішнього класу в поточний простір імен.
JosiahYoder-deactive за винятком ..

31
Зауважте, що ви можете використовувати map.values()або, map.keySet()якщо ви хочете прокручувати лише значення або ключі.
dguay

1214

Щоб узагальнити інші відповіді та поєднати їх із тим, що мені відомо, я знайшов 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. Використання keySet та foreach

    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    
  5. Використання keySet та ітератора

    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 of Eclipse (CS)

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

Тести на продуктивність (режим = AverageTime, система = Windows 8.1 64-розрядні, Intel i7-4790 3,60 ГГц, 16 ГБ)

  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. Для карти з 10000 елементами оцінка 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. На карті зі 100000 елементами найкраща оцінка - 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 перешкоджає захопленню лямбда ... (наприклад, long sum = 0; map.forEach( /* accumulate in variable sum*/);фіксує sumдовге, що може бути повільніше, ніж, наприклад, stream.mapToInt(/*whatever*/).sumнаприклад. на лаву.
GPI

17
Ваш 8 тест неправильний. він отримує доступ до тієї ж змінної з різних потоків без синхронізації. Змініть, щоб AtomicIntegerвирішити проблему.
талекс

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і ви знаєте , що ці результати випробувань НЕ мають сенсу.
Хольгер

8
Як щодо порівняння трьох основних реалізацій: HashMap, LinkedHashMap та TreeMap?
Тьєррі

9
№1 і №6 абсолютно однакові. Використання циклу whileпроти forциклу - це не інша техніка ітерації. І я здивований, що у ваших тестах між ними є така різниця, що говорить про те, що тести не є належним чином ізольовані від зовнішніх факторів, не пов'язаних із речами, які ви маєте намір перевірити.
ErikE

293

У Java 8 ви можете робити це чисто і швидко, використовуючи нові функції лямбдашів:

 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більше не потрібно .

Простенька!


12
Залежно від того, що ви хочете зробити з картою, ви також можете використовувати API потоку для записів, повернених map.entrySet().stream() docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Федоренко Віталій

1
Це не спрацює, якщо ви хочете посилатись на не остаточні змінні, оголошені поза вашим лямбда-виразом зсередини forEach () ...
Кріс

7
@Chris Правильно. Це не спрацює, якщо ви спробуєте ефективно використовувати не остаточні змінні за межами лямбда.
Координатор

242

Так, замовлення залежить від конкретної реалізації карти.

@ 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 ніж while .. for (Iterator entries = myMap.entrySet (). Iterator (); entries.hasNext ();) {...} При цьому синтаксисі область 'entries' зменшується лише до циклу for .
джай

8
@jpredham Ви маєте рацію, що використання forконструкції as for (Entry e : myMap.entrySet)не дозволить вам змінювати колекцію, але приклад, як згадував @HanuAthena, повинен працювати, оскільки він дає вам Iteratorсферу застосування. (Якщо я щось не пропускаю ...)
pkaeding

1
IntelliJ дає мені помилки щодо Entry thisEntry = (Entry) entries.next();: не розпізнає Entry. Це псевдокод для чогось іншого?
JohnK

1
@JohnK спробуйте імпортувати java.util.Map.Entry.
pkaeding

1
Це рішення не працюватиме, якщо у вас є цілий ключ і ключ String.

139

Типовим кодом для повторення карти є:

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

HashMapє реалізацією канонічної карти і не дає гарантій (або хоча вона не повинна змінювати порядок, якщо на ній не виконується жодна операція, що мутує). SortedMapповерне записи на основі природного впорядкування ключів або Comparator, якщо таке передбачено. LinkedHashMapабо поверне записи в порядку вставки, або в порядку доступу, залежно від способу побудови. EnumMapповертає записи в натуральному порядку клавіш.

(Оновлення: я думаю, це більше не відповідає дійсності. ) Примітка, IdentityHashMap entrySetітератор в даний час має своєрідну реалізацію, яка повертає один і той же Map.Entryекземпляр для кожного елемента в entrySet! Однак кожен раз, коли новий ітератор просуває Map.Entryоновлення, оновляється.


6
EnumMap також має таку своєрідну поведінку разом з IdentityHashMap
Premraj

1
"LinkedHashMap або поверне записи в [...] порядку замовлення [...]" ... так що ви отримаєте доступ до елементів у тому порядку, до якого ви отримуєте доступ до них? Або тавтологічне, або щось цікаве, що могло б використати відступ. ;-)
jpaugh

5
@jpaugh Лише прямий доступ до LinkedHashMapрахунку. Ті , через iterator, spliterator, entrySetі т.д., не змінюють порядок.
Том Хотін - тайклін

1
1. хочаякщо ? 2. Останній абзац може отримати користь від підкреслення.
Пітер Мортенсен

120

Приклад використання ітератора та дженерики:

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, щоб обмежити його обсяг.
Стів Куо

@SteveKuo Що ви маєте на увазі під «обмеженням його обсягу»?
StudioWorks

12
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Використовуючи цю конструкцію, ми обмежуємо область (видимість змінної) entriesциклом for.
ComFreek

3
@ComFreek О, бачу. Не знав, що це так важливо.
StudioWorks

101

Це питання з двох частин:

Як перебрати записи на карті - @ ScArcher2 відповів це чудово.

Який порядок ітерації - якщо ви просто використовуєте Map, то строго кажучи, жодних гарантій замовлення немає . Тому ви не повинні покладатися на замовлення, надані будь-якою реалізацією. Однак SortedMapінтерфейс розширюється Mapі забезпечує саме те, що ви шукаєте - реалізація завжди дасть послідовний порядок сортування.

NavigableMapє ще одне корисне розширення - це SortedMapдодаткові методи пошуку записів за їх упорядкованим положенням у наборі ключів. Таким чином , потенційно це може усунути необхідність в переборі в першу чергу - Ви могли б бути в змозі знайти специфічні entryви після використання higherEntry, lowerEntry, ceilingEntryабо floorEntryметоди. descendingMapМетод навіть дає явний спосіб зміни порядку обходу .


83

Існує кілька способів перебору карти.

Ось порівняння їхніх характеристик для загального набору даних, що зберігається у карті, зберігаючи мільйон пар ключових значень у карті та буде повторюватись над картою.

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. Тому часи є ненадійними, оскільки, наприклад, код може бути повністю оптимізований компілятором JIT.
AlexB

58

Правильний спосіб зробити це - використовувати прийняту відповідь, оскільки вона є найбільш ефективною. Я вважаю, що наступний код виглядає трохи чистіше.

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

15
Це не найкращий підхід, набагато ефективніше використовувати entrySet (). Findbugs позначить цей код (див. Findbugs.sourceforge.net/… )
Jeff Olson

6
@JeffOlson Мех, не дуже. Пошук карти - O (1), тому обидві петлі ведуть себе однаково. Правда, мікро-орієнтир буде трохи повільніше, але я іноді це роблю також, тому що я ненавиджу писати аргументи типу знову і знову. Крім того, це, ймовірно, ніколи не буде вашим вузьким місцем продуктивності, тому прийміть це, якщо це зробить код більш читабельним.
kritzikratzi

4
більш докладно: O(1) = 2*O(1)в значній мірі є визначення великої нотації O. Ви маєте рацію в тому, що вона працює трохи повільніше, але за складністю вони однакові.
kritzikratzi

2
під зіткненням чи ні, я мав на увазі, що це не має значення, якщо у вас кілька зіткнень, очевидно, це інша історія, якщо у вас є лише зіткнення. значить, ви досить дрібні, але так, те, що ви говорите, є правдою.
kritzikratzi

2
@ Джеф Олсон: зауваження, що складність "Big O" не змінюється, коли є лише постійний фактор, є правильним. Все-таки для мене важливо, чи займає операція одну годину чи дві години. Що важливіше, слід підкреслити, що цей фактор не є 2, оскільки ітерація над тоном entrySet()взагалі не має пошуку; це лише лінійний обхід усіх записів. На відміну від цього, ітерація над keySet()і виконанням пошуку за ключем містить один пошук на ключ, тому ми говоримо про нульові пошукові підходи проти n оглядачів тут, n - розмір Map. Тож фактор набагато перевищує 2
Холгер

56

FYI, ви також можете використовувати, map.keySet()і map.values()якщо вас цікавлять лише ключі / значення карти, а не інші.


42

Завдяки Java 8 ви можете ітератувати Map, використовуючи forEach та лямбда-вираз,

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

40

З Eclipse , колекцією , ви повинні використовувати 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.


36

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

ОНОВЛЕННЯ


У тому числі посилання на документацію до Oracle Docs. Більш докладної інформації про Lambda перейти на цю посилання і повинен читати агрегатні операції і Spliterator перейти по цій посиланням .


35

Теоретично найефективніший спосіб залежатиме від того, яка реалізація Map. Офіційний спосіб зробити це - дзвінок map.entrySet(), який повертає набір Map.Entry, кожен з яких містить ключ і значення ( entry.getKey()і entry.getValue()).

У ідіосинкратичному здійсненні це може змінити чи ви використовуєте map.keySet(), map.entrySet()чи щось інше. Але я не можу придумати причину, чому хтось написав би це так. Швидше за все, це не має ніякого значення для виконання того, що ви робите.

І так, порядок залежатиме від реалізації - а також (можливо) порядку вставки та інших важко контрольованих факторів.

[редагувати] Я писав valueSet()спочатку, але, звичайно, entrySet()це відповідь.


35

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 та потоків):

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()вище.

forEachOrderedvs forEachз потоками? forEachНе слід порядку наштовхується (якщо вони визначені) і за своєю суттю недетермінований в природі , де , як це forEachOrderedробить. Тож forEachне гарантує, що порядок буде дотримано. Також перевірте це для отримання додаткової інформації.


32

Java 8:

Ви можете використовувати лямбда-вирази:

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

Для отримання додаткової інформації слідкуйте за цим .


@injecteer: Здається мотив
лямбдаських

9
Вам не потрібен потік, якщо ви просто хочете перейти на карту. myMap.forEach( (currentKey,currentValue) -> /* action */ );набагато стисліший.
Холгер

28

Спробуйте це з Java 1.4:

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

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

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

  //...
}

25

На карті можна провести ітерацію над 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

Якщо у мене є об’єкт, що реалізує інтерфейс Map на 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або ConcurrentSkipListMap, передаючи свій Comparator.

Оригінальний порядок вставки

Якщо ви хочете, щоб пари вашої карти зберігалися в їх первісному порядку, у якому ви вставили їх на карту, використовуйте LinkedHashMap.

Порядок визначення перерахунків

Якщо ви використовуєте enum, такий як DayOfWeekабо Monthяк ваші ключі, використовуйте EnumMapклас. Мало того, що цей клас сильно оптимізований для використання дуже мало пам'яті і дуже швидко бігти, він зберігає свої пари в порядку , визначеному перерахуванням. Для DayOfWeek, наприклад, ключ DayOfWeek.MONDAYбуде знайдений , коли перша ітерація, і ключ DayOfWeek.SUNDAYбуде останнім.

Інші міркування

Вибираючи Mapреалізацію, враховуйте також:

  • NULL. Деякі реалізації забороняють / приймають NULL як ключ та / або значення.
  • Паралельність. Якщо ви маніпулюєте картою через нитки, ви повинні використовувати реалізацію, яка підтримує паралельність. Або оберніть карту Collections::synchronizedMap(менш бажано).

Обидва ці міркування висвітлено у графічній таблиці вище.


Пізній коментар до відповіді, яка також спізнюється на вечірку (але дуже інформативна). +1 від мене для згадки EnumMap, оскільки це вперше я чув про це. Напевно, існує багато випадків, коли це може стати в нагоді.
користувач991710

15

Замовлення завжди залежатиме від конкретної реалізації карти. Використовуючи Java 8, ви можете використовувати будь-яке з них:

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

Або:

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

Результат буде однаковим (той самий порядок). Вхідний набір, підкріплений картою, так що ви отримуєте те саме замовлення. Другий - це зручно, оскільки дозволяє використовувати лямбда, наприклад, якщо ви хочете друкувати лише об'єкти Integer, які перевищують 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());    
    }

14

Використовуйте Java 8:

map.entrySet().forEach(entry -> System.out.println(entry.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

Ефективним ітеративним рішенням для карти є цикл "для кожного" з 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));
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.