Unsupported add/addAll operations for Map<K,V>.keySet() - java

Regarding the Map<K,V> interface:
Why does keySet() return a Set that supports the remove operation but doesn't support add() and addAll() operations?

The Set returned by keySet is backed by the Map, so changes to the map are
reflected in the set, and vice-versa. This means that calling remove on that Set removes the matching Entry from the Map.
It would make no sense to call add or addAll on that Set, since you can't add key[s] without corresponding value[s] to the Map.

Think about what you are asking for:
you want to retrieve all KEYS of a map (and that set is not a "copy" of the keys; it represents the keys of the map).
And then you ask to add elements to those KEYS. In other words: the "data set" you are looking at has the semantic meaning of keys coming from a map. And you want to increase that "data set" - but without providing the corresponding entries for that map.
Deletion on the other hand is straight forward; deleting a key will also delete the corresponding entry from the map.

It's because each key in the set is linked to a value in the map. Removing a key will remove the associated value, but to add you'll need a value and not just a key.

Related

get only key from map

I have a Map<String, List<List<Double>>> with only one key, e.g.
{onlyKey=[[1.0, 2.3, 20.1], [6.5, -9.3, 4.5]]}
But I don't actually know what the name of the key is.
How can I get the name of the only key stored within the map?
Answered here.
I know that keySet exists, but I already tried map.keySet().get(0) and Set doesn't have a get(int) method. Pretty sure sets are generally used when you know (or have some idea of) what they contain.
The Map<First, Second> has the method keySet() that returns a Set<First> of the key.
Also: There's entrySet() that returns a Set<Entry<First, Second>>.
Note that return is a Set and not a List, the order of insertion is not preserved nor you have access to get(int index), you need to iterate over a set to get a first value.
From the Map Documentation you can see you can use keySet() to recover the Set of all keys in your Map, which, in your case, will contain your only key.
As an alternative for using iterator you might use stream() and findFirst():
map.keySet().stream().findFirst().get()
I used map.keySet().iterator().next() to get the only key within the map.

ConcurrentSkipListMap put in order

I have a ConcurrentSkipListMap of keys and values. It is very important to hold the order of the keys.
The problem appears when I try to insert a new value in a particular position. The only one method to insert a value is the put() that put this value in the last position.
With the replace method it can only edit the value, not the key.
Is it possible? What can I do? Can you tell me another class to do it?
The ConcurrentSkipListMap holds the order of the keys on its own as they are sorted.
So either you were not aware of it or you actually do not want to hold order of the keys but manipulate them yourself.
If you meant that you want the keys returned in the order in which they were put into the map than use the separet List (ConcurrentQueue if you need concurrency) and place the keys there manually.

"key-value" pairs collection maintaining order and retrieving by key and by pair index?

I'd would like to use a collection of "key - value" pairs:
Which is the base for a JTable data model (TableModel implementation), so I need to access elements by their index/position (e.g. to implement Object getValueAt(int rowIndex, int columnIndex)). The collection must preserve the order of the elements. An ArrayList would be good for that.
Which allows to retrieve elements by the value of the key. A HashMap would be good for that.
The closest usable collection I've found is LinkedHashMap. It preserves the order and allows retrieval by key. However the access to an item is not possible by index/position, I must iterate over the items until the good one is found. This is not be time-effective.
Is there a better one than this one?
Thanks.
(Question is similar to this one, but the solution provided uses the conversion toArray() which is not time-effective. If the set of pairs changes, the conversion needs to be done again.)
There is no such collection implementation in the JRE.
But you can easily overcome this issue by using a Map<K,V> for storing the key-value pairs and an additional List<K> to store the keys in sequential order. You can then access a value either by key or by index using: keyValueMap.get(keys.get(index)).
You must make sure that modifications are always synchronized on both collections:
Add/Change an entry: if (keyValueMap.put(key, value) == null) keys.add(key)
Remove an entry: if (keyValueMap.remove(key) != null) keys.remove(key)
Note that this implementation assumes that values are never null. In case null values are required too, the code gets slightly more complex as we have to check for existence of an entry using keyValueMap.contains(key).
Without implementing your own collection, it might be easiest to just have two collections, with the key-value reversed in the second one but otherwise ordered the same.
(wanted this to be a comment, but not enough rep yet)
Apache Commons Collection has a ListOrderedMap class that makes what you want.

Does adding a duplicate value to a HashSet/HashMap replace the previous value

Please consider the below piece of code:
HashSet hs = new HashSet();
hs.add("hi"); -- (1)
hs.add("hi"); -- (2)
hs.size() will give 1 as HashSet doesn't allow duplicates so only one element will be stored.
I want to know if we add the duplicate element, then does it replace the previous element or it simply doesn't add it?
Also, what will happen usingHashMap for the same case?
In the case of HashMap, it replaces the old value with the new one.
In the case of HashSet, the item isn't inserted.
The first thing you need to know is that HashSet acts like a Set, which means you add your object directly to the HashSet and it cannot contain duplicates. You just add your value directly in HashSet.
However, HashMap is a Map type. That means every time you add an entry, you add a key-value pair.
In HashMap you can have duplicate values, but not duplicate keys. In HashMap the new entry will replace the old one. The most recent entry will be in the HashMap.
Understanding Link between HashMap and HashSet:
Remember, HashMap can not have duplicate keys. Behind the scene HashSet uses a HashMap.
When you attempt to add any object into a HashSet, this entry is actually stored as a key in the HashMap - the same HashMap that is used behind the scene of HashSet. Since this underlying HashMap needs a key-value pair, a dummy value is generated for us.
Now when you try to insert another duplicate object into the same HashSet, it will again attempt to be insert it as a key in the HashMap lying underneath. However, HashMap does not support duplicates. Hence, HashSet will still result in having only one value of that type. As a side note, for every duplicate key, since the value generated for our entry in HashSet is some random/dummy value, the key is not replaced at all. it will be ignored as removing the key and adding back the same key (the dummy value is the same) would not make any sense at all.
Summary:
HashMap allows duplicate values, but not keys.
HashSet cannot contains duplicates.
To play with whether the addition of an object is successfully completed or not, you can check the boolean value returned when you call .add() and see if it returns true or false. If it returned true, it was inserted.
The docs are pretty clear on this: HashSet.add doesn't replace:
Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.
But HashMap.put will replace:
If the map previously contained a mapping for the key, the old value is replaced.
It the case of HashSet, it does NOT replace it.
From the docs:
http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html#add(E)
"Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false."
Correct me if I'm wrong but what you're getting at is that with strings, "Hi" == "Hi" doesn't always come out true (because they're not necessarily the same object).
The reason you're getting an answer of 1 though is because the JVM will reuse strings objects where possible. In this case the JVM is reusing the string object, and thus overwriting the item in the Hashmap/Hashset.
But you aren't guaranteed this behavior (because it could be a different string object that has the same value "Hi"). The behavior you see is just because of the JVM's optimization.
HashMap basically contains Entry which subsequently contains Key(Object) and Value(Object).Internally HashSet are HashMap and HashMap do replace values as some of you already pointed..but does it really replaces the keys???No ..and that is the trick here. HashMap keeps its value as key in the underlying HashMap and value is just a dummy object.So if u try to reinsert same Value in HashMap(Key in underlying Map).It just replaces the dummy value and not the Key(Value for HashSet).
Look at the below code for HashSet Class:
public boolean [More ...] add(E e) {
return map.put(e, PRESENT)==null;
}
Here e is the value for HashSet but key for underlying map.and key is never replaced. Hope i am able to clear the confusion.
You need to check put method in Hash map first as HashSet is backed up by HashMap
When you add duplicate value say a String "One" into HashSet,
An entry ("one", PRESENT) will get inserted into Hashmap(for all the
values added into set, the value will be "PRESENT" which if of type Object)
Hashmap adds the entry into Map and returns the value, which is in this case
"PRESENT" or null if Entry is not there.
Hashset's add method then returns true if the returned value from Hashmap equals
null otherwise false which means an entry already exists...
To say it differently: When you insert a key-value-pair into a HashMap where the key already exists (in a sense hashvalue() gives the same value und equal() is true, but the two objects can still differ in several ways), the key isn't replaced but the value is overwritten. The key is just used to get the hashvalue() and find the value in the table with it.
Since HashSet uses the keys of a HashMap and sets arbitrary values which don't really matter (to the user) as a result the Elements of the Set aren't replaced either.
The simple way to think about it is that if you look at the add method of hashset you will see an optional return type of boolean T / F. The return type as false is meaningful only if the hashset is not able to add the element.

Java HashSet key/value pair

Why doesn't Java provide functions to get the key/value pairs in a HashSet like exists in Hashtable? It seems like a real pain to have to iterate over it every time you need to get at something. Or is there an easier way to do this?
HashSet doesn't have key/value pairs. It is a Set of objects and you would use an implementer of Set to ensure that a collection of objects contained no duplicates.
Implementers of Map like HashMap have key/value pairs and provide a get(Object key) method to get the value associated with a key.
Since a Set doesn't contain keys and values, there is no way such a view could be provided.
What would you consider to be the key and what would be the value in a Set?
A Set don't have any key/value pairs, just (unique) values. As you already said you get these values via the Iterator or by returning an array with these values with the toArray() method.
Maybe you are looking for a List instead.

Categories

Resources