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.
Related
I have a LinkedHashMap<String,String> which looks something like this (don't really know how to illustrate a HashMap):
{
"10/10/2010 10:10:10" => "SomeText1",
"10/10/2019 10:10:19" => "SomeText2",
"10/10/2020 10:10:20" => "SomeText3",
"10/10/2021 10:10:21" => "SomeText4"
}
And I want to put it like this:
{
"10/10/2021 10:10:21" => "SomeText4",
"10/10/2020 10:10:20" => "SomeText3",
"10/10/2019 10:10:19" => "SomeText2",
"10/10/2010 10:10:10" => "SomeText1"
}
I have written this solution which works because the result I want is an ArrayList, but i was thinking if there was an easier way to reverse the LinkedHashMap maintaining the same type using a tool like sort for example.
private LinkedHashMap<String, String> map = new LinkedHashMap<>();
int sizeOfHashMap = map.size();
ArrayList reversedHashToArrayList = new ArrayList(map.size());
for (Map.Entry<String,String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
reversedHashToArrayList.add(0,entry);
}
A LinkedHashMap orders by insertion; it would be more logical to sort on the associated date time:
private SortedMap<LocalDateTime, String> map = new TreeMap<>(Comparator.naturalOrder()
.reversed());
LocalDateTimeFormatter formatter = LocalDateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss");
map.put(LocalDateTime.parse("10/10/2010 10:10:10", formatter), "...");
To specify that the map is sorted, there is the interface SortedMap. Better use an interface, which is more versatile. The implementation class for a sorted map is the TreeMap. However you want a reversed comparison.
You could use a Local specific pattern. Mind that above I chose Month/Day and not the British Day/Month.
If your motive is just to reverse the map ( show in descending order ) you can use
Java.util.TreeMap.descendingMap() : It returns a reverse order view of the mappings contained in the map`
LinkedHashMap<String,String> map = .... //this is your intial hashmap
TreeMap<String,String> tmap = new TreeMap<>(map);
map.clear();
map.putAll(tmap.descendingMap());
This will do the trick.
Here Is My Own Written Logic for you. without using any built in functions to reverse:
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("10/10/2010 10:10:10", "SomeText1");
map.put("10/10/2019 10:10:19", "SomeText2");
map.put("10/10/2020 10:10:20", "SomeText3");
map.put("10/10/2021 10:10:21", "SomeText4");
LinkedHashMap<String, String> reversed = new LinkedHashMap<>();
String[] keys = map.keySet().toArray(new String[map.size()]);
for (int i = keys.length - 1; i >= 0; i--) {
reversed.put(keys[i], map.get(keys[i]));
}
If you want to keep using a LinkedHashMap reversing it while keeping it somewhat efficient is not as easy. This is a solution that reverses a given LinkedHashMap using the iterator order (which is predictable for a LinkedHashMap and therefore probably what you are looking for).
Note that other solutions like using a SortedMap or a TreeMap are probably still better. Yet, for the sake of sticking to your original question, here is a solution:
public static <K, V> LinkedHashMap<K, V> reverse(LinkedHashMap<K, V> map)
{
LinkedHashMap<K, V> reversedMap = new LinkedHashMap<K, V>();
ListIterator<Entry<K, V>> it = new ArrayList<>(map.entrySet()).listIterator(map.entrySet().size());
while (it.hasPrevious())
{
Entry<K, V> el = it.previous();
reversedMap.put(el.getKey(), el.getValue());
}
return reversedMap;
}
Note that you won't get around wrapping the entry set into an ArrayList sadly as only that provides you with an ListIterator which can be initialized to any point other than the first element. Having something like a reverseIterator() method would simplify life a lot - sadly there is none available.
Complexity wise you iterate the list twice with this approach, first for the listIterator call from start to the last element and then once more from the back to the front when using previous. So you are looking at O(2n) here.
So I am very new to Java and as such I'm fighting my way through an exercise, converting one of my Python programs to Java.
I have run into an issue where I am trying to replicate the behavior, from python the following will return only the keys sorted (by values), not the values:
popular_numbers = sorted(number_dict, key = number_dict.get, reverse = True)
In Java, I have done a bit of research and have not yet found an easy enough sample for a n00b such as myself or a comparable method. I have found examples using Guava for sorting, but the sort appears to return a HashMap sorted by key.
In addition to the above, one of the other nice things about Python, that I have not found in Java is the ability to, easily, return a subset of the sorted values. In Python I can simply do the following:
print "Top 10 Numbers: %s" % popular_numbers[:10]
In this example, number_dict is a dictionary of key,value pairs where key represents numbers 1..100 and the value is the number of times the number (key) occurs:
for n in numbers:
if not n == '':
number_dict[n] += 1
The end result would be something like:
Top 10 Numbers: ['27', '11', '5', '8', '16', '25', '1', '24', '32',
'20']
To clarify, in Java I have successfully created a HashMap, I have successfully examined numbers and increased the values of the key,value pair. I am now stuck at the sort and return the top 10 numbers (keys) based on value.
Put the map's entrySet() into a List.
Sort this list using Collections.sort and a Comparator which sorts Entrys based on their values.
Use the subList(int, int) method of List to retrieve a new list containing the top 10 elements.
Yes, it will be much more verbose than Python :)
With Java 8+, to get the first 10 elements of a list of intergers:
list.stream().sorted().limit(10).collect(Collectors.toList());
To get the first 10 elements of a map's keys, that are integers:
map.keySet().stream().sorted().limit(10).collect(Collectors.toMap(Function.identity(), map::get));
HashMaps aren't ordered in Java, and so there isn't really a good way to order them short of a brute-force search through all the keys. Try using TreeMap: http://docs.oracle.com/javase/6/docs/api/java/util/TreeMap.html
Assuming your map is defined something like this and that you want to sort based on values:
HashMap<Integer, Integer> map= new HashMap<Integer, Integer>();
//add values
Collection<Integer> values= map.values();
ArrayList<Integer> list= new ArrayList<Integer>(values);
Collections.sort(list);
Now, print the first top 10 elements of the list.
for (int i=0; i<10; i++) {
System.out.println(list.get(i));
}
The values in the map are not actually sorted, because the HashMap is not sorted at all (it stores the values in the buckets based on the hashCode of the key). This code is just displaying 10 smallest elements in the map.
EDIT sort without loosing the key-value pairs:
//sorted tree map
TreeMap<Integer, Integer> tree= new TreeMap<>();
//iterate over a map
Iteartor<Integer> it= map.keySet().iterator();
while (it.hasNext()) {
Integer key= it.next();
tree.put(map.get(key), key);
}
Now you have the TreeMap tree that is sorted and has reversed key-value pairs from the original map, so you don't lose the information.
Try the next:
public static void main(String[] args) {
// Map for store the numbers
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
// Populate the map ...
// Sort by the more popular number
Set<Entry<Integer, Integer>> set = map.entrySet();
List<Entry<Integer, Integer>> list = new ArrayList<>(set);
Collections.sort(list, new Comparator<Entry<Integer, Integer>>() {
#Override
public int compare(Entry<Integer, Integer> a,
Entry<Integer, Integer> b) {
return b.getValue() - a.getValue();
}
});
// Output the top 10 numbers
for (int i = 0; i < 10 && i < list.size(); i++) {
System.out.println(list.get(i));
}
}
Guava Multiset is a great fit for your use case, and would nicely replace your HashMap. It is a collection which counts the number of occurences of each element.
Multisets has a method copyHighestCountFirst, which returns an immutable Multiset ordered by count.
Now some code:
Multiset<Integer> counter = HashMultiset.create();
//add Integers
ImmutableMultiset<Integer> sortedCount = Multisets.copyHighestCountFirst(counter);
//iterate through sortedCount as needed
Use a SortedMap, call values(). The docs indicate the following:
The collection's iterator returns the values in ascending order of the corresponding keys
So as long as your comparator is written correctly you can just iterate over the first n keys
Build a list from the keyset.
Sort the HashMap by values using the keys to access the value in the Collection.sort() method.
Return a sub list of the sorted key set.
if you care about the values, you can use the keys in step 3 and build value set.
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
List list = new ArrayList(hashMap.keySet());
Collections.sort(list, (w1, w2) -> hashMap.get(w2) - hashMap.get(w1)); //sorted descending order by value;
return list.subList(0, 10);
To preserve the ranking order and efficiently return top count, much smaller than the size of the map size:
map.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(count)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new))
Is it tossibe to aceess a Map<Integer, Integer> via index?
I need to get the second element of the map.
You're using the wrong data structure. If you need to lookup by key, you use a Map. If you need to lookup by index or insertion order, use something that lets you index, like an array or list or linked list.
If you need to lookup by both, then you need to create a composite data structure that tracks both keys and insertion order (implementation would be backed by a Map and one of the above aforementioned data structures).
There's even one built into the framework: LinkedHashMap.
There is no direct way to access a map "via index", but it looks like you want a LinkedHashMap, which provides a predictable iteration order:
... which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
A definition of index is not applicable to Map, as it's not an ordered collection by default.
You can use a TreeMap, which implements NavigableMap, and then iterate the key set using the navigableKeySet() method.
If you just need to get the second element all the time. Why not use a iterator and then do next ,next.
It will depends of Map implementation, but if you want to retrieve the second inserted element, you can use a LinkedHashMap and then create an iterator on values.
Map<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();
map.put(1, 1);
map.put(2, 2);
Integer value = null;
if (map.size() > 1) {
Iterator<Integer> iterator = map.values().iterator();
for (int i = 0; i < 2; i++) {
value = iterator.next();
}
}
// value contains second element
System.out.println(value);
Map does not store elements in the insertion order. It stores elements into buckets based on the value of the hashCode of the element that is being stored. So no, you cannot get it by index.
Anyways, you could imitate something like this by using the LinkedHashMap implementation of the Map interface, which remembers the insertion order (unlinke the HashMap).
You would have to "hack" with manual index counter and the code would look something like this:
Map<String, String> map= new LinkedHashMap<>();
map.put("1", "one");
map.put("2", "two");
map.put("3", "three");
int index= 0;
for (String key : map.keySet()) {
if (index++ == 1) {
System.out.println(map.get(key));
}
}
Will print:
"two"
Which is what you want.
You can also use org.apache.commons.collections.map.ListOrderedMap from apache commons-collection. It implements Map and provides some methods from the List interface, like get(int index) and remove(int index).
It uses an ArrayList internally, so performance will be better than iterating on a Map to retrieve a value at specified position.
Not sure if this is any "cleaner", but:
If use LinkedHashMap and u want to retrieve element inserted second following will work
List keys = new ArrayList(map.keySet());
Object obj = map.get(keys.get(1));
//do you staff here
I've tried to search over the internet to find a solution of deleting another item but not the one you are visiting. Unfortunately, there is not a way to do it.
Here is the problem.
Assume I have a hashmap and
the items are <0,10> <1,20> <2,30>
Map<Integer,Integer> map = new HashMap<Integer, Integer>() ;
Iterator<Map.Entry<Integer, Integer> >entries = map.entrySet().iterator();
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
map.remove(2); //2 is the key of 3th item
}
}
Then the problem occours.
Really appreciate the suggestions.
Do it in two passes:
iterate through the entries, and collect the keys to delete in a Set<Integer>
iterate over the set, and remove all the keys it contains from the map. (Or call map.keySet().removeAll(keysToRemove))
Let me guess, you're getting a ConcurrentModificationException.
That's baked in. The javadocs say it may be thrown if you do what you're doing. You can either follow #JBNizet's or you can restart iterating each time you remove an element. Which you choose will depend upon your specific situation.
A 3rd option is to create a copy of the entry set and iterate over that. This one works best if restarting the iteration is expensive and you need to remove quickly.
Iterator<Map.Entry<Integer, Integer> >entries = new HashSet<Map.Entry<Integer, Integer>>(map.entrySet()).iterator();
Do it in 2 passes, 1st accumulate keys to remove, then perform actual removal:
List<Integer> keysToRemove = ...
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
keysToRemove.add(2);
}
}
for (Integer key : keysToRemove)
map.remove(key);
You cannot modify a HashMap while iterating through it. Instead, you could for example collect a list of keys to remove while iterating through the map, and then remove the items in the list from the map after you have completed the iterating.
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.