44

I have a LinkedHashMap:

LinkedHashMap<String, RecordItemElement>

that I need to iterate through from a given key's position, backwards. So if I was given the 10th item's key, I'd need iterate backwards through the hashmap 9, 8, 7 etc.

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
Dominic Bou-Samra
  • 14,799
  • 26
  • 100
  • 156

7 Answers7

16

The question requires a LinkedHashMap in reverse order, some answers suggesting using a TreeSet but this will reorder the map based upon the key.

This solution allows the iteration over the original LinkedHashMap not the new ArrayList as has also been proposed:

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

The HashMap:

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

Reverse iterating over values:

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

Reverse iterating over keys:

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

Reverse iterating over both:

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();
Ali
  • 21,572
  • 15
  • 83
  • 95
9

You don't have to iterate through it. But it would be handy to pull the keys off and store it in a list. Thats the only way you can do indexOf() type operations.

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)));
Kal
  • 24,724
  • 7
  • 65
  • 65
  • 5
    Instead of keyList.indexOf(...) use listIterator with the position. and iterate with previous() – Shimi Bandiel Aug 24 '11 at 05:56
  • Instead of keyList.indexOf(...) use keyList.size()-1 – Óscar López Jan 17 '12 at 11:09
  • 7
    @Kal The operation of your solution is O(n), which is well.. *horrible*. – Pacerier Feb 23 '12 at 18:08
  • Well doing so you create unnecessary overhead by copying the key set. Consider using TreeMap. – Anton May 07 '12 at 15:55
  • Doesn't a `Set` destroy the order? Consequently, wouldn't `keyList` contain the keys in random order? – trss Oct 09 '14 at 19:09
  • I suppose it works because the documentation says the returned `Set` is backed by the `LinkedHashMap`. Seems flaky though, since there is generally no guarantee on the order of iteration of a `Set`. – trss Oct 09 '14 at 19:43
  • 3
    The `LinkedHashMap#keySet()` returns a `Set` backed by a `LinkedKeySet`, which is a nested class with package-local visibility. The iteration order is defined by its `Spliterator`, which is ordered in the same order as the map entries. So, in this case, the order IS guaranteed. – Chthonic Project Jun 27 '15 at 20:34
4
new LinkedList(linkedHashMap.keySet()).descendingIterator();
marcolopes
  • 9,232
  • 14
  • 54
  • 65
0

Using "user22745008" solution and labdas with some generics you can have a very neat solution as a method:

  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;
    }
Hernanibus
  • 98
  • 2
  • 4
0

This is an old question, but I think it's lacking an answer that takes a newer approach. The following uses Java 9 features:

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

The code above streams the map's entryset, keeping entries until a key equal to the given key is found. Then, the entries are collected to an ArrayDeque.

One detail is missing, though. Depending on whether you need the entry that matches the given key to also be included in the result or not, you might need to manually add it to the deque. If you don't want it added, then you're done. Otherwise, simply do:

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

Now, to iterate the Deque in reverse order, simply use its descendingIterator():

Iterator<Map.Entry<String, RecordItemElement>> descIt = top.descendingIterator();

It's worth mentioning that this approach only works if the stream is sequential. Anyways, we wouldn't have gained anything using a parallel stream here.

fps
  • 33,623
  • 8
  • 55
  • 110
0

JEP 431: Sequenced Collections in the upcoming version 21 of Java adds a reversed() method to LinkedHashMap. This reversed view of the map can be iterated over in the standard manner.

myLinkedHashMap.reversed().forEach(key, value -> {
  // Do something with the key and/or value
});
M. Justin
  • 14,487
  • 7
  • 91
  • 130