59

我有一个有序的 LinkedHashMap,我想在特定索引处添加元素,比如在地图的第一位或最后一位。如何在 LinkedHashMap 中的特定位置添加元素?

即使我可以在 LinkedHashMap 的第一个或最后一个位置添加一个元素也会有所帮助!

4

9 回答 9

28
于 2014-11-30T15:22:18.303 回答
26

您不能更改顺序。它是insert-order(默认情况下)或access-order使用此构造函数:

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

  • 构造一个具有指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。

  • 参数:initialCapacity - 初始容量 loadFactor - 负载因子 accessOrder - 排序模式 - 访问顺序为 true,插入顺序为 false

  • 抛出: IllegalArgumentException - 如果初始容量为负或负载因子为非正

看:LinkedHashMap

于 2011-10-06T20:08:12.117 回答
15

Apache Commons 解决方案:ListOrderedMap

由于 JDKLinkedHashMap确保只检索插入顺序,如果您想在索引处插入,我们可以使用 Apache Commons 的替代方法ListOrderedMap。它就像听起来一样 - 通过一个列表来维护插入顺序以及相应的索引和一个法线贴图来插入,就像我们通常做的那样。这是文档所说的:

public class ListOrderedMap<K,V>
extends AbstractMapDecorator<K,V>
implements OrderedMap<K,V>, Serializable

装饰 aMap以确保使用 List 保持添加顺序以保持顺序。

该顺序将通过toArray视图上的迭代器和方法使用。订单也由MapIterator. 该 orderedMapIterator()方法访问一个迭代器,该迭代器可以在映射中向前和向后迭代。此外,还提供了非接口方法来按索引访问地图。

如果第二次将对象添加到 Map 中,它将在迭代中保持在原始位置。

请注意,ListOrderedMap它不是同步的,也不是线程安全的。如果您希望从多个线程同时使用此映射,则必须使用适当的同步。最简单的方法是使用Collections.synchronizedMap(Map). 这个类在被没有同步的并发线程访问时可能会抛出异常。

请注意,这ListOrderedMap不适用于IdentityHashMapCaseInsensitiveMap或违反 的一般合同的类似地图Map。(ListOrderedMap或更准确地说,底层的List)依赖于equals(). 这很好,只要装饰Map也基于equals(), and hashCode(), whichIdentityHashMapCaseInsensitiveMapdon't :前者使用==, 后者使用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    }
于 2017-06-11T12:37:06.677 回答
7
public static <K, V> void add(LinkedHashMap<K, V> map, int index, K key, V value) {
  assert (map != null);
  assert !map.containsKey(key);
  assert (index >= 0) && (index < map.size());

  int i = 0;
  List<Entry<K, V>> rest = new ArrayList<Entry<K, V>>();
  for (Entry<K, V> entry : map.entrySet()) {
    if (i++ >= index) {
      rest.add(entry);
    }
  }
  map.put(key, value);
  for (int j = 0; j < rest.size(); j++) {
    Entry<K, V> entry = rest.get(j);
    map.remove(entry.getKey());
    map.put(entry.getKey(), entry.getValue());
  }
}
于 2014-01-10T20:57:40.913 回答
6

只需将您分成LinkedHashMap2 个阵列。制作第一个具有大小的数组index - 1并放在最后 new Entry。然后用第二个数组的条目填充第一个数组

于 2012-10-30T13:00:09.627 回答
1

它是一个Map,它没有索引。它有桶。它的工作方式是当你做一个

put(key, val)

它对密钥进行哈希处理以找出将 val 放入哪个存储桶。

维护一个双向链表,LinkedHashMap因此它可以记录条目插入(或访问,取决于您如何实例化映射)的顺序。Map 上的 API 没有方法可以在链表的某个索引处插入 key,val 对,因为这不是它的用途。

于 2011-10-06T20:07:23.987 回答
1
...

LinkedHashMap<String, StringExtension> map = new LinkedHashMap<>();

map.put("4", new StringExtension("4", "a"));
map.put("1", new StringExtension("1", "b"));
map.put("3", new StringExtension("3", "c"));
map.put("2", new StringExtension("2", "d"));

for (Map.Entry<String, StringExtension> entry : map.entrySet()) {
    Log.e("Test", "" + entry.getKey() + " - "+ entry.getValue().value);
}


Collection<StringExtension> temp = new ArrayList<>(map.values());
StringExtension value = map.remove("3");
map.clear();
map.put(value.key, value);

for (StringExtension val : temp) {
    map.put(val.key, val);
}

Log.e("Test", "---");

for (Map.Entry<String, StringExtension> entry : map.entrySet()) {
    Log.e("Test", "" + entry.getKey() + " - "+ entry.getValue().value);
}

...

private class StringExtension
{
    String key;
    String value;

    public StringExtension(String key, String value) {
        this.key = key;
        this.value = value;
    }
}
于 2018-05-01T21:06:08.663 回答
1

正如其他答案中所建议的那样,当然不建议您插入一个值,因为您将违背系统。

为什么?
因为 LinkedHashMap 由 HashMap 和 LinkedList 支持。
最后一个用于保留插入顺序。
显然,LinkedList 在插入头部和故事(双重链接)方面非常好。它为此操作提供 O(1) 时间复杂度。
但是,插入到某个位置非常慢,因为为了找到这个位置,您必须遍历整个列表,在最坏的情况下,这将花费 O(n) ,这对于大型数据集来说非常昂贵。

我个人不推荐它,因为它效率很低,但是如果您了解其缺点并且这是您想要的,
您可以使用此扩展功能将密钥对插入到某个位置

fun <K, V> LinkedHashMap<K, V>.insertAtIndex(key: K, value: V, index: Int) {
    if (index < 0) {
        throw IllegalArgumentException("Cannot insert into negative index")
    }
    if (index > size) {
        put(key, value)
        return
    }
    val holderMap = LinkedHashMap<K, V>(size + 1)
    entries.forEachIndexed { i, entry->
        if (i == index) {
            holderMap.put(key, value)
        }
        holderMap.put(entry.key, entry.value)
    }
    clear()
    putAll(holderMap)
}

这将花费 O(n) 时间和 O(n) 空间。

于 2020-11-21T19:35:49.457 回答
0

假设应用到的数据Map来自一个List(集合),那么您应该创建一个反向循环,从该列表的末尾开始读取,直到它到达第一个,如下所示: for (int key = collection.size()-1; key > map.size(); key--) { Data data = collection.get(key); map.put(key, data); }

于 2021-03-26T14:39:48.307 回答