Apache Commons solution : ListOrderedMap
Since the JDK's LinkedHashMap ensures only insertion order retrieval, in case you we want to insert at an index, we can use alternatively Apache Commons' ListOrderedMap. It does it as it sounds - by having a list to maintain the order of insertion with the corresponding index and a normal map to insert as we generally do. Here is what the docs say:
public class ListOrderedMap<K,V>
extends AbstractMapDecorator<K,V>
implements OrderedMap<K,V>, Serializable
Decorates a Map to ensure that the order of addition is retained
using a List to maintain order.
The order will be used via the iterators and toArray methods on the
views. The order is also returned by the MapIterator. The
orderedMapIterator() method accesses an iterator that can iterate
both forwards and backwards through the map. In addition,
non-interface methods are provided to access the map by index.
If an object is added to the Map for a second time, it will remain in
the original position in the iteration.
Note that ListOrderedMap is not synchronized and is not thread-safe.
If you wish to use this map from multiple threads concurrently, you
must use appropriate synchronization. The simplest approach is to wrap
this map using Collections.synchronizedMap(Map). This class may
throw exceptions when accessed by concurrent threads without
synchronization.
Note that ListOrderedMap doesn't work with IdentityHashMap,
CaseInsensitiveMap, or similar maps that violate the general
contract of Map. The ListOrderedMap (or, more precisely, the
underlying List) is relying on equals(). This is fine, as long as
the decorated Map is also based on equals(), and hashCode(),
which IdentityHashMap, and CaseInsensitiveMap don't: The former
uses ==, and the latter uses equals() on a lower-cased key.
Here is its implementation for adding to a position:
/**
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 }