在反向顺序中迭代LinkedHashMap

44

我有一个LinkedHashMap:

LinkedHashMap<String, RecordItemElement>

我需要从给定的键所在位置开始向后迭代。例如,如果给出第10个项的键,则需要向后遍历哈希映射的9、8、7等。


你知道所有项目的键吗? - Android Killer
我建议我遍历一次,将其添加到列表中,然后反向迭代使用它们作为键?假设那样会起作用。 - Dominic Bou-Samra
1
我假设你只是想在这里按“插入顺序”反向迭代,或者你想要基于某种自然排序的真正排序?另请参阅https://dev59.com/cHI-5IYBdhLWcg3wR2Jr#1936472。 - rogerdpack
7个回答

16

问题要求按照相反的顺序使用LinkedHashMap,一些答案建议使用TreeSet,但这将基于键重新排序映射。

该解决方案允许迭代原始的LinkedHashMap而不是新的ArrayList(也被提出为一种解决方案):

List<String> reverseOrderedKeys = new ArrayList<String>(linkedHashMap.keySet());
Collections.reverse(reverseOrderedKeys);
for (String key : reverseOrderedKeys) {
    RecordItemElement line = linkedHashMap.get(key);
}

11

HashMap:

HashMap<Integer, String> map = new HashMap<Integer, String>();

反向迭代值:

ListIterator<Sprite> iterator = new ArrayList<String>(map.values()).listIterator(map.size());
while (iterator.hasPrevious()) String value = iterator.previous();

反向遍历键:

ListIterator<Integer> iterator = new ArrayList(map.keySet()).listIterator(map.size());
while (iterator.hasPrevious()) Integer key = iterator.previous();

同时反向迭代:

ListIterator<Map.Entry<Integer, String>> iterator = new ArrayList<Map.Entry<Integer, String>>(map.entrySet()).listIterator(map.size());
while (iterator.hasPrevious()) Map.Entry<Integer, String> entry = iterator.previous();

9

您不必遍历它。但是,从中提取键并将其存储在列表中会很方便。这是执行indexOf()类型操作的唯一方式。

List<String> keyList = new ArrayList<String>(map.keySet());
// Given 10th element's key
String key = "aKey";
int idx = keyList.indexOf(key);
for ( int i = idx ; i >= 0 ; i-- ) 
 System.out.println(map.get(keyList.get(i)));

5
使用listIterator与位置一起遍历,而不是使用keyList.indexOf(...)。并使用previous()进行迭代。 - Shimi Bandiel
7
你的解决方案的操作复杂度是O(n),这其实相当“糟糕”。 - Pacerier
一个 Set 不会破坏顺序吗?因此,keyList 中的键不会以随机顺序出现吗? - trss
我猜它能工作是因为文档说返回的 Set 是由 LinkedHashMap 支持的。不过似乎有点不可靠,因为通常情况下无法保证 Set 的迭代顺序。 - trss
3
LinkedHashMap#keySet() 返回一个由 LinkedKeySet 支持的 SetLinkedKeySet 是一个具有包局部可见性的嵌套类。它的迭代顺序由其 Spliterator 定义,该顺序与映射条目相同。因此,在这种情况下,顺序是有保证的。 - Chthonic Project
显示剩余2条评论

4
new LinkedList(linkedHashMap.keySet()).descendingIterator();

0

这是一个老问题,但我认为它缺少了一种采用较新方法的答案。以下使用Java 9功能:

Deque<Map.Entry<String, RecordItemElement>> top = map.entrySet().stream()
        .takeWhile(e -> !givenKey.equals(e.getKey()))
        .collect(Collectors.toCollection(ArrayDeque::new));

上面的代码流式传输了地图的entryset,保留条目直到找到一个等于给定键的键。然后,这些条目被收集到一个ArrayDeque中。

不过还有一个细节需要注意。根据您是否需要将与给定键匹配的条目包含在结果中,您可能需要手动将其添加到deque中。如果您不想添加它,则完成了。否则,只需执行以下操作:

top.add(Map.entry(givenKey, map.get(givenKey)));

现在,要迭代反向的Deque,只需使用其descendingIterator()即可:
Iterator<Map.Entry<String, RecordItemElement>> descIt = top.descendingIterator();

值得一提的是,这种方法只适用于流是顺序的情况。无论如何,在这里使用并行流也不会有任何收益。

0

在即将发布的Java 21版本中,JEP 431: Sequenced CollectionsLinkedHashMap添加了一个reversed()方法。这个地图的反转视图可以按照标准的方式进行迭代。

myLinkedHashMap.reversed().forEach(key, value -> {
  // Do something with the key and/or value
});

0

使用"user22745008"的解决方案和带有一些泛型的lambda表达式,您可以将其作为方法来实现非常简洁的解决方案:

  public static <T, Q> LinkedHashMap<T, Q> reverseMap(LinkedHashMap<T, Q> toReverse)
  {
      LinkedHashMap<T, Q> reversedMap = new LinkedHashMap<>();
      List<T> reverseOrderedKeys = new ArrayList<>(toReverse.keySet());
      Collections.reverse(reverseOrderedKeys);
      reverseOrderedKeys.forEach((key)->reversedMap.put(key,toReverse.get(key)));
      return reversedMap;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接