Итерация по EnumMap, которая не приводит к созданию нового объекта за одну итерацию
Есть ли способ перебора EnumMap, который не приводит к созданию нового объекта за одну итерацию? Итератор набора записей каждый раз возвращает новую запись. Единственный способ увидеть это
for(K k: map.keySet())
foo(k, map.get(k));
Чтобы пояснить это конкретно о EnumMap, который имеет следующую реализацию итератора на своем EntrySet
public Map.Entry<K,V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedEntry = new Entry(index++);
return lastReturnedEntry;
}
3 ответа
Прежде всего, из того, что вы говорите, кажется, что вы хотите, чтобы итератор возвращал кортеж из двух объектов.
В Java единственный способ сделать это - обернуть их в другой объект. (На момент написания, то есть.) Таким образом, итератор должен вернуть объект, отличный от ключа и значения. Этот объект должен быть создан в какой-то момент перед вызовом next()
возвращается.
Учитывая это ограничение, есть три возможных пути:
- Создать этот объект записи на
put()
, - Создайте объект ввода на первой итерации по
entrySet()
(но кешировать это потом). - Создайте новый объект записи на каждой итерации
entrySet()
,
Встроенный EnumMap
выбрал вариант 3 по тем причинам, по которым его проще всего реализовать и что это наиболее экономичное решение, если вам не нужно перебирать записи. Недостатком является то, что если вам нужно выполнить итерацию более одного раза, вы создаете больше объектов, чем любое другое решение.
Вариант 1 такой же простой в реализации, но при добавлении записи на карту возникают очевидные накладные расходы, даже если вы никогда не намереваетесь получить к ней доступ.
Наконец, вариант 2 включает немного большую сложность кода и пару крайних случаев, когда вы чередуете итерацию и добавляете больше элементов, но это дает вам лучший профиль памяти в теории.
Если в приложении возникает проблема с нехваткой памяти для нескольких итераций, вы можете легко реализовать вариант 2, но я сомневаюсь, что в большинстве случаев разница будет заметна.
PS: если вы готовы отклониться от идиоматических решений и перейти на слегка безумную территорию, вы можете повторно использовать то же самое Map.Entry
экземпляр для всех ваших записей. Это будет очевидно противоречить тому, что мы ожидаем от Map.Entry
, но он предлагает вам минимальные накладные расходы на выделение памяти, и вы можете справиться с этим в простых итерационных сценариях. Кто-то может догадаться, что у вас получится более быстрый конечный продукт, вам нужно его измерить.
Я рефлекторно сомневаюсь в законности такого уровня озабоченности по поводу создания объектов. Но если избегать создания объектов действительно так важно, вы можете сохранить свой собственный массив констант enum и проверить map.contains(...)
для каждого. Вы должны проверить это, чтобы увидеть сравнение производительности.
Во-первых, ваш код не создает объект каждый раз. Он получает только ссылку на существующий объект.
Да, есть лучший способ:
for (Map.Entry<K, V> entry : map.entrySet()) {
// use entry.getKey() and entry.getValue()
}
Или версия Java 8:
map.forEach((k, v) -> {...});