Get the previous element of a LinkedHashMap - java

I need to get the previous element of a LinkedHashMap.
I tried using the ListIterator because it has the previous() method. But the problem is ListIterator needs a List not a set.
ListIterator<Entry<String, Integer>> it = (ListIterator<Entry<String, Integer>>) unitsItems.entrySet().iterator();
I have to transform my entrySet into a list. So I tried this :
List entryList= new ArrayList (unitsItems.entrySet());
ListIterator<Entry<String, Integer>> it = (ListIterator<Entry<String, Integer>>) entryList.iterator();
I got this error:
java.util.ArrayList$Itr cannot be cast to java.util.ListIterator
Can anyone tell me the correct way to transform the set to a list and then use it in ListIterator?
Thank you.

As per request: Since you already have the list entryList you just need to call listIterator() on it to get what you want.
Btw, I'd add the generic type to the list as well: List<Entry<String, Integer>> = new ArrayList<>(unitsItems.entrySet());

If you're using Java 8, you can do this:
#Test
public void obtainListIterator() {
LinkedHashMap<String, String> test = new LinkedHashMap<>();
test.put("1", "a");
test.put("2", "a");
test.put("3", "a");
ListIterator<Map.Entry<String, String>> listIterator =
test
.entrySet()
.stream()
.collect(Collectors.toList())
.listIterator();
assertThat(listIterator.next().getKey()).isEqualTo("1");
assertThat(listIterator.previous().getKey()).isEqualTo("1");
}
However, the answer given by #Thomas is better than mine:
ListIterator<Map.Entry<String, String>> listIterator = new LinkedList(test.entrySet()).listIterator();

Using entrySet() is wrong since it's a method of HashMap and thus it does not contain any notion of list (thus your problems).
As you can see from the sources the linked list is implemented directly at the LinkedHashMap level.
That means that to do what you need (since the API do not supply a way) you'll need to subclass the LinkedHashMap and do it yourself.

Related

How to sort map of JSONObject

I have a map of JSONObject and Integer. I want to sort it in an incremental order using its values, here is what I've done so far:
Map<JSONObject, Integer> unsortedMap = new HashMap<>();
unsortedMap.put(jsonObject2.getJSONObject(key),key1);
Are you sure you want to use an Object as a key in a hashmap? It's normal practice to use immutable values for keys. The slightest change in any of the elements of the JSONObject will change the hash value and you will be unable to get your info back out of the map.
What is the integer value of your map?
Only a suggestion but you may want to check out the Java API for collection and see if there is anything that better suits your needs. Java Collection API
In the past I have wanted a sorted list with no duplicates. I had to first write to a TreeMap and then write to a SortedSet to get the result I wanted. Not code efficient, but I couldn't achieve what I wanted any other way.
Not sure if this is the best option but could help:
public static Map<String, Integer> SortByKey(Map<String, Integer> map)
{
ArrayList<String> sortedKeys =
new ArrayList<String>(map.keySet());
Collections.sort(sortedKeys);
Map<String, Integer> sortedMap = new HashMap<>();
for (String x : sortedKeys)
sortedMap.put(map.get(x), Integer.parseInt(x));
return sortedMap;
}

Why does flatmapping and collecting a list of treesets return an object?

I am trying to put all the values treeset elements of a list with treesets into a linkedHashSet. This list of treesets is returned by the values() method of a TreeMap<String, TreeSet>. The code looks like this:
Map<String, TreeSet> sortedByMonthAndVarietyNameMap = new HashMap<>();
sortedByMonthAndVarietyNameMap.values().stream().flatMap(monthList -> monthList.stream()).collect(Collectors.toCollection(LinkedHashSet::new));
This should be returning a LinkedHashSet with all the elements of the flatmapped treesets. But in reality it returns an object of type Object.
Why is this happening? Can someone explain what i'm doing wrong?
The TreeSet is not fully typed:
Map<String, TreeSet<Integer>> sortedByMonthAndVarietyNameMap = new HashMap<>();
LinkedHashSet<Integer> result = sortedByMonthAndVarietyNameMap.values().stream()
.flatMap(monthList -> monthList.stream())
.collect(Collectors.toCollection(LinkedHashSet::new));
Is this what you wish?
LinkedHashSet<TreeSet> collect = sortedByMonthAndVarietyNameMap
.values()
.stream()
.flatMap(Stream::of)
.collect(Collectors.toCollection(LinkedHashSet::new));
P.S. This data conversion looks strange. Perhaps you could describe your case in more details?
As you have provided the constructor reference of LinkedHashSet without specifying the type.
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.13

List of Map entries from map

I'm trying to get a (sorted by value) list of map entries from a Map. I tried this:
List<Pair<String, AtomicInteger>> expectedList = expectedMap.entrySet().stream()
.sorted(Comparator.comparing(e -> -e.getValue().get()))
.map(e -> new Pair<>(e.getKey(), e.getValue())).collect(Collectors.toList());
but if I try to substitute Pair with Map.Entry, it tells me that Map.Entry is abstract and cannot be instantiated. Is there a way to adapt this construct to get a list of entries instead of a list of pairs?
See the Javadoc for Map.Entry:
Interface Map.Entry
All Known Implementing Classes:
AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry
Pick one implementing class that suits your needs, for example
List<Entry<String, AtomicInteger>> expectedList = expectedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> -e.getValue().get()))
.map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), e.getValue()))
.collect(Collectors.toList());
But you could also just remove the map and use the entries that entrySet() returns:
List<Entry<String, AtomicInteger>> expectedList = expectedMap.entrySet().stream()
.sorted(Comparator.comparingInt(e -> -e.getValue().get()))
.collect(Collectors.toList());
PS.: If you are comparing primitive data types, you should use the right method like comparingInt. This avoids unnecessary boxing.
I don't see why you need to map Map.Entry to something else if you need Map.Entry.
Here's my take. I've opted to first create a list of entries and then sort it. Also used mor of the built-in API to construct the comparator.
List<Map.Entry<String, AtomicInteger>> expectedList =
new ArrayList<>(expectedMap.entrySet());
Collections.sort(expectedList,
Map.Entry.comparingByValue(
Comparator.comparingInt(AtomicInteger::get).reversed());

Iterating through a LinkedHashMap in reverse order

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.
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);
}
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();
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)));
new LinkedList(linkedHashMap.keySet()).descendingIterator();
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;
}
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.

How can I sort the keys of a Map in Java?

This is a very basic question, I'm just not that good with Java. I have a Map and I want to get a list or something of the keys in sorted order so I can iterate over them.
Use a TreeMap, which is an implementation of the SortedMap interface. It presents its keys in sorted order.
Map<String, Object> map = new TreeMap<String, Object>();
/* Add entries to the map in any order. */
...
/* Now, iterate over the map's contents, sorted by key. */
for (Map.Entry<String, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
If you are working with another Map implementation that isn't sorted as you like, you can pass it to the constructor of TreeMap to create a new map with sorted keys.
void process(Map<String, Object> original) {
Map<String, Object> copy = new TreeMap<String, Object>(original);
/* Now use "copy", which will have keys in sorted order. */
...
}
A TreeMap works with any type of key that implements the Comparable interface, putting them in their "natural" order. For keys that aren't Comparable, or whose natural ordering isn't what you need, you can implement your own Comparator and specify that in the constructor.
You have several options. Listed in order of preference:
Use a SortedMap:
SortedMap<whatever> myNewMap = new TreeMap<whatever>(myOldMap);
This is vastly preferable if you want to iterate more than once. It keeps the keys sorted so you don't have to sort them before iterating.
There is no #2.
There is no #3, either.
SortedSet<whatever> keys = new TreeSet<whatever>(myMap.keySet());
List<whatever> keys = new ArrayList<whatever>(myMap.keySet());
Collections.sort(keys);
The last two will get you what you want, but should only be used if you only want to iterate once and then forget the whole thing.
You can create a sorted collection when iterating but it make more sense to have a sorted map in the first place. (As has already been suggested)
All the same, here is how you do it.
Map<String, Object> map;
for(String key: new TreeSet<String>(map.keySet()) {
// accessed in sorted order.
}
Apart from the methods mentioned in other answers, with Java 8 streams, another shorthand to get a sorted key list from a map would be -
List<T> sortedKeys = myMap.keySet().stream().sorted().collect(Collectors.toList());
One could actually get stuff done after .sorted() as well (like using a .map(...) or a .forEach(...)), instead of collecting it in the list and then iterating over the list.

Categories