How to get the next element to the current in a LinkedHashMap? - java

Let's say I have the LinkedHashMap with some unknown data inside.
//==================
Map< Integer, String > map = new LinkedHashMap<>();
map.put(10, "C");
map.put(20, "C++");
map.put(50, "JAVA");
map.put(40, "PHP");
map.put(30, "Kotlin");
//=============
And I know just the key = 50;
I am wondering what is the best way to get the next element to the element that I have by this key (50)? This is not a multi-threaded application. I don't worry about thread-safety.
I don't like the way to iterate all keys through entrySet from the beginning.
It would be great to somehow get access to the next() of LinkedHashMaps Entry.

This is LinkedHashMap so it remembers the order of elements insertion.
public static Map.Entry<Integer, String> getNextEntry(LinkedHashMap<Integer, String> map, Integer key) {
List<Integer> keys = new ArrayList<>(map.keySet());
int index = keys.indexOf(key);
if (index < 0 || index >= keys.size() - 1)
return null;
int k = keys.get(index + 1);
return Map.entry(k, map.get(k));
}
Or you can use Iterator:
public static Map.Entry<Integer, String> getNextEntry(LinkedHashMap<Integer, String> map, Integer key) {
boolean found = false;
for (Map.Entry<Integer, String> entry : map.entrySet()) {
if (found)
return Map.entry(entry.getKey(), entry.getValue());
if (entry.getKey().intValue() == key)
found = true;
}
return null;
}

LinkedHashMap doesn't offer a functionality which would allow finding the next key or entry.
In case if you simply don't want to bother with managing iteration yourself manually, then sure you can alternate this process, but keep in mind that the iteration should happen somewhere anyway.
Stream API
Alternatively you can make use of the Stream API if you don't want to bother with loops.
public static Optional<Map.Entry<Integer, String>> getNextEntry(Map<Integer, String> map,
int previous) {
return map.entrySet().stream()
.dropWhile(entry -> entry.getKey() != previous) // discard the entries, until the target key has been encountered
.skip(1) // skip the entry with the target key
.findFirst(); // grab the next entry and return it as an Optional (because the next entry might not exist)
}
TreeMap
However, you would be able to navigate through the keys of the map if you were using a TreeMap.
TreeMap maintains a red-black tree under the hood, and it keep the entries in sorted order based on keys. And it offers various method like higherEntry(), higherKey().
NavigableMap<Integer, String> map = new TreeMap<>();
// populating the map
int key = 50;
Map.Entry<Integer, String> next = map.higherEntry(key);

Related

How to delete entry in Java TreeMap?

I am making a method, which takes a provided TreeMap, removes entries where the key is a multiple of keyFilter and the value contains the valueFilter character, and then returns the resulting TreeMap.
This is what I have so far:
public static TreeMap<Integer, String> filterTreeMap(
TreeMap<Integer, String> map, int keyFilter, char valueFilter) {
for (Map.Entry<Integer, String> entry : map.entrySet()) {
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey%keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
map.remove(mapKey);
}
}
return map;
}
However, under the if condition where I want to delete the entries, I don't know how to delete entries in tree map. As far as I know, there is no existing method that I can use?
Use an Iterator. As the Iterator.remove() Javadoc notes
The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method.
Something like
public static TreeMap<Integer, String> filterTreeMap(TreeMap<Integer, String> map,
int keyFilter, char valueFilter) {
Iterator<Map.Entry<Integer, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Integer, String> entry = iter.next();
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey % keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
iter.remove();
}
}
return map;
}
It is possible to apply method removeIf to the entry set.
default boolean removeIf(Predicate<? super E> filter)
Removes all of the elements of this collection that satisfy the given predicate. Errors or runtime exceptions thrown during iteration or by the predicate are relayed to the caller.
Implementation Requirements:
The default implementation traverses all elements of the collection using its iterator(). Each matching element is removed using Iterator.remove(). If the collection's iterator does not support removal then an UnsupportedOperationException will be thrown on the first matching element.
Then the method filterTreeMap may have void return type because the input map is modified and this change will be "visible" outside this method.
public static void filterTreeMap(
TreeMap<Integer, String> map, int keyFilter, char valueFilter) {
map.entrySet().removeIf(e ->
e.getKey() % keyFilter == 0
&& e.getValue().indexOf(valueFilter) != -1
);
}
Keys on the map are unique. So, find that keys, and then remove them form the map.
public static TreeMap<Integer, String> filterTreeMap(TreeMap<Integer, String> map,
int keyFilter, char valueFilter) {
Set<Integer> keysToRemove = map.entrySet().stream()
.filter(kv -> kv.getKey() % keyFilter == 0 && kv.getValue().indexOf(valueFilter) != -1) // can be Predicate parameter
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
keysToRemove.forEach(map::remove);
return map; // keep in mind, map is modified here. You might want to return a new map instead
}
Iterate over a copy and you can add/remove just fine:
for (Map.Entry<Integer, String> entry : new LinkedHashMap<Integer,String> (map).entrySet()) {
int mapKey = entry.getKey();
String mapValue = entry.getValue();
if (mapKey%keyFilter == 0 && mapValue.indexOf(valueFilter) != -1) {
map.remove(mapKey);
}
}
It's not even any more lines of code, because the copy is made in-line via the copy constructor. LinkedHashMap was chosen to preserve iteration order (if that matters).

How to search value with partial key in TreeMap

I need to search a value in TreeMap with partial key
I tried with HashMap but its failing
TreeMap<String, BigDecimal> mapYr1 = new TreeMap<String, BigDecimal>();
mapYr1.put("abcdef",100.00);
Is it possible to retrieve value of 100.00 with something like below?
mapYr1.get("abcd");
Here I only know "abcd". I'm not sure "ef" part of key.
Not directly, but you can go over the entries and search for it:
public static BigDecimal findPartialKey(Map<String, BigDecimal> map, String search) {
return map.entrySet()
.stream()
.filter(e -> e.getKey().startsWith(search))
.map(Map.Entry::getValue)
.findFirst()
.orElse(null);
}
Note the using a stream like this, while (questionably) elegant, doesn't take advantage of the fact that the keys in a TreeMap are sorted, and may waste time looking for a matching key in a region that can't contain it. Using a good old-fashioned loop may be a bit clunkier, but in the general case, should perform a bit better:
public static BigDecimalfindPartialKey(SortedMap<String, BigDecimal> map, String search) {
Iterator<Map.Entry<String, Double>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Double> entry = iter.next();
String key = entry.getKey();
if (key.startsWith(search)) {
return entry.getValue();
}
if (key.compareTo(search) > 0) {
return null;
}
}
return null;
}

How to get the previous key/value and the next key/value in Maps

for (Entry<Double, String> entry : map.entrySet()) {
Double key = entry.getKey();
String value = entry.getValue();
// double nextKey = ?
// String nextvalue = ?
// double prevKey = ?
// String prevValue = ?
}
is it possible to know what the previous element and the next element while iterating the map?
You can use NavigableMap for this, which entrySet()'s iterator return entries in ascending key order:
NavigableMap<Double, String> myMap = new TreeMap<>();
//...
for (Map.Entry<Double, String> e : myMap.entrySet()) {
Map.Entry<Double, String> next = myMap.higherEntry(e.getKey()); // next
Map.Entry<Double, String> prev = myMap.lowerEntry(e.getKey()); // previous
// do work with next and prev
}
Every entry retrieval is O(logN), so for full iteration this is not the most effective approach. To be more effective, on iteration just remember last 3 entries, and use 1st as prev, 2nd as current and 3rd as next, as #Malt suggests.
A TreeMap is an OrderedMap and a NavigableMap and will allow you to iterate forward and backward, allowing you to access previous and next keys with lowerKey() and higherKey() respectively. However it might not be the best solution.
Can you describe the actual problem you're trying to solve, and we can give you a more fitting solution?

Efficient way to find min value in Map

I am trying to find key with minimum value in Map shown below.
Map<Node, Integer> freeMap = new TreeMap<>();
Node minNode = null;
for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
if (minNode == null) {
minNode = entry.getKey();
} else {
if (entry.getValue() < freeMap.get(minNode)) {
minNode = entry.getKey();
}
}
}
Firstly, Is there a straight forward way to find key with minimum value than using foreach loop. Secondly, can you suggest some alternate data structure approach which can be used to store a Node object and an associated Integer value, so I can fetch entry with minimum value in constant time O(1).
If your goal is to improve time complexity, there's really only one possible change, from O(n log n) to O(n):
Map<Node, Integer> freeMap = new TreeMap<>();
Map.Entry<Node, Integer> minEntry = null;
for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
if (minEntry == null || entry.getValue() < minEntry.getValue()) {
minEntry = entry;
}
}
Node minNode = minEntry.getKey();
The keys for a concise, efficient and elegant solution here are the Collections#min method and the Map.Entry#comparingByValue method
The first method can be applied to the entrySet of the map, and the second one provides a Comparator that compares map Entry objects by their value. So the solution is a one-liner, and you can either obtain the entry or the key directly, as shown in this example:
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
public class KeyWithMinValue
{
public static void main(String[] args)
{
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("Zero", 0);
map.put("One", 1);
map.put("Two", 2);
map.put("Three", 3);
map.put("Four", 4);
// Obtain the entry with the minimum value:
Entry<String, Integer> entryWithMinValue = Collections.min(
map.entrySet(), Entry.comparingByValue());
System.out.println(entryWithMinValue);
// Or directly obtain the key, if you only need that:
String keyWithMinValue = Collections.min(
map.entrySet(), Entry.comparingByValue()).getKey();
System.out.println(keyWithMinValue);
}
}
I suspect that Integer values are not unique in your system.
If this is the case, I suggest you use TreeMultimap from guava library, and use Integer value as a key.
TreeMultimap<Integer, Node> freeMap = new TreeMultimap<>();
Node minNode =
freeMap.isEmpty()
? null
: freeMap.entries().iterator().next().getValue();
Minor improvement not a whole bunch :
Map<Node, Integer> freeMap = new TreeMap<Node, Integer>();
Node minNode = freeMap.isEmpty() ? null : (Node) freeMap.entrySet().iterator().next();
for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
if (entry.getValue() < freeMap.get(minNode)) {
minNode = entry.getKey();
}
}
Got the if check out of the loop.
Data Structure for O(1) Min
For an alternative data structure, how about a Priority Queue.
You can either use a custom Comparator or have your data type implement Comparable.
From the javadoc:
Implementation note: this implementation provides O(log(n)) time for the enqueing and dequeing methods (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; and constant time for the retrieval methods (peek, element, and size).
Data Structure for O(1) Min and amortized O(1) find
If you want both efficient min and efficient find and you control access to the data structure (otherwise what is the point of the question?) you can just roll out your own by extending Java's HashMap to keep track of the minimum element.
You will have to override the put, putAll and remove methods. In each case, you can just call the super class method (e.g. super.put(key, value)) and then update the minimum element, which is kept as an instance member of your newly defined class.
Note that this increases the remove time to (O(N)) (since you will have to update the minimum value).
You can define your own Comparator and use Collections.min.
Example:
Comparator<Entry<Node, Integer>> customComparator = new Comparator<>() {
#Override
public int compare(Entry<Node, Integer> o1, Entry<Node, Integer> o2){
return (int)(o1.getValue() - o2.getValue());
}
#Override
public boolean equals(Object obj) {
return false;
}
};
Entry<Node, Integer> minVal = Collections.min(freeMap.entrySet(), customComparator);
Hope this helps :)

Iterating over and deleting from Hashtable in Java

I have a Hashtable in Java and want to iterate over all the values in the table and delete a particular key-value pair while iterating.
How may this be done?
You need to use an explicit java.util.Iterator to iterate over the Map's entry set rather than being able to use the enhanced For-loop syntax available in Java 6. The following example iterates over a Map of Integer, String pairs, removing any entry whose Integer key is null or equals 0.
Map<Integer, String> map = ...
Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, String> entry = it.next();
// Remove entry if key is null or equals 0.
if (entry.getKey() == null || entry.getKey() == 0) {
it.remove();
}
}
You can use Enumeration:
Hashtable<Integer, String> table = ...
Enumeration<Integer> enumKey = table.keys();
while(enumKey.hasMoreElements()) {
Integer key = enumKey.nextElement();
String val = table.get(key);
if(key==0 && val.equals("0"))
table.remove(key);
}
You can use a temporary deletion list:
List<String> keyList = new ArrayList<String>;
for(Map.Entry<String,String> entry : hashTable){
if(entry.getValue().equals("delete")) // replace with your own check
keyList.add(entry.getKey());
}
for(String key : keyList){
hashTable.remove(key);
}
You can find more information about Hashtable methods in the Java API
So you know the key, value pair that you want to delete in advance? It's just much clearer to do this, then:
table.delete(key);
for (K key: table.keySet()) {
// do whatever you need to do with the rest of the keys
}

Categories