Trouble understanding Java map Entry sets - java

I'm looking at a java hangman game here: https://github.com/leleah/EvilHangman/blob/master/EvilHangman.java
The code in particular is this:
Iterator<Entry<List<Integer>, Set<String>>> k = partitions.entrySet().iterator();
while (k.hasNext())
{
Entry<?, ?> pair = (Entry<?, ?>)k.next();
int sizeOfSet = ((Set<String>)pair.getValue()).size();
if (sizeOfSet > biggestPartitionSize)
{
biggestPartitionSize = sizeOfSet;
}
}
Now my question. My google foo is weak I guess, I cannot find much on Entry sets other than the java doc itself. Is is just a temporary copy of the map? And I cannot find any info at all on the syntax:
Entry<?, ?>
Can anyone explain or point me toward an explanation of what is going on with those question marks? Thanks in advance!

An entrySet is the set of all Entries in a Map - i.e. the set of all key,value pairs in the Map. Because a Map consists of key,value pairs, if you want to iterate over it you need to specify whether you want to iterate over the keys, the values, or both (Entries).
The <?,?> indicates that the pair variable holds an Entry where the key and the value could be of any type. This would normally indicate that we don't care what types of values it holds. In your code this is not the case, because you need to cast the value to Set<String> so you can check its size.
You could also rewrite the code as follows, avoiding the cast to Set<String>
Iterator<Entry<List<Integer>, Set<String>>> k = partitions.entrySet().iterator();
while (k.hasNext())
{
Entry<?, Set<String>> pair = (Entry<?, Set<String>>)k.next();
int sizeOfSet = pair.getValue().size();
if (sizeOfSet > biggestPartitionSize)
{
biggestPartitionSize = sizeOfSet;
}
When we need to be more specific about the types that the Entry holds, we can use the full type: Entry<List<Integer>, Set<String>>. This avoids the need to cast the key or value to a particular type (and the risk of casting to the wrong type).
You can also specify just the type of the key, or the value, as shown in my example above.

You can find information about Entry in the Javadoc:
http://docs.oracle.com/javase/7/docs/api/java/util/Map.Entry.html
The <?, ?> part is because Entry is a generic interface.
More info on ? here: http://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
That being said, the usage in this example is not very nice. A cleaner way of getting sizeOfSet:
int sizeOfSet = k.next().getValue().size();

You aren't supposed to know much about the entrySet() function returns. All you are allowed to depend on is that it is a Set<Map.Entry<x,y>>. It might be a rather special (*) copy, but it's more likely to be an object that provides a view of the innards of the original Map.
In modern Java, this sort of thing get commonly written as:
for (Map.Entry<X, Y> me : map.entrySet()) {
// code that looks at me.xxx() here
}
In your example, X,Y is List<Integer>, Set<String>.
(*) The documentation says, 'The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.'
Thanks to #Miserable Variable.

Entry<?, ?>
Can anyone explain or point me toward an explanation of what is going on with those question marks?
I could be wrong but I think the programmer is trying to be lazy. It should have been
Entry<List<Integer>, Set<String>>>
then none of the casts would have been required
ALSO:
My google foo is weak I guess, I cannot find much on Entry sets other than the java doc itself. Is is just a temporary copy of the map?
Javadoc has everything you need (emphasis mine):
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.
If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined.
The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll and clear operations.
It does not support the add or addAll operations.
What more information are you looking for?

Related

How to create HashMap with streams overriding duplicates?

I'm creating a HashMap using java8 stream API as follows:
Map<Integer, String> map = dao.findAll().stream()
.collect(Collectors.toMap(Entity::getType, Entity::getValue));
Now if an element is added to the collection where the key already exists, I just want to keep the existing element in the list and skip
the additional element. How can I achieve this? Probably I have to make use of BinaryOperation<U> of toMap(), but could anyone provide
an example of my specific case?
Yes, you need that BinaryOperation<U> and use it as a third argument for Collectors.toMap().
In case of a conflict (appearance of an already existing key) you get to choose between value oldValue (the existing one) and newValue. In the code example we always take value oldValue. But you are free to do anything else with these two values (take the larger one, merge the two etc.).
The following example shows one possible solution where the existing value always remains in the map:
Map<Integer, String> map = dao.findAll().stream()
.collect(Collectors.toMap(Entity::getType, Entity::getValue, (oldValue, newValue) -> oldValue));
See the documentation for another example.

How to check whether a HashMap has all the elements of an ArrayList?

If I have a HashMap hashM, an ArrayList arrayL. If I would like to use an if statement to check whether hashM has all the elements in arrayL, how can I do that?
I cm currently using something like
if (hashM.values().containsAll(arrayL.getPreReqs()))
However it doesn't work properly.
Dear all thanks for the answers!
Actually containsAll works however the way I structure the my codes is wrong so that I got wrong outcomes. Now it has been fixed.
Cheers!
Given
Map<?,?> map = new HashMap<?,?>();
List<?> list = new ArrayList<?>();
The approach you tried (well, nearly, as pointed out by Marko Topolnik) is indeed correct:
if (map.values().containsAll(list)) { ... }
(Or map.keySet().containsAll(list) if you were interested in the map keys instead of values.)
For this to work as expected for custom types, you of course must have implemented equals() and hashcode() correctly for them. (See e.g. this question or better yet, read Item 9 in Effective Java.)
By the way, when working with Java Collections, it is good practice to define fields and variables using the interfaces (such as List, Set, Map), not implementation types (e.g. ArrayList, HashSet, HashMap). For example:
List<String> list = new ArrayList<String>();
Map<Integer, String> map = new HashMap<Integer, String>();
Similarly, a more "correct" or fluent title for your question would have been "How to check whether a Map has all the elements of a List?". Check out the Java Collections tutorial for more info.
Your code is correct except..
if (hashM.values().containsAll(arrayL)) {....}
[EDIT]
You can use HashMap.containsValue(Object value)
public boolean containsList(HashMap<K, V> map, List<V> list) {
for(V value : list) {
if(!map.containsValue(value)) {
return false;
}
}
return true;
}
Your code should work - but will not be particularly efficient. You need to compare every element in the list with every element in the map.
If (and only if) you can easily extract the key of the map from the elements then you would be better off looping through your List and for each element do map.containsKey(getKey(elem)), this will be much faster.
If you are doing this sort of comparison a lot and you cannot map from element to key then it may be worth keeping a HashSet of the values for this purpose.
I agree with JoniK. This can be done in a single line like this.
if(hashM.values().containsAll(arrayL)) {// put your code here that will be returned}

Is it possible/required to speed up HashMap operations on same entry?

Suppose I wish to check HashMap entry and then replace it:
if( check( hashMap.get(key) ) ) {
hashMap.put(key, newValue);
}
this will cause search procedure inside HashMap to run two times: once while get and another one while put. This looks ineffective. Is it possible to modify value of already found entry of Map?
UPDATE
I know I can make a wrapper and I know I have problems to mutate entry. But the question is WHY? May be HashMap remembers last search to improve repeated one? Why there are no methods to do such operation?
EDIT: I've just discovered that you can modify the entry, via Map.Entry.setValue (and the HashMap implementation is mutable). It's a pain to get the entry for a particular key though, and I can't remember ever seeing anyone do this. You can get a set of the entries, but you can't get the entry for a single key, as far as I can tell.
There's one evil way of doing it - declare your own subclass of HashMap within the java.util package, and create a public method which just delegates to the package-private existing method:
package java.util;
// Please don't actually do this...
public class BadMap<K, V> extends HashMap<K, V> {
public Map.Entry<K, V> getEntryPublic(K key) {
return getEntry(key);
}
}
That's pretty nasty though.
You wouldn't normally modify the entry - but of course you can change data within the value, if that's a mutable type.
I very much doubt that this is actually a performance bottleneck though, unless you're doing this a heck of a lot. You should profile your application to prove to yourself that this is a real problem before you start trying to fine-tune something which is probably not an issue.
If it does turn out to be an issue, you could change (say) a Map<Integer, String> into a Map<Integer, AtomicReference<String>> and use the AtomicReference<T> as a simple mutable wrapper type.
Too much information for a comment on your question. Check the documentation for Hashmap.
This implementation provides constant-time performance for the basic
operations (get and put), assuming the hash function disperses the
elements properly among the buckets. Iteration over collection views
requires time proportional to the "capacity" of the HashMap instance
(the number of buckets) plus its size (the number of key-value
mappings). Thus, it's very important not to set the initial capacity
too high (or the load factor too low) if iteration performance is
important.
Constant time means that it always requires the same amount of time to do the get and put operations [O(1)]. The amount of time that is going to be required is going to be linear based on how many times you need to loop [O(n)].
You can change the entry if it is mutable. One example of where you might do this is
private final Map<String, List<String>> map = new LinkedHashMap<>();
public void put(String key, String value) {
List<String> list = map.get(key);
if (list == null)
map.put(key, list = new ArrayList<>());
list.add(value);
}
This allows you to update a value, but you can't find and replace a value in one operation.
Take a look at trove ( http://trove4j.sourceforge.net/ ), their maps do have several methods that might be what you want:
adjustOrPut
putIfAbsent
I don't know how this is implemented internally, but i would guess that since trove is made to be highly performant, there will be only one lookup.

java hashmap basic question

HashMap model1 = wordobject.getMap();
Set sample = model1.keySet();
Iterator it = sample.iterator();
==
Can you please explain me the above 3 lines?
I see that we are trying to get the hash table from the object and get it assigned to the HashMapmodel1.
1) what is keyset?
2) what does .iterator do?
You are declaring a typical Java HashMap in the first line (a little obvious). You usually construct a HashMap using generics for key value pairs: HashMap<K,V>
The Java API HashMap class allows you to get a set of the keys used for the HashMap. The keySet() method returns a Set<K>.
An iterator allows you to iterate through the set calling methods like next() and hasNext(). It is a way to traverse the set sequentially.
A Hashtable != HashMap
The documentation for Map#keyset() says Returns a Set view of the keys contained in this map
The documentation for Iterator in Java - a more general discussion can be found on Wikipedia
the ketSet() is clearly going to return a Set object (hence when we are instantiating the Set object, sample, with it's result). This Set contains all of the Key Values from the HashMap. Its type will be whatever type the Keys in the hasMap were. The iterator provides a way to step through the elements of the set.
oh and as someone pointed out, we are getting a HashMap from wordobject.
An iterator allows you to loop through the Set. A Set is like an ArrayList but does not allow you to index it. Quick google of set or iterator will give you some more info about them both. Here's a link that explains iterators: http://www.java-samples.com/showtutorial.php?tutorialid=235
1) keySet() is a method on map that returns all the keys for the map. Just to make it clearer, map is like a collection of pairs. i.e. each item in a map has a key and a value associated with it. Like a english dictionary, where each item in the dictionary is a word (the key) and a corresponding meaning (the value). So, keySet() will return a set of all the keys, i.e. the words in the dictionary.
2).iterator() returns an Iterator for the set. You can use the iterator "it" to iterate through the items in the set by using its methods like "next()", "hasNext()", "remove()".. etc..
Read up a little more Java docs to learn more.

How to transform List<String> to Map<String,String> with Google collections?

I have a list of strings and I have a function to generate a value for each key in the list.
I want to create a map using this function. Can I do this with Google collections?
Use Maps.uniqueIndex(Iterable, Function) :
Returns an immutable map for which the
Map.values() are the given elements in
the given order, and each key is the
product of invoking a supplied
function on its corresponding value.(from javadoc)
Example:
Map<String,String> mappedRoles = Maps.uniqueIndex(yourList, new Function<String,String>() {
public String apply(String from) {
// do stuff here
return result;
}});
As of 7/26/2012, Guava master contains two new ways to do this. They should be in release 14.0.
Maps.asMap(Set<K>, Function<? super K, V>) (and two overloads for SortedSet and NavigableSet) allows you to view a Set plus a Function as a Map where the value for each key in the set is the result of applying the function to that key. The result is a view, so it doesn't copy the input set and the Map result will change as the set does and vice versa.
Maps.toMap(Iterable<K>, Function<? super K, V>) takes an Iterable and eagerly converts it to an ImmutableMap where the distinct elements of the iterable are the keys and the values are the results of applying the function to each key.
EDIT: It's entirely possible that Sean's right and I misunderstood the question.
If the original list is meant to be keys, then it sounds like you might be able to just use a computing map, via MapMaker.makeComputingMap, and ignore the input list to start with. EDIT: As noted in comments, this is now deprecated and deleted in Guava 15.0. Have a look at CacheBuilder instead.
On the other hand, that also doesn't give you a map which will return null if you ask it for a value corresponding to a key which wasn't in the list to start with. It also won't give you In other words, this may well not be appropriate, but it's worth consideration, depending on what you're trying to do with it. :)
I'll leave this answer here unless you comment that neither approach here is useful to you, in which case I'll delete it.
Original answer
Using Guava you can do this pretty easily with Maps.uniqueIndex:
Map<String, String> map = Maps.uniqueIndex(list, keyProjection);
(I mentioned Guava specifically as opposed to Google collections, as I haven't checked whether the older Google collections repository includes Maps.uniqueIndex.)
Either I have misunderstood you or the other posters have. I understand that you want your list to be the map keys, while Maps.uniqueIndex() creates keys to map to your values (which is quite the opposite).
Anyway, there is an open Guava issue that requests the exact functionality you are requesting, and I have also implemented such a solution for a previous question.
Using Guava + Lambda
Map<String, YourCustomClass> map = Maps.uniqueIndex(YourList, YourCustomClass -> YourCustomClass.getKey());

Categories

Resources