Что можно сделать, чтобы гарантировать порядок обхода элементов в словаре?
Что проверяют
Знаете ли вы реализации Map в стандартной библиотеке и понимаете ли, что у HashMap порядок обхода не определён вовсе. За вопросом обычно стоит реальный баг: где-то полагались на то, что элементы вернутся так же, как их клали.
Как отвечать
Порядок задаётся выбором реализации, а не перебором:
LinkedHashMapхранит порядок вставки за счёт двусвязного списка поверх хэш-таблицы; конструктор с флагом порядка доступа плюс переопределённыйremoveEldestEntryдают готовый LRU-кэш;TreeMapупорядочивает по ключу, естественным порядком или переданнымComparator, и умеет диапазонные операции вродеheadMapиceilingKey;ConcurrentSkipListMapдаёт тот же порядок по ключу, но потокобезопасен, тогда как уConcurrentHashMapпорядка нет;EnumMapобходит константы в порядке их объявления.
По стоимости: LinkedHashMap это константный доступ и немного лишней памяти, TreeMap даёт логарифм на операцию, зато сортировку и поиск по диапазону.
Если тип структуры менять нельзя, порядок наводят на месте обхода: отсортировать entrySet потоком и собрать результат в LinkedHashMap. Важно указать его явно, потому что обычный сборщик в Map вернёт HashMap и порядок снова потеряется.
Где спотыкаются
Полагаются на то, что HashMap обычно отдаёт элементы в порядке вставки. Порядок зависит от хэшей и текущей ёмкости и меняется при перестроении таблицы; совпадение на маленьких целочисленных ключах случайно и ломается на проде.
Теряют гарантию на границе. Метод создаёт LinkedHashMap, объявляет возвращаемый тип как Map и нигде не пишет, что порядок значим, после чего следующая правка спокойно подменяет реализацию. Фабрики неизменяемых коллекций вида Map.of порядок намеренно перемешивают.
Ломают TreeMap ключами. Компаратор, несогласованный с equals, приводит к тому, что элементы теряются или дублируются, а изменение поля ключа после вставки делает запись ненаходимой. Та же беда у HashMap, если менять поля, участвующие в hashCode.