Если у меня есть объект, реализующий интерфейс Map на Java, и я хочу перебрать каждую содержащуюся в нем пару, каков наиболее эффективный способ просмотра карты?
Будет ли порядок элементов зависеть от конкретной реализации карты, которая у меня есть для интерфейса?
Java 8: stackoverflow.com/questions/46898/…




Да, порядок зависит от конкретной реализации карты.
@ 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, чем while .. for (Iterator entries = myMap.entrySet (). Iterator (); entries.hasNext ();) {...} Благодаря этому синтаксису область 'записей' сокращается только до цикла for .
@jpredham Вы правы в том, что использование конструкции for в качестве for (Entry e : myMap.entrySet) не позволит вам изменять коллекцию, но в примере @HanuAthena упоминается, что она должна работать, поскольку дает вам Iterator в области видимости. (Если я чего-то не упускаю ...)
IntelliJ выдает ошибки на Entry thisEntry = (Entry) entries.next();: не распознает Entry. Этот псевдокод для чего-то другого?
@JohnK попробуйте импортировать java.util.Map.Entry.
Это решение не будет работать, если у вас есть целочисленный ключ и строковый ключ.
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
Если вы это сделаете, это не сработает, поскольку Entry является вложенным классом в Map. java.sun.com/javase/6/docs/api/java/util/Map.html
вы можете записать импорт как «import java.util.Map.Entry;» и это будет работать.
@Pureferret Единственная причина, по которой вы можете захотеть использовать итератор, - это вызов его метода remove. Если это так, этот другой ответ покажет вам, как это сделать. В противном случае лучше использовать расширенный цикл, показанный в ответе выше.
Я считаю, что форма Map.Entry более понятна, чем импорт внутреннего класса в текущее пространство имен.
где объявлена карта из map.entrySet ()?
Быстрый вопрос: должны ли общие значения <String, String> соответствовать значению карты? Например, у меня <String, Foo>.
Конечно, предполагается, что ваш Map называется map. Новички могут не понять.
@brandaemon Согласен. Даже для промежуточных звеньев приятно видеть, какой именно тип реализует метод entrySet.
Обратите внимание, что вы можете использовать map.values() или map.keySet(), если хотите перебирать только значения или ключи.
Это неэффективно. Карта содержит данные в наборе записей. Для скорости всегда перебирайте набор записей.
Я получаю ошибку несовместимых типов для записи «Map.Entry entry» и «Map.Entry <Integer, HashMap>»: «Требуемый объект, найдена запись». Я сопоставляю целые числа с хэш-картами. Из-за этого необходимо использовать набор ключей для цикла и вытащить значение через 'value = (HashMap) integertomap.get (key)', хотя и менее эффективно, чем если бы я мог использовать entrySet без итератора в соответствии с вашим примером.
@MarcusMacWilliam «Карта содержит данные в наборе записей» - это неверно для многих реализаций Map. См., Например, реализацию TreeMap, которая просматривает дерево для ключей, значений или записей.
@ ScArcher2 Нет, это не так. Это вложенный интерфейс, и даже если бы это был вложенный c, он все равно работал бы. Этот код работает. Использовал. Этому на 15 лет. Импорт не требуется.
Теоретически наиболее эффективный способ будет зависеть от того, какая реализация Map. Официальный способ сделать это - вызвать map.entrySet(), который возвращает набор Map.Entry, каждый из которых содержит ключ и значение (entry.getKey() и entry.getValue()).
В идиосинкразической реализации может иметь значение, используете ли вы map.keySet(), map.entrySet() или что-то еще. Но я не могу придумать причину, по которой кто-то мог бы написать это так. Скорее всего, для производительности не имеет значения то, что вы делаете.
И да, порядок будет зависеть от реализации, а также (возможно) порядка вставки и других факторов, которые трудно контролировать.
[править] Первоначально я написал valueSet(), но, конечно же, entrySet() - это ответ.
Типичный код для итерации по карте:
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 возвращает записи в естественном порядке ключей.
(Обновление: я думаю, что это уже не так.) Обратите внимание: итератор IdentityHashMapentrySet в настоящее время имеет особую реализацию, которая возвращает один и тот же экземпляр Map.Entry для каждого элемента в entrySet! Однако каждый раз, когда новый итератор продвигается, Map.Entry обновляется.
EnumMap также имеет это своеобразное поведение вместе с IdentityHashMap.
«LinkedHashMap будет либо возвращать записи в [...] порядке доступа [...]» ... чтобы вы обращались к элементам в порядке доступа к ним? Либо тавтологическое, либо что-то интересное, в котором можно сделать отступление. ;-)
@jpaugh Только прямой доступ к счетчику LinkedHashMap. Посылки через iterator, spliterator, entrySet и т. д. Не изменяют порядок.
1. хотя → если? 2. Последний абзац можно улучшить.
Это вопрос из двух частей:
Как перебирать записи на карте - @ ScArcher2 отлично имеет ответил.
Каков порядок итерации - если вы просто используете Map, то, строго говоря, есть нет гарантии заказа. Таким образом, вам не следует полагаться на порядок, заданный какой-либо реализацией. Однако интерфейс SortedMap расширяет Map и предоставляет именно то, что вы ищете - реализации всегда будут давать согласованный порядок сортировки.
NavigableMap - еще одно полезное расширение - это SortedMap с дополнительными методами поиска записей по их упорядоченной позиции в наборе ключей. Таким образом, потенциально это может в первую очередь избавить от необходимости повторения - вы сможете найти конкретный entry после использования методов higherEntry, lowerEntry, ceilingEntry или floorEntry. Метод descendingMap даже дает вам явный метод изменение порядка обхода.
К вашему сведению, вы также можете использовать map.keySet() и map.values(), если вас интересуют только ключи / значения карты, а не другие.
Правильный способ сделать это - использовать принятый ответ, так как он наиболее эффективен. Я считаю, что следующий код выглядит немного чище.
for (String key: map.keySet()) {
System.out.println(key + "/" + map.get(key));
}
Это не лучший подход, гораздо эффективнее использовать entrySet (). Findbugs отметит этот код (см. findbugs.sourceforge.net/…)
@ ДжеффОлсон, да, не совсем. поиск по карте - O (1), поэтому оба цикла ведут себя одинаково. по общему признанию, это будет немного медленнее в микробенчмарке, но я иногда делаю это, потому что ненавижу писать аргументы типа снова и снова. Кроме того, это, скорее всего, никогда не будет узким местом для вашей производительности, поэтому сделайте это, если это сделает код более читабельным.
@kritzikratzi, но с подходом entrySet () вы выполняете один поиск для каждого элемента, тогда как с подходом keySet () / get () вы выполняете два поиска для каждого элемента. Итак, теоретически (не тестировал) это O (1) против 2 * O (1). Или вдвое дольше. Правильно?
подробнее: O(1) = 2*O(1) - это в значительной степени определение нотации большой буквы О. вы правы в том, что он работает немного медленнее, но по сложности они такие же.
Поищите на карте O (1), правда?))))) Где на Земле это O (1)?))) HashMap: во-первых - вы должны вычислить хеш, во-вторых - искать в массиве по хешу, в-третьих! - линейный поиск по всем элементам, имеющим одинаковый хэш-код, это называется «столкновение», разве вы не слышали об этом? =) А что вы можете сказать о: TreeMap, ConcurrentSkipListMap они тоже «O (1)»?
@kornero хорошая точка, поиск по древовидной карте - O (log n), я имел в виду только хэш-карты (которые ~ O (1), коллизии или нет)
Коллизии @kritzikratzi в хэш-картах создают сложность ~ O (n), и на основе этого есть "отказ в обслуживании через алгоритмические атаки сложности", вы можете узнать больше об этом здесь: cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003
Под столкновением я имел в виду, что не имеет значения, есть ли у вас несколько столкновений, очевидно, это другая история, если у вас есть только столкновения. Итак, вы ведете себя довольно мелочно, но да, то, что вы говорите, правда.
@ Джефф Олсон: комментарии о том, что сложность «большого О» не меняется, когда есть только постоянный коэффициент, верны. Тем не менее, для меня важно, займет ли операция час или два. Что еще более важно, необходимо подчеркнуть, что коэффициент равен нет2, поскольку итерация по entrySet() вообще не требует поиска; это просто линейный обход всех записей. Напротив, итерация по keySet() и выполнение поиска по каждому ключу требует поиска один для каждого ключа, поэтому здесь мы говорим о нулевых поисках по сравнению с поисками п, где п - это размер Map. Так что фактор выходит далеко за рамки 2…
@kornero: стоит отметить, что вам не нужны ключи с одинаковым хэш-кодом для коллизии; коллизия уже есть, когда hashcode % capacity такой же. Начиная с Java 8, сложность элементов, имеющих один и тот же hashcode % capacity, но другой hashcode или Comparable, возвращается к O(log n), и только ключи с одинаковым хэш-кодом, не являющиеся Comparable, создают сложность O(n). Но утверждение, что на практике сложность поиска может быть больше, чем O(1), остается в силе.
Пример использования итератора и дженериков:
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, чтобы ограничить его область действия.
@SteveKuo Что вы имеете в виду, говоря «ограничить сферу охвата»?
@StudioWorks for (Iterator<Map.Entry<K, V>> entries = myMap.entrySet().iterator(); entries.hasNext(); ) { Map.Entry<K, V> entry = entries.next(); }. Используя эту конструкцию, мы ограничиваем область видимости entries (видимость переменной) циклом for.
@ComFreek Понятно. Не знал, что это так важно.
Попробуйте это с Java 1.4:
for( Iterator entries = myMap.entrySet().iterator(); entries.hasNext();){
Entry entry = (Entry) entries.next();
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();
}
}
}
С Коллекции 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.
Да, как многие согласились, это лучший способ перебрать Map.
Но есть шансы скинуть nullpointerexception, если карта null. Не забудьте поставить null. Check in.
|
|
- - - -
|
|
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.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<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.
Очень просто!
В зависимости от того, что вы хотите делать с картой, вы также можете использовать потоковый API для записей, возвращаемых map.entrySet().stream()docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.ht ml
Это не сработает, если вы хотите ссылаться на неокончательные переменные, объявленные вне вашего лямбда-выражения, из forEach () ...
@ Крис Верно. Это не сработает, если вы попытаетесь использовать переменные фактически не окончательный извне лямбда.
Если у вас есть общая нетипизированная карта, вы можете использовать:
Map map = new HashMap();
for (Map.Entry entry : ((Set<Map.Entry>) map.entrySet())) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
Есть несколько способов перебрать карту. См. Следующий код.
Когда вы повторяете карту с использованием интерфейса итератора, вы должны использовать Entry<K,V> или entrySet().
Это выглядит так:
import java.util.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class IteratMapDemo{
public static void main(String arg[]){
Map<String, String> mapOne = new HashMap<String, String>();
mapOne.put("1", "January");
mapOne.put("2", "February");
mapOne.put("3", "March");
mapOne.put("4", "April");
mapOne.put("5", "May");
mapOne.put("6", "June");
mapOne.put("7", "July");
mapOne.put("8", "August");
mapOne.put("9", "September");
mapOne.put("10", "Octomber");
mapOne.put("11", "November");
mapOne.put("12", "December");
Iterator it = mapOne.entrySet().iterator();
while(it.hasNext())
{
Map.Entry me = (Map.Entry) it.next();
//System.out.println("Get Key through While loop = " + me.getKey());
}
for(Map.Entry<String, String> entry:mapOne.entrySet()){
//System.out.println(entry.getKey() + " = " + entry.getValue());
}
for (Object key : mapOne.keySet()) {
System.out.println("Key: " + key.toString() + " Value: " +
mapOne.get(key));
}
}
}
keySet () работает медленно
В Map можно выполнять итерацию по 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);
}
Есть несколько способов перебрать карту.
Вот сравнение их производительности для общего набора данных, хранящегося в карте, путем сохранения миллиона пар ключевых значений в карте и повторения по карте.
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 миллисекунд
Время выполнения взято из статьи, в которой не используется Java Microbenchmarking Harness. Поэтому время ненадежно, так как код мог, например, быть полностью оптимизирован JIT-компилятором.
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());
}
Java 8:
Вы можете использовать лямбда-выражения:
myMap.entrySet().stream().forEach((entry) -> {
Object currentKey = entry.getKey();
Object currentValue = entry.getValue();
});
Для получения дополнительной информации следуйте это.
@injecteer: Похоже, мотив лямбда-выражений
Вам не нужен поток, если вы просто хотите перебирать карту. myMap.forEach( (currentKey,currentValue) -> /* action */ ); намного лаконичнее.
Я скопировал данные карты на другую с помощью этого кода:
HashMap product =(HashMap)shopping_truck.get(i);
HashMap tmp = new HashMap();
for (Iterator it = product.entrySet().iterator(); it.hasNext();) {
Map.Entry thisEntry = (Map.Entry) it.next();
tmp.put(thisEntry.getKey(), thisEntry.getValue());
}
Он не совсем отвечает на вопрос OP, но может быть полезен другим, кто найдет эту страницу:
Если вам нужны только значения, а не ключи, вы можете сделать это:
Map<Ktype, Vtype> myMap = [...];
for (Vtype v: myMap.values()) {
System.out.println("value: " + v);
}
Ktype, Vtype - это псевдокод.
Вот общий типобезопасный метод, который можно вызвать для сброса любого заданного Map.
import java.util.Iterator;
import java.util.Map;
public class MapUtils {
static interface ItemCallback<K, V> {
void handler(K key, V value, Map<K, V> map);
}
public static <K, V> void forEach(Map<K, V> map, ItemCallback<K, V> callback) {
Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<K, V> entry = it.next();
callback.handler(entry.getKey(), entry.getValue(), map);
}
}
public static <K, V> void printMap(Map<K, V> map) {
forEach(map, new ItemCallback<K, V>() {
@Override
public void handler(K key, V value, Map<K, V> map) {
System.out.println(key + " = " + value);
}
});
}
}
Вот пример его использования. Обратите внимание, что тип Map определяется методом.
import java.util.*;
public class MapPrinter {
public static void main(String[] args) {
List<Map<?, ?>> maps = new ArrayList<Map<?, ?>>() {
private static final long serialVersionUID = 1L;
{
add(new LinkedHashMap<String, Integer>() {
private static final long serialVersionUID = 1L;
{
put("One", 0);
put("Two", 1);
put("Three", 3);
}
});
add(new LinkedHashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("Object", new Object());
put("Integer", new Integer(0));
put("Double", new Double(0.0));
}
});
}
};
for (Map<?, ?> map : maps) {
MapUtils.printMap(map);
System.out.println();
}
}
}
One = 0
Two = 1
Three = 3
Object = java.lang.Object@15db9742
Integer = 0
Double = 0.0
Я считаю, что это самый простой способ сделать это ...
/* For example, this could be a map object */
Map<String, Integer> MAP = new Map<>();
// Do something like put keys/value pairs into the map, etc...
MAP.put("Denver", 35);
MAP.put("Patriots", 14);
/* Then, simply use a for each loop like this to iterate */
for (Object o : MAP.entrySet()) {
Map.Entry pair = (Map.Entry) o;
// Do whatever with the pair here (i.e. pair.getKey(), or pair.getValue();
}
Если ваша причина для итерации через Map, заключается в том, чтобы выполнить операцию со значением и записать в полученный Map. Рекомендую использовать transform-методы в классе Google GuavaMaps.
import com.google.common.collect.Maps;
После того, как вы добавили Maps в свой импорт, вы можете использовать Maps.transformValues и Maps.transformEntries на своих картах, например:
public void transformMap(){
Map<String, Integer> map = new HashMap<>();
map.put("a", 2);
map.put("b", 4);
Map<String, Integer> result = Maps.transformValues(map, num -> num * 2);
result.forEach((key, val) -> print(key, Integer.toString(val)));
// key=a,value=4
// key=b,value=8
Map<String, String> result2 = Maps.transformEntries(map, (key, value) -> value + "[" + key + "]");
result2.forEach(this::print);
// key=a,value=2[a]
// key=b,value=4[b]
}
private void print(String key, String val){
System.out.println("key = " + key + ",value = " + val);
}
У нас есть метод 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 не гарантирует соблюдение порядка. Также проверьте это для получения дополнительной информации.
Lambda Expression Java 8
В Java 1.8 (Java 8) это стало намного проще благодаря использованию метода для каждого из Aggregate operations (Потоковые операции), который похож на итераторы из интерфейса Итерабельный.
Просто скопируйте инструкцию вставки ниже в свой код и переименуйте переменную HashMap из хм в свою переменную 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. Для получения дополнительной информации о Лямбда перейдите к этому связь и должны прочитать Агрегированные операции, а для Spliterator перейдите к этому связь.
Если вы хотите перебрать карту в том порядке, в котором были добавлены элементы, используйте LinkedHashMap, а не только Map.
Этот подход работал у меня в прошлом:
LinkedHashMap<String,Integer> test=new LinkedHashMap();
test.put("foo",69);
test.put("bar",1337);
for(int i=0;i<test.size();i++){
System.out.println(test.get(test.keySet().toArray()[i]));
}
Выход:
69
1337
Чтобы обобщить другие ответы и объединить их с тем, что я знаю, я нашел 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();
}
Использование для каждого и Map.Entry
long i = 0;
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
i += pair.getKey() + pair.getValue();
}
Использование для каждого из Java 8
final long[] i = {0};
map.forEach((k, v) -> i[0] += k + v);
Использование keySet и для каждого
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);
}
Использование за и 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());
Использование Java 8 Параллельный Stream API
final long[] i = {0};
map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
Использование IterableMapApache 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;
});
Тесты производительности (режим = Среднее время, система = 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.
@Viacheslav: очень хороший ответ. Просто интересно, как API Java8 затрудняются в вашем тесте из-за захвата лямбда-выражений ... (например, long sum = 0; map.forEach( /* accumulate in variable sum*/); захватывает sum long, что может быть медленнее, например, stream.mapToInt(/*whatever*/).sum. Конечно, вы не всегда можете избежать захвата состояния, но это может быть разумное дополнение к скамейке.
Ваш 8-й тест неверен. он обращается к одной и той же переменной из разных потоков без синхронизации. Чтобы решить проблему, перейдите на AtomicInteger.
Почему Java 8 forEach медленнее на 100000 элементов, тогда как он был самым быстрым на 100 и 10000?
@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, а вы знать, что эти результаты тестов бессмысленны.
Что насчет map.entrySet().stream().[parallel().]mapToInt(e -> e.getKey() + e.getValue()).sum();?
А как насчет сравнения трех основных реализаций: HashMap, LinkedHashMap и TreeMap?
№1 и №6 абсолютно одинаковы. Использование while по сравнению с циклом for - это не другой метод итерации. И я удивлен, что между ними есть такие различия в ваших тестах, что говорит о том, что тесты не изолированы должным образом от внешних факторов, не связанных с тем, что вы собираетесь тестировать.
для #7 используйте map + reduce, а не forEach.
#8 - ужасный пример, поскольку parallel теперь вызывает состояние гонки при добавлении к i.
.stream() не нужен для forEach, поскольку он также определен в интерфейсе Iterable.
map.values () дает набор значений в данной карте. Это может быть кому-то полезно.
Проголосовали против, потому что этот тест совершенно неработоспособен и ненадежен, как уже заметили другие комментаторы.
Эта логика не выглядит надежной, поскольку я думаю, что нет гарантии, будет ли сначала оцениваться левый или правый операнд +, а .next() может иметь побочные эффекты: i += it.next() + it.getValue()
Интересно. Однако те, в которых вы должны использовать "array-hack", чтобы избежать замыкания, могут иметь недостаток? Может быть, было интересно позволить им всем пользоваться этим? Или, может быть, даже какой-то «объект-сборщик», может быть, AtomicLong или что-то в этом роде, который не может быть легко исключен jit?
//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();
}
Порядок всегда будет зависеть от конкретной реализации карты. Используя Java 8, вы можете использовать любое из них:
map.forEach((k,v) -> { System.out.println(k + ":" + v); });
Или же:
map.entrySet().forEach((e) -> {
System.out.println(e.getKey() + " : " + e.getValue());
});
Результат будет таким же (в том же порядке). EntrySet поддерживается картой, поэтому вы получаете тот же порядок. Второй удобен тем, что позволяет использовать лямбды, например если вы хотите печатать только объекты 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,
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));
}
}
Итерация карта очень проста.
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);
}
Что ж, это излишне медленно, потому что сначала получите ключи, а затем записи. Альтернатива: получите набор записей, а затем для каждой записи установите ключ и значение
Есть много способов сделать это. Ниже приведены несколько простых шагов:
Предположим, у вас есть одна карта вроде:
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));
}
Используйте Java 8:
map.entrySet().forEach(entry -> System.out.println(entry.getValue()));
Самый компактный с Java 8:
map.entrySet().forEach(System.out::println);
С помощью Java 8 вы можете перебирать Map, используя forEach и лямбда-выражение,
map.forEach((k, v) -> System.out.println((k + ":" + v)));
Эффективным итеративным решением для 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");
}
});
Мне нравится объединять счетчик, а затем сохранять окончательное значение счетчика;
int counter = 0;
HashMap<String, String> m = new HashMap<String, String>();
for(int i = 0;i<items.length;i++)
{
m.put("firstname"+i, items.get(i).getFirstName());
counter = i;
}
m.put("recordCount",String.valueOf(counter));
Затем, когда вы захотите получить:
int recordCount = Integer.parseInf(m.get("recordCount"));
for(int i =0 ;i<recordCount;i++)
{
System.out.println("First Name :" + m.get("firstname"+i));
}
Using Java 7
Map<String,String> sampleMap = new HashMap<>();
for (sampleMap.Entry<String,String> entry : sampleMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
/* your Code as per the Business Justification */
}
Using Java 8
Map<String,String> sampleMap = new HashMap<>();
sampleMap.forEach((k, v) -> System.out.println("Key is : " + k + " Value is : " + v));
If I have an object implementing the Map interface in Java and I wish to iterate over every pair contained within it, what is the most efficient way of going through the map?
Если эффективность зацикливания ключей является приоритетом для вашего приложения, выберите реализацию Map, которая поддерживает ключи в желаемом порядке.
Will the ordering of elements depend on the specific map implementation that I have for the interface?
Да, конечно.
Map обещают определенный порядок итераций, другие - нет.Map поддерживают разный порядок пар ключ-значение.См. Эту таблицу, которую я создал, обобщая различные реализации Map в комплекте с Java 11. В частности, обратите внимание на столбец порядок итераций. Щелкните / коснитесь для увеличения.
Вы можете видеть, что есть четыре реализации Map, поддерживающие порядок:
TreeMapConcurrentSkipListMapLinkedHashMapEnumMapNavigableMap интерфейсДва из них реализуют интерфейс NavigableMap: TreeMap и ConcurrentSkipListMap.
Старый интерфейс SortedMap фактически заменен новым интерфейсом NavigableMap. Но вы можете найти сторонние реализации, реализующие только старый интерфейс.
Если вам нужен Map, который хранит свои пары в «естественном порядке» ключа, используйте TreeMap или ConcurrentSkipListMap. Термин «естественный порядок» означает, что класс ключей реализует Comparable. Значение, возвращаемое методом compareTo, используется для сравнения при сортировке.
Если вы хотите указать настраиваемую процедуру сортировки для ваших ключей, которая будет использоваться при поддержании отсортированного порядка, передайте реализацию Comparator, соответствующую классу ваших ключей. Используйте TreeMap или ConcurrentSkipListMap, передав свой Comparator.
Если вы хотите, чтобы пары вашей карты оставались в том порядке, в котором вы их вставляли на карту, используйте LinkedHashMap.
Если вы используете в качестве ключей перечисление, такое как DayOfWeek или Month, используйте класс EnumMap. Этот класс высоко не только оптимизирован для использования очень небольшого объема памяти и очень быстрой работы, но и поддерживает ваши пары в порядке, определенном перечислением. Например, для DayOfWeek ключ DayOfWeek.MONDAY будет найден первым при повторении, а ключ DayOfWeek.SUNDAY будет последним.
При выборе реализации Map также учитывайте:
Collections::synchronizedMap (менее предпочтительно).Оба эти соображения отражены в графической таблице выше.
Поздний комментарий к ответу, который тоже опаздывает на вечеринку (но очень информативный). +1 от меня за упоминание EnumMap, так как я впервые слышу о нем. Вероятно, есть много случаев, когда это может пригодиться.
Map.forEachКак насчет простого использования Map::forEach, когда и ключ, и значение передаются в ваш BiConsumer?
map.forEach((k,v)->{
System.out.println(k+"->"+v);
});
Это было описано в Ответ Ловы Читтумури. Также рассматривается как пункт 3 в Ответ Вячеслава Веденина, получившем большое количество голосов.
Но доступно только на этикетке API 24.
Начиная с Java 10, вы можете использовать вывод локальной переменной (также известный как «var»), чтобы сделать многие уже доступные ответы менее раздутыми. Например:
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
Map<String, String> map =
for (Map.Entry<String, String> entry : map.entrySet()) {
MapKey = entry.getKey()
MapValue = entry.getValue();
}
Вы можете найти ключ и с его помощью найти связанное значение карты, так как карта имеет уникальный ключ, посмотрите, что происходит, когда ключ дублируется здесь или здесь.
Демо-карта:
Map<String, String> map = new HashMap();
map.put("name", "Badri Paudel");
map.put("age", "23");
map.put("address", "KTM");
map.put("faculty", "BE");
map.put("major", "CS");
map.put("head", "AVD");
Чтобы получить только ключ, вы можете использовать map.keySet(); следующим образом:
for(String key : map.keySet()) {
System.out.println(key);
}
Чтобы получить только значение, вы можете использовать map.values(); следующим образом:
for(String value : map.values()) {
System.out.println(value);
}
Чтобы получить и ключ, и его значение, вы все равно можете использовать map.keySet(); и получить соответствующее значение, например:
//this prints the key value pair
for (String k : map.keySet()) {
System.out.println(k + " " + map.get(k) + " ");
}
map.get(key) дает значение, указанное этим ключом.
В Java 8 с использованием лямбда-выражения: stackoverflow.com/a/25616206/1503859