I honestly tried to look at a lot of posts but I am not sure what is the correct way of getting a specific key from a ConcurrentHashMap.
How to get the first key in a ConcurrentHashMap?
How to get the key on first encounter of a certain value?
How to update the value of a specific key?
Okay, lets tackle these questions one by one:
1) You cannot. A ConcurrentHashMap has no order. There is no "first" and no "last".
2) A ConcurrentHashMap provides an entrySet which is "weakly consistent", so that if the content of the Map changes during iteration you might see the changes, you might not:
public static <K, V> Optional<K> getKeyForVal(final V val, final Map<K, V> map) {
return map.entrySet().stream()
.filter(e -> e.getValue().equals(val))
.map(Map.Entry::getKey)
.findFirst();
}
Again, there is no "first" this is just the first one encountered during iteration.
3) This is easy, just add the same key -> value pair again, this will update the value for a particular key.
So the main takeaway here is that a ConcurrentHashMap has no order. The order of iteration over a ConcurrentHashMap is undefined and may even change between different iterations. There is no "first", no "last".
Something like ConcurrentSkipListMap does have an ordering, it's sorted by the order of the keys as defined by a Comparator. So that would have a "first" and a "last" element.
Related
I have a HashMap with Document as Key and a Double Value as Value.
My aim is to sort the Hashmap by descending value. The .reverse() should be after comparingbyValue() but conflicts with sorted. How do I solve this?
HashMap<Document, Double> sortedMap = Map.entrySet().stream().sorted(Entry.comparingByValue())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
HashMap is fundamentally unsorted. TreeMap is fundamentally sorted on key. In other words, you can't do this. Just now how it works. You have two options:
Figure out a way to sort on keys
And then just use a TreeMap with a custom comparator.
Copy the whole thing
Make a tuple class, then copy the entire thing into an ArrayList by turning each k/v pair into a tuple, then sort that list on value as you want, then move the entire thing into a LinkedHashMap which preserves order. You cannot now modify the thing without going through this entire routine again.
Rethink your architecture
If neither is acceptable you'll have to go back to the drawing board.
I know that HashMap is not sorted but is there any away i can create iterator which returns values in sorted order of key. I can use Sorted versions of the collections but I am looking for a way to do the same with Hash Based map.
Any such iterator would have to internally sort all the keys of the HashMap in order to be able to iterate over them in sorted order. It would be more efficient to use an already sorted Map implementation.
You can use TreeMap since its a sorted map.
With Java 8 this is very simple:
import static java.util.Map.Entry.comparingByKey;
public <K extends Comparable<? super K>, V> Iterator<V> orderedIterator(final Map<K, V> map) {
return map.entrySet().stream()
.sorted(comparingByKey())
.map(Map.Entry::getValue)
.iterator();
}
Note, this is slow, as the Stream needs to be sorted each time - so iteration becomes O(n lg n) rather than O(n). If you need to do this a lot, you would be better off using a TreeMap - where insertion is O(lg n) (rather than O(1)) but iteration is still O(n).
I'm not sure this is completely possible, at least from a map point of view, although we could create a special hash map the returns keys from a sort order.
The map could extend HashMap and have a variable the contains the sort order and then have a method that returns the keys and values in the sort order.
You could have a static utility method that takes a HashMap and returns an array of Map.Entrys in the sort order.
While the above may work, TreeMap is probably the way to go. It's designed for this task and was written by Josh Blotch so it's bound to be fast at what it does. Reinventing the wheel always takes longer and doesn't work as well.
Note: This depends on the use case. If you only need to use the sorted values once, then the utility method or custom HashMap implementation will be best. If you're planning on using the Map often, then go with a TreeMap.
as I know keyset() doesn't guarantee any particular order, however does values() get same order as keyset()?
how about linkedhashmap? seems it can provide a consistent order keyset, assume that it also get a same order values()?
There are absolutely no promises about order in hash tables.
The best way to iterate through any java Map is to use the idiom:
for(Map.Entry<K,V> e : map.entrySet()){
K theKey = e.getKey();
V theValue = e.getValue();
// do something with them!
}
This idiom makes the question irrelevant as you are going through entries in the map in the form of key, value pairs.
As already noted, there is no order guarantees except for SortedMaps or LinkedHashMap and the like. If you have a total ordering on your keys, use a SortedMap: always model your problem explicitly.
Is it possible in Guava,
To conduct a reverse lookup in BiMap for key and multiple values? Precisely, I have key and corresponding multiple values, I want to get key from a value.
To store multiple values in LinkedHashMap? Precisely, I want to store, key - multiple values in some order thus I can get key position in the list.
Ad. 1. Yes, it's possible to do a reverse lookup with a BiMap<K, V>, you just call inverse on your BiMap and you get inversed BiMap<V, K> view of your BiMap.
Example (taken from Guava's test suite):
public void testMapConstructor() {
/* Test with non-empty Map. */
Map<String, String> map = ImmutableMap.of(
"canada", "dollar",
"chile", "peso",
"switzerland", "franc");
HashBiMap<String, String> bimap = HashBiMap.create(map);
assertEquals("dollar", bimap.get("canada"));
assertEquals("canada", bimap.inverse().get("dollar"));
}
Ad. 2. Assuming you mean "I want to store, key -> multiple [collection] values" (Map<K, Collection<V>>), ListMultimap is probably what you want, more precisly ArrayListMultimap (preserves values order) or LinkedListMultimap (preserves both keys and values order). If your object is going to be immutable, I strongly advice you use ImmutableListMultimap.
You can also create your own implementation of Multimap by using factory (bit verbose), i.e. I use:
private static <K, V> ListMultimap<K, V> makeLinkedArrayListMultimap() {
return Multimaps.newListMultimap(Maps.<K, Collection<V>>newLinkedHashMap(),
new Supplier<List<V>>() {
#Override public List<V> get() {
return Lists.newArrayList();
}
});
}
public static void main(final String[] args) {
final ListMultimap<String, String> multimap = makeLinkedArrayListMultimap();
multimap.putAll("one", ImmutableList.of("zero", "three"));
multimap.putAll("two", ImmutableList.of("three", "four", "three"));
multimap.putAll("three", ImmutableList.<String>of()); // note that this doesn't add key to multimap
multimap.put("four", "forty-two");
System.out.println(multimap);
// prints {one=[one, three], two=[three, four, three], four=[forty-two]}
final List<String> listForOnes = multimap.get("one");
System.out.println(listForOnes.get(0));
// prints zero
}
P.S. Take a look at Guava's wiki, which is explaining both BiMap and Multimap.
The closest in Guava is Multiset to map multiple values to key, but I doubt it satisfies your requirement.
I doubt it is good idea to look up key using values (when you have multiple values mapped to single key), in order to do this your value should be unique and considering your data structure (which is like Map<Key, Collection<Value>) it cannot be guaranteed to have unique values.
The another option with guava is BiMap which requires unique values and can provide a reverse mappings (value -> key) but since you need to map multiple values to same key, this also not a good fit.
As #Xaerxess says in his answer to your 2nd question, you can make your own ListMultimap that uses a LinkedHashMap as its backing map using the Multimaps.newListMultimap method.
For your 1st question, where you have keys mapped to multiple values (i.e. a Multimap), you can use the method Multimaps.invertFrom to create an inverted copy of your original Multimap to do inverse lookups on. Also, you can create an ImmutableListMultimap copy of the original and use its inverse() method to get the inverse, though that's just going to copy the original just like Multimaps.invertFrom does (though it will cache it so repeated calls to inverse() return the same copy.)
This is likely worth it if you don't mind the extra memory consumption, are going to want to do multiple inverse lookups, and don't need the inverse copy to stay up to date with changes to the original that happen after you create it. If you just want to lookup the keys that map to one specific value, you can do that in one iteration of the entries without creating a full copy.
I'd like to do the following functionality:
if (!map.contains(key)) {
map.put(key, val);
}
Update: Let's assume it's not HashMap so the map is implemented as a tree of some kind.
However note that it's a little inefficient, since if we get into the if we actually search the map twice. I'd actually like to do something like that:
map.put_if_new_key(key, val);
Any idea how to do it in Java?
If you expect to be inserting new elements a vast majority of the time.
ValType temp = map.put(key, val);
if(temp != null)
map.put(key, temp);
I don't think it's a good idea in general, but it is worth considering if you can reason sufficiently about your use case.
Second thought on this if you can use a particular map implementation instead of just the map interface you could do this with a NavigableMap
Map sub = map.subMap(key, true, key, true);
if (!sub.contains(key)) {
sub.put(key, val);
}
Since the sub tree will be 0 or 1 nodes large there is no repeated work.
If you have a ConcurrentMap<K, V> there is the method putIfAbsent:
If the specified key is not already associated with a value, associate it with the given value. This is equivalent to
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
except that the action is performed atomically.
However this method does not exist on Map<K, V>.
I don't think your proposed code is inefficient. See if key is already there in Map then its a single map look-up. And even for the cases when key is not found there are not 2 searches. Only 1 search and 1 insert into Map.
I don't think there's any way around checking "contains()" to see whether or not the key exists or not.
Look here to see if this might be an alternative for you:
What happens when a duplicate key is put into a HashMap?