I have been wondering about the Map from java.util.
Why does values() method return a Collection while the keySet and entrySet return a Set?
What's the advantages/disadvantages of a Set and Collection?
A set guarantees that a given entry can only exist in it once. A Collection doesn't. Since a Map has no uniqueness guarantees in terms of values, the set of them isn't really a set at all but would have to be a Collection.
It's not really an issue of advantages and disadvantages -- it's what the keys, values and entries of a map represent that's important.
Keys in a map are unique
The keys in a Map are unique -- that is, there aren't going to be duplicate keys in a Map. A Collection which assures that duplicates don't exist is a Set.
Therefore, the keys are returned as a Set by the keySet method.
Values in a map are not necessarily unique
On the other hand, the values of a Map does not have to be unique.
For example, we could have an entry in a map with the key "fruit" map to the value "apple", and also have another entry with key "computer" mapping to the value "apple":
map {
key:"fruit" -> value:"apple"
key:"computer" -> value:"apple"
}
Having duplicate values in a map is allowed.
Therefore, we cannot use a Set, as that necessitates that all the entries unique. A good choice for the values of a Map is to return a plain-old Collection as it does not impose any restrictions to what the values are.
Entries in a map are also unique
The entries of the Map are unique -- they are a combination of the key and value, represented by the Map.Entry object. Since this key-value pair is unique, it is returned as a Set by the entrySet method.
Further reading
Wikipedia: associative arrays (map)
Wikipedia: set
Map internally manages Set of keys because keys are unique values aren't
Returns a Set view of the keys 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), 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.
Also See
Javadoc
Source Code
A Set is a Collection that contains no duplicate elements. The advantage of returning a Set when possible is that it makes the guarantee of uniqueness explicit.
As already pointed out by others, values() cannot return a Set because the collection of values may contain duplicates.
values() could be duplicated, so it is Collection.
keySet() and entrySet() couldn't be duplicated, so they are Set.
ps: Set is a non-duplicated Collection.
Related
I've used a HashMap to order keys out from their values. Now I want the keys transfered over to a list, where the order of the keys will be the same. I've tried multiple option to add each key to a list, but none of them keeps the same order of the keys, as they had in the HashMap.
HashMap is the most known and common implementation of Map. However:
This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
If you want to persist the order in which the elements have been put to the Map, you need to use LinkedHashMap implementation. As the documentation states:
This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
What are the practical scenario for choosing among the linkedhashmap and hashmap? I have gone through working of each and come to the conclusion that linkedhashmap maintains the order of insertion i.e elements will be retrieved in the same order as that of insertion order while hashmap won't maintain order.
So can someone tell in what practical scenarios selection of one of the collection framework and why?
LinkedHashMap will iterate in the order in which the entries were put into the map.
null Values are allowed in LinkedHashMap.
The implementation is not synchronized and uses double linked buckets.
LinkedHashMap is very similar to HashMap, but it adds awareness to the order at which items are added or accessed, so the iteration order is the same as insertion order depending on construction parameters.
LinkedHashMap also provides a great starting point for creating a Cache object by overriding the removeEldestEntry() method. This lets you create a Cache object that can expire data using some criteria that you define.
Based on linked list and hashing data structures with linked list (think of indexed-SkipList) capability to store data in the way it gets inserted in the tree. Best suited to implement LRU ( least recently used ).
LinkedHashMap extends HashMap.
It maintains a linked list of the entries in the map, in the order in which they were inserted. This allows insertion-order iteration over the map. That is,when iterating through a collection-view of a LinkedHashMap, the elements will be returned in the order in which they were inserted. Also if one inserts the key again into the LinkedHashMap, the original order is retained. This allows insertion-order iteration over the map. That is, when iterating a LinkedHashMap, the elements will be returned in the order in which they were inserted. You can also create a LinkedHashMap that returns its elements in the order in which they were last accessed.
LinkedHashMap constructors
LinkedHashMap( )
This constructor constructs an empty insertion-ordered LinkedHashMap instance with the default initial capacity (16) and load factor (0.75).
LinkedHashMap(int capacity)
This constructor constructs an empty LinkedHashMap with the specified initial capacity.
LinkedHashMap(int capacity, float fillRatio)
This constructor constructs an empty LinkedHashMap with the specified initial capacity and load factor.
LinkedHashMap(Map m)
This constructor constructs a insertion-ordered Linked HashMap with the same mappings as the specified Map.
LinkedHashMap(int capacity, float fillRatio, boolean Order)
This constructor construct an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode.
Important methods supported by LinkedHashMap
Class clear( )
Removes all mappings from the map.
containsValue(object value )>
Returns true if this map maps one or more keys to the specified value.
get(Object key)
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
removeEldestEntry(Map.Entry eldest)
Below is an example of how you can use LinkedHashMap:
Map<Integer, String> myLinkedHashMapObject = new LinkedHashMap<Integer, String>();
myLinkedHashMapObject.put(3, "car");
myLinkedHashMapObject.put(5, "bus");
myLinkedHashMapObject.put(7, "nano");
myLinkedHashMapObject.put(9, "innova");
System.out.println("Modification Before" + myLinkedHashMapObject);
System.out.println("Vehicle exists: " +myLinkedHashMapObject.containsKey(3));
System.out.println("vehicle innova Exists: "+myLinkedHashMapObject.containsValue("innova"));
System.out.println("Total number of vehicles: "+ myLinkedHashMapObject.size());
System.out.println("Removing vehicle 9: " + myLinkedHashMapObject.remove(9));
System.out.println("Removing vehicle 25 (does not exist): " + myLinkedHashMapObject.remove(25));
System.out.println("LinkedHashMap After modification" + myLinkedHashMapObject);
Shopping Cart is a real life example, where we see cart number against Item we have chosen in order we selected the item. So map could be LinkedHashMap<Cart Number Vs Item Chosen>
HashMap makes absolutely no guarantees about the iteration order. It can (and will) even change completely when new elements are added.
LinkedHashMap will iterate in the order in which the entries were put into the map
LinkedHashMap also requires more memory than HashMap because of this ordering feature. As I said before LinkedHashMap uses doubly LinkedList to keep order of elements.
In most cases when using a Map you don't care whether the order of insertion is maintained. Use a HashMap if you don't care, and a LinkedHashMap is you care.
However, if you look when and where maps are used, in many cases it contains only a few entries, not enough for the performance difference of the different implementations to make a difference.
LinkedHashMap maintain insertion order of keys, i.e the order in which keys are inserted into LinkedHashMap. On the other hand HashMap doesn't maintain any order or keys or values. In terms of Performance there is not much difference between HashMap and LinkedHashMap but yes LinkedHashMap has more memory foot print than HashMap to maintain doubly linked list which it uses to keep track of insertion order of keys.
A HashMap has a better performance than a LinkedHashMap because a LinkedHashMap needs the expense of maintaining the linked list. The LinkedHashMap implements a normal hashtable, but with the added benefit of the keys of the hashtable being stored as a doubly-linked list.
Both of their methods are not synchronized.
Let's take a look their API documentation:
The HashMap is a hash table with buckets in each hash slot.
API documentation:
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.
LinkedHashMap is a linked list implementing the map interface. As
said in the API documentation:
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, which is
normally the order in which keys were inserted into the map
(insertion-order).
One way that I have used these at work are for cached backend REST queries. These also have the added benefit of returning the data in the some order for the client. You can read more about it in the oracle docs:
https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html
This technique is particularly useful if a module takes a map on input, copies it, and later returns results whose order is determined by that of the copy. (Clients generally appreciate having things returned in the same order they were presented.)
A special constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches. Invoking the put, putIfAbsent, get, getOrDefault, compute, computeIfAbsent, computeIfPresent, or merge methods results in an access to the corresponding entry (assuming it exists after the invocation completes). The replace methods only result in an access of the entry if the value is replaced. The putAll method generates one entry access for each mapping in the specified map, in the order that key-value mappings are provided by the specified map's entry set iterator. No other methods generate entry accesses. In particular, operations on collection-views do not affect the order of iteration of the backing map.
I'm wondering if a HashMap uses a HashSet to store its keys. I would guess it does, because a HashMap would correspond with a HashSet, while a TreeMap would correspond with a TreeSet.
I looked at the source code for the HashMap class, and the method returns an AbstractSet that's implemented by some kind of Iterator.
Additionally...when I write
HashMap map = new HashMap();
if(map.keySet() instanceof HashSet){
System.out.println("true");
}
The above if statement never runs. Now I'm unsure
Could someone explain how the HashMap stores its keys?
You're actually asking two different questions:
Does a HashMap use a HashSet to store its keys?
Does HashMap.keySet() return a HashSet?
The answer to both questions is no, and for the same reason, but there's no technical reason preventing either 1. or 2. from being true.
A HashSet is actually a wrapper around a HashMap; HashSet has the following member variable:
private transient HashMap<E,Object> map;
It populates a PRESENT sentinel value as the value of the map when an object is added to the set.
Now a HashMap stores it's data in an array of Entry objects holding the Key, Value pairs:
transient Entry<K,V>[] table;
And it's keySet() method returns an instance of the inner class KeySet:
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet<K> {
// minimal Set implementation to access the keys of the map
}
Since KeySet is a private inner class, as far as you should be concerned it is simply an arbitrary Set implementation.
Like I said, there's no reason this has to be the case. You could absolutely implement a Map class that used a HashSet internally, and then have your Map return a HashSet from .keySet(). However this would be inefficient and difficult to code; the existing implementation is both more robust and more efficient than naive Map/Set implementations.
Code snippets taken from Oracle JDK 1.7.0_17. You can view the source of your version of Java inside the src.zip file in your Java install directory.
I'm wondering if a HashMap uses a HashSet to store its keys.
That would not work too well, because a Set only keeps track of the keys. It has no way to store the associated value mapping.
The opposite (using a Map to store Set elements) is possible, though, and this approach is being used:
HashSet is implemented by using a HashMap (with a dummy value for all keys).
The set of keys returned by HashMap#keySet is implemented by a private inner class (HashMap.KeySet extends AbstractSet).
You can study the source for both class, for example on GrepCode: HashMap and HashSet.
Could someone explain how the HashMap stores its keys?
It uses an array of buckets. Each bucket has a linked list of entries. See also
How does Java HashMap store entries internally
Hashmap and how this works behind the scene
The set that is returned by the keySet is backed by the underlying map only.
As per javadoc
Returns a Set view of the keys 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), 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.
Blockquote
HashMap stores keys into buckets. Keys that have same hash code goes into the same bucket. When retrieving value for an key if more than one key is found in the bucket than equals method is used to find the right key and hence the right value.
Answer is: NO.
HashMap.keySet() is a VIEW of the keys contained in this map.
The data of the map is stored in Entry[] table of HashMap.
I have a fixed set of key value pairs with which I am initializing the LinkedHashMap<String, Double>.
Does the insertion order remain the same when I do a
LinkedHashMap.put(existingKey, newValue);
To be precise my question is, when we update a value for a key using the put method, is the order of initial insertion disturbed??
If so 1) why?
2) How can I prevent this to preserve the initial order of insertion?
I chose LinkedHashMap because, I wanted a collection that supports a key value pair and maintains the the insertion order.
TIA
From the class doc:
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.)
This linked list defines the iteration ordering, which is normally
the order in which keys were inserted into the map (insertion-order).
So, yes, keySet(), values(), and entrySet() (the three collection views mentioned) return values in the order the internal linked list uses. And yes, the JavaDoc for Map and LinkedHashMap guarantee it.
I can't confirm this in the documentation but if i have a LinkedHashMap and i call keySet() on it and iterate over this set is it guaranteed to iterate in insertion order?
It's specified in the Map documentation:
The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings. The order of a map is defined as the order in which the iterators on the map's collection views return their elements. Some map implementations, like the TreeMap class, make specific guarantees as to their order; others, like the HashMap class, do not.
That means for LinkedHashMap, all the 3 methods - values(), keySet() and entrySet(), each of them providing 3 different collection views, are guaranteed to iterate in the insertion order.
Yes.
See the docs(that you can not see) here: http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html
Hash table and linked list implementation of the Map interface, with
predictable iteration order. This implementation differs from HashMap
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, 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.)