Если у меня есть объект, реализующий интерфейс Map
в Java, и я хочу перебрать каждую содержащуюся в нем пару, каков наиболее эффективный способ просмотра карты?
Будет ли порядок элементов зависеть от конкретной реализации карты, которая у меня есть для интерфейса?
30 ответов
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
На Java 10+:
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
remove
. В этом случае другой ответ покажет вам, как это сделать. В противном случае лучше использовать расширенный цикл, показанный в ответе выше.
map.values()
или map.keySet()
, если хотите перебирать только значения или ключи.
В 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
.
Очень просто!
map.entrySet().stream()
docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
Да, порядок зависит от конкретной реализации карты.
@ 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();
// ...
}
for
как for (Entry e : myMap.entrySet)
не позволит вам изменять коллекцию, но в примере как @HanuAthena упоминается, что она должна работать, поскольку дает вам Iterator
в области видимости . (Если я чего-то не упускаю ...)
Entry thisEntry = (Entry) entries.next();
: не распознает Entry
. Этот псевдокод для чего-то другого?
java.util.Map.Entry
.
Типичный код для итерации по карте:
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
обновляется.
LinkedHashMap
. Те, кто через iterator
, spliterator
, entrySet
и т. Д., Не изменяют порядок.
Пример использования итератора и дженериков:
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();
// ...
}
Iterator
в цикл for, чтобы ограничить его область действия.
for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }
. Используя эту конструкцию, мы ограничиваем область (видимость переменной) entries
циклом for.
Это вопрос из двух частей:
Как перебирать записи карты - @ ScArcher2 отлично ответил на это.
Каков порядок итерации - если вы просто используете Map
, то, строго говоря, нет гарантий заказа . Таким образом, вам не следует полагаться на порядок, заданный какой-либо реализацией. Однако интерфейс SortedMap
extends Map
и предоставляет именно то, что вы ищете - реализации всегда будут давать согласованный порядок сортировки.
NavigableMap
- это еще одно полезное расширение - это SortedMap
с дополнительными методами поиска записей по их упорядоченной позиции в наборе ключей. Таким образом, потенциально это может в первую очередь избавить от необходимости повторения - вы сможете найти конкретный entry
, который у вас есть, после использования higherEntry
, lowerEntry
, ceilingEntry
, или floorEntry
методы. Метод descendingMap
даже дает вам явный метод изменения порядка обхода .
Есть несколько способов перебрать карту.
Вот сравнение их производительности для общего набора данных, хранящегося в карте, путем сохранения миллиона пар ключевых значений в карте и повторения по карте.
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
.
К вашему сведению, вы также можете использовать map.keySet()
и map.values()
, если вас интересуют только ключи / значения карты, а не другие.
В Java 8 можно выполнять итерацию Map, используя forEach и лямбда-выражение,
map.forEach((k, v) -> System.out.println((k + ":" + v)));
Используя Коллекции 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 Collections.
Теоретически наиболее эффективный способ будет зависеть от того, какая реализация Map. Официальный способ сделать это - вызвать map.entrySet()
, который возвращает набор Map.Entry
, каждый из которых содержит ключ и значение (entry.getKey()
и entry.getValue()
).
В идиосинкразической реализации может иметь значение, используете ли вы map.keySet()
, map.entrySet()
или что-то еще. Но я не могу придумать причину, по которой кто-то мог бы написать это так. Скорее всего, для производительности не имеет значения то, что вы делаете.
И да, порядок будет зависеть от реализации, а также (возможно) порядка вставки и других факторов, которые трудно контролировать.
[править] Изначально я написал valueSet()
, но, конечно же, entrySet()
- это ответ.
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
не гарантирует, что порядок будет сохранен. Также проверьте это, чтобы узнать больше.
Лямбда выражение Java 8
В Java 1.8 (Java 8) это стало намного проще благодаря использованию метода forEach из агрегированных операций ( потоковые операции ), который похож на итераторы из Iterable Интерфейс.
Просто скопируйте приведенную ниже инструкцию в свой код и переименуйте переменную 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.
Ниже приведен пример кода, который я пытался использовать с помощью Лямбда-выражения . Это так круто. Должен попытаться.
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 , перейдите по этой ссылке a> и должен прочитать Aggregate Operations, а для Spliterator перейти к этому ссылка.
Java 8:
Вы можете использовать лямбда-выражения:
myMap.entrySet().stream().forEach((entry) -> {
Object currentKey = entry.getKey();
Object currentValue = entry.getValue();
});
Для получения дополнительной информации следуйте этому.
myMap.forEach( (currentKey,currentValue) -> /* action */ );
намного лаконичнее.
На карте можно выполнять итерацию по keys
и / или values
и / или both (e.g., entrySet)
в зависимости от того, что вас интересует. Например:
Выполните итерации по
keys -> keySet()
карты:Map<String, Object> map = ...; for (String key : map.keySet()) { //your Business logic... }
Выполните итерации по
values -> values()
карты:for (Object value : map.values()) { //your Business logic... }
Выполните итерации по
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);
}
Попробуйте это с Java 1.4:
for( Iterator entries = myMap.entrySet().iterator(); entries.hasNext();){
Entry entry = (Entry) entries.next();
System.out.println(entry.getKey() + "/" + entry.getValue());
//...
}
Порядок всегда будет зависеть от конкретной реализации карты. Используя Java 8, вы можете использовать любое из них:
map.forEach((k,v) -> { System.out.println(k + ":" + v); });
Или же:
map.entrySet().forEach((e) -> {
System.out.println(e.getKey() + " : " + e.getValue());
});
Результат будет таким же (в том же порядке). EntitySet поддерживается картой, поэтому вы получаете тот же порядок. Второй удобен тем, что позволяет использовать лямбды, например если вы хотите печатать только объекты 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,
Самый компактный с Java 8:
map.entrySet().forEach(System.out::println);
Если у меня есть объект, реализующий интерфейс карты в Java, и я хочу перебрать каждую содержащуюся в нем пару, какой способ просмотра карты является наиболее эффективным?
Если эффективность зацикливания ключей является приоритетом для вашего приложения, выберите реализацию Map
, которая поддерживает ключи в желаемом порядке.
Будет ли порядок элементов зависеть от конкретной реализации карты, которая у меня есть для интерфейса?
Да, конечно.
- Некоторые реализации
Map
обещают определенный порядок итераций, другие - нет. - Различные реализации
Map
поддерживают разный порядок пар "ключ-значение".
См. Составленную мной таблицу, в которой представлены различные реализации Map
, связанные с Java 11. В частности, обратите внимание на столбец порядок итераций . Щелкните / коснитесь для увеличения.
Как видите, существует четыре Map
реализации порядка :
TreeMap
ConcurrentSkipListMap
LinkedHashMap
EnumMap
NavigableMap
интерфейс
Два из них реализуют NavigableMap
интерфейс: TreeMap
& ConcurrentSkipListMap
.
Более старый SortedMap
интерфейс заменен более новым интерфейсом NavigableMap
интерфейс. Но вы можете найти сторонние реализации, реализующие только старый интерфейс.
Естественный порядок
Если вам нужен Map
, в котором пары расположены в «естественном порядке» ключа, используйте TreeMap
или ConcurrentSkipListMap
. Термин «естественный порядок» означает, что класс ключей реализует Comparable
. Значение, возвращаемое compareTo
для сравнения при сортировке.
Изготовленный на заказ заказ
Если вы хотите указать настраиваемую процедуру сортировки для ваших ключей, которая будет использоваться для поддержания порядка сортировки, передайте Comparator
реализация, соответствующая классу ваших ключей. Используйте либо TreeMap
или ConcurrentSkipListMap
, передав ваш Comparator
.
Исходный заказ на размещение
Если вы хотите, чтобы пары вашей карты оставались в том порядке, в котором вы их вставляли на карту, используйте LinkedHashMap
.
Порядок определения перечисления
Если вы используете перечисление, например DayOfWeek
или Month
в качестве ключей, используйте EnumMap
класс. Этот класс не только высоко оптимизирован для использования очень небольшого объема памяти и очень быстрой работы, но и поддерживает ваши пары в порядке, определенном перечислением. Например, для DayOfWeek
ключ DayOfWeek.MONDAY
будет найден первым при повторении, а ключ DayOfWeek.SUNDAY
будет последним.
Другие соображения
При выборе реализации Map
также учитывайте:
- NULL. Некоторые реализации запрещают / принимают NULL как ключ и / или значение.
- Параллелизм. Если вы управляете картой между потоками, вы должны использовать реализацию, поддерживающую параллелизм. Или оберните карту
Collections::synchronizedMap
(менее предпочтительно).
Оба эти соображения отражены в графической таблице выше.
EnumMap
, поскольку я впервые слышу о нем. Вероятно, есть много случаев, когда это может пригодиться.
Если у вас есть общая нетипизированная карта, вы можете использовать:
Map map = new HashMap();
for (Map.Entry entry : ((Set<Map.Entry>) map.entrySet())) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
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();
}
}
}
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());
}
Вы можете сделать это с помощью дженериков:
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());
}
Используйте Java 8:
map.entrySet().forEach(entry -> System.out.println(entry.getValue()));
Эффективное итеративное решение для Map - это цикл for
от 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");
}
});
//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();
}
Есть много способов сделать это. Ниже приведены несколько простых шагов:
Предположим, у вас есть одна карта вроде:
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));
}
Итерация Map очень проста.
for(Object key: map.keySet()){
Object value= map.get(key);
//Do your stuff
}
Например, у вас есть Map<String, int> data;
for(Object key: data.keySet()){
int value= data.get(key);
}
Правильный способ сделать это - использовать принятый ответ, так как он наиболее эффективен. Я считаю, что следующий код выглядит немного чище.
for (String key: map.keySet()) {
System.out.println(key + "/" + map.get(key));
}
O(1) = 2*O(1)
в значительной степени является определением нотации большого O. вы правы в том, что он работает немного медленнее, но по сложности они такие же.
2
, поскольку итерация по entrySet()
вообще не требует поиска; это просто линейный обход всех записей. Напротив, итерация по keySet()
и выполнение поиска по каждому ключу несет один поиск по каждому ключу, поэтому здесь мы говорим о нулевых поисках по сравнению с n поисками. , n - размер Map
. Так что фактор выходит далеко за рамки 2
…
hashcode % capacity
то же самое. Начиная с Java 8, сложность для элементов, имеющих одинаковый hashcode % capacity
, но разные hashcode
или Comparable
, возвращается к O(log n)
, и только ключи с одинаковым хэш-кодом, а не бытие Comparable
налагает O(n)
сложность. Но утверждение о том, что на практике сложность поиска может превышать O(1)
, остается в силе.
Чтобы обобщить другие ответы и объединить их с тем, что я знаю, я нашел 10 основных способов сделать это (см. Ниже). Также я написал несколько тестов производительности (см. Результаты ниже). Например, если мы хотим найти сумму всех ключей и значений карты, мы можем написать:
Использование итератора и 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(); }
Использование foreach и Map.Entry
long i = 0; for (Map.Entry<Integer, Integer> pair : map.entrySet()) { i += pair.getKey() + pair.getValue(); }
Использование forEach из Java 8
final long[] i = {0}; map.forEach((k, v) -> i[0] += k + v);
Использование keySet и foreach
long i = 0; for (Integer key : map.keySet()) { i += key + map.get(key); }
Использование keySet и итератора
long i = 0; Iterator<Integer> itr2 = map.keySet().iterator(); while (itr2.hasNext()) { Integer key = itr2.next(); i += key + map.get(key); }
Использование for и 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(); }
Использование Java 8 Stream API
final long[] i = {0}; map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
Использование параллельного потокового API Java 8
final long[] i = {0}; map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
Использование IterableMap из
Apache Collections
long i = 0; MapIterator<Integer, Integer> it = iterableMap.mapIterator(); while (it.hasNext()) { i += it.next() + it.getValue(); }
Использование MutableMap коллекций 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 ГБ)
Для небольшой карты (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
Для карты с 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
Для карты со 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.
long sum = 0; map.forEach( /* accumulate in variable sum*/);
захватывает длинную длину sum
, что может быть медленнее, чем, например, stream.mapToInt(/*whatever*/).sum
. Конечно вы не всегда можете избежать захвата состояния, но это может быть разумным дополнением к скамейке.
x±e
подразумевает, что был результат в интервале от x-e
до x+e
, поэтому самый быстрый результат (1184.767±332.968
) находится в диапазоне от 852
до 1518
, тогда как второй самый медленный (1706.676±436.867
) выполняется между 1270
и 2144
, поэтому результаты все равно значительно перекрываются. Теперь посмотрите на самый медленный результат, 3289.866±1445.564
, который подразумевает расхождение между 1844
и 4735
, и вы знаете , что эти результаты теста бессмысленны.
while
по сравнению с циклом for
- это не другой метод итерации. И я удивлен, что между ними есть такие различия в ваших тестах, что говорит о том, что тесты не изолированы должным образом от внешних факторов, не связанных с тем, что вы собираетесь тестировать.
#8
- ужасный пример, потому что parallel
теперь возникает состояние гонки при добавлении к i
.
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.