Решение Apache Commons: ListOrderedMap
Поскольку JDK LinkedHashMap
обеспечивает только извлечение порядка вставки, в случае, если вы хотите вставить индекс, мы можем использовать Apache Commons альтернативно 'ListOrderedMap
. Он делает это так, как кажется - имея список для поддержания порядка вставки с соответствующим индексом и карту нормалей для вставки, как мы обычно это делаем. Вот что говорят документы :
public class ListOrderedMap<K,V>
extends AbstractMapDecorator<K,V>
implements OrderedMap<K,V>, Serializable
Украшает Map
, чтобы обеспечить сохранение порядка добавления
использование списка для поддержания порядка.
Порядок будет использоваться через итераторы и toArray
методы на
Просмотры. Заказ также возвращается MapIterator
.
orderedMapIterator()
метод обращается к итератору, который может перебирать
и вперед и назад по карте. К тому же,
предоставляются неинтерфейсные методы для доступа к карте по индексу.
Если объект добавлен на карту во второй раз, он останется в
исходная позиция в итерации.
Обратите внимание, что ListOrderedMap
не синхронизируется и не является поточно-ориентированным.
Если вы хотите использовать эту карту из нескольких потоков одновременно, вы
необходимо использовать соответствующую синхронизацию. Самый простой подход - завернуть
эта карта, используя Collections.synchronizedMap(Map)
. Этот класс может
генерировать исключения при обращении к параллельным потокам без
синхронизации.
Обратите внимание, что ListOrderedMap
не работает с IdentityHashMap
,
CaseInsensitiveMap
, или аналогичные карты, которые нарушают общие
контракт Map
. ListOrderedMap
(или, точнее,
базовый List
) полагается на equals()
. Это хорошо, пока
декорированный Map
также основан на equals()
и hashCode()
,
которые IdentityHashMap
и CaseInsensitiveMap
не делают: бывший
использует ==
, а последний использует equals()
для клавиши в нижнем регистре.
Вот его реализация для добавления в позицию:
/**
428 * Puts a key-value mapping into the map at the specified index.
429 * <p>
430 * If the map already contains the key, then the original mapping
431 * is removed and the new mapping added at the specified index.
432 * The remove may change the effect of the index. The index is
433 * always calculated relative to the original state of the map.
434 * <p>
435 * Thus the steps are: (1) remove the existing key-value mapping,
436 * then (2) insert the new key-value mapping at the position it
437 * would have been inserted had the remove not occurred.
438 *
439 * @param index the index at which the mapping should be inserted
440 * @param key the key
441 * @param value the value
442 * @return the value previously mapped to the key
443 * @throws IndexOutOfBoundsException if the index is out of range [0, size]
444 * @since 3.2
445 */
446 public V put(int index, final K key, final V value) {
447 if (index < 0 || index > insertOrder.size()) {
448 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + insertOrder.size());
449 }
450
451 final Map<K, V> m = decorated();
452 if (m.containsKey(key)) {
453 final V result = m.remove(key);
454 final int pos = insertOrder.indexOf(key);
455 insertOrder.remove(pos);
456 if (pos < index) {
457 index--;
458 }
459 insertOrder.add(index, key);
460 m.put(key, value);
461 return result;
462 }
463 insertOrder.add(index, key);
464 m.put(key, value);
465 return null;
466 }