Does ConcurrentHashmap size() method count null keys? - java

Lets say I have a concurrenthashmap with some keys and values that are not null. Later these keys can become null, then what happens? Are they automatically removed from concurrenthashmap or do they stay there as null? Does the size() method count them?

Most Java Maps can contain null values.
The following code is perfectly valid and will return a size of 1.
Map m = new HashMap<String, String>();
m.put("foo", null);
System.out.println(m.size()); //will print "1"
This is because HashMap allows for null values.
Turns out, however, based on my testing, that the following code will NOT work.
Map m = new ConcurrentHashMap<String, String>();
m.put("foo", null); //Will throw a NullPointerException
This is because, apparently, ConcurrentHashMap does not allow for null values.
According to the Javadoc(http://docs.oracle.com/javase/7/docs/api/java/util/Map.html):
Some map implementations have restrictions on the keys and values they may contain. For example, some implementations prohibit null keys and values, and some have restrictions on the types of their keys.
So, there you have it. Hope this helps :)

From ConcurrentHashMap API:
Like Hashtable but unlike HashMap, this class does not allow null to be used as a key or value.

Related

Can I declare a HashMap<key> instead of a HashMap<key,value>?

I'm trying to insert keys into a hashmap but I don't really need values inserted. I could but they wouldn't be used. I know hashmaps can accept null keys and values but only one null key,value pair. I could map.put(key,null) with the values being null but that method seems inefficient. My intention is that I'm going to use the map.containsKey(key) method to determine if a key exists in a hashmap which is why I don't need a value.
With that being said, is there a way to declare HashMap<key> instead of HashMap<key,arbitraryValue> so I won't have to add unnecessary null values? Sorry if this may be a dumb question.
As Jacob G. said, you should use a Set<E>.
A set is just a collection that does not contain any duplicates. The implementing class HashSet<E> actually uses a HashMap<K,V> under the hood, as mentioned by Ole V.V., but using a Set<E> in your code is the better approach IMO, because your problem does not require the values.
Additionally, there is a problem with using a HashMap<K,V> where all your values are null is in the get(K key) method. This method will return null if the requested key does not have an associated value. So how do you know if your call to get returned a valid or invalid null? i.e.
Map<Integer, Object> map = new HashMap<>();
// add entry 0 ->
map.put(0, null);
Object get1 = map.get(0); // returns null, so 0 must be a key in our map!
Object get2 = map.get(1); // also returns null, so is 1 a key too? No!
So, for your specific problem, I would try something like this!
Set<MyKey> set = new HashSet<>(); // or any implementing class
...
MyKey someKey = ...
// Check if your key set doesn't have some key, if so add it to the key set
if (!set.contains(someKey)) {
set.add(someKey);
}

Is it possible to add a key without a value in hashtable Java?

I'm trying to work with Java's Junit Test. My goal is to create a hashtable as
Hashtable< String , Hashtable<String,String> > student =
new Hashtable<String, Hashtable<String, String>>();
Next, I want to add at first only the key. After that, when I have the value then add it to the key in hashtable.
Example
student.put("student1",null) ;
I tried to work with null, but during test process I get a
java.lang.NullPointerException
Is it not possible? Any ideas to how adding only the key without value?
Is it possible to add a key without a value in hashtable Java?
Short answer: No
Why?
Because it will throw and Exception if
* #exception NullPointerException if the key or value is
* <code>null</code>
And in the same source code has a validation
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
...
You can use a Map Instead.
HashTable is an older implementation and HashMap came as an advanced version with more capabilities. You can't call .equals() or .hashCode() on it as null isn't an object.
HashMap is a better replacement for single threaded applications or any time synchronization is not a requirement, because of the performance impact synchronization introduces. If you need a Threadsafe option you can also use ConcurrentHashMap
The documentation says that you can't:
Maps the specified key to the specified value in this hashtable.
Neither the key nor the value can be null.
The best that you can do, if you need the key, is to use an empty HashTable as the value:
student.put("student1", new Hashtable<>());
In most use cases you would not care about putting null into a HashMap. You can check which keys have a value assigned via the keySet() and treat everything else as null (since it was never assigned).
However, your question to me indicates that you need to distinguish three states: Key-Value pair, key that were never assigned a value, and keys that were explicitly assigned a null value.
Since the default HashMap does not support this behavior, you could implement your own class that implements the Map interface. It basically only wraps a HashMap, with a few important exceptions:
When you assign a value of null to a key, you put the key into a Set of "NullKeys" instead of into the HashMap.
When you retrieve a key, check whether it is in the "NullKey" Set. If yes, return null, otherwise look into the HashMap.
There are some special cases (overwriting an existing value with null etc.) but this would be the basic strategy.
(I am not sure whether this is a useful class to have other than in very specialized scenarios but it would meet your requirements.)

Is there an efficient way of checking if HashMap contains keys that map to the same value?

I basically need to know if my HashMap has different keys that map to the same value. I was wondering if there is a way other than checking each keys value against all other values in the map.
Update:
Just some more information that will hopefully clarify what I'm trying to accomplish. Consider a String "azza". Say that I'm iterating over this String and storing each character as a key, and it's corresponding value is some other String. Let's say I eventually get to the last occurrence of 'a' and the value is already be in the map.This would be fine if the key corresponding with the value that is already in the map is also 'a'. My issue occurs when 'a' and 'z' both map to the same value. Only if different keys map to the same value.
Sure, the fastest to both code and execute is:
boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();
which executes in O(n) time.
Sets don't allow duplicates, so the set will be smaller than the values list if there are dupes.
Very similar to EJP's and Bohemian's answer above but with streams:
boolean hasDupeValues = map.values().stream().distinct().count() != map.size();
You could create a HashMap that maps values to lists of keys. This would take more space and require (slightly) more complex code, but with the benefit of greatly higher efficiency (amortized O(1) vs. O(n) for the method of just looping all values).
For example, say you currently have HashMap<Key, Value> map1, and you want to know which keys have the same value. You create another map, HashMap<Value, List<Key>> map2.
Then you just modify map1 and map2 together.
map1.put(key, value);
if(!map2.containsKey(value)) {
map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);
Then to get all keys that map to value, you just do map2.get(value).
If you need to put/remove in many different places, to make sure that you don't forget to use map2 you could create your own data structure (i.e. a separate class) that contains 2 maps and implement put/remove/get/etc. for that.
Edit: I may have misunderstood the question. If you don't need an actual list of keys, just a simple "yes/no" answer to "does the map already contain this value?", and you want something better than O(n), you could keep a separate HashMap<Value, Integer> that simply counts up how many times the value occurs in the map. This would take considerably less space than a map of lists.
You can check whether a map contains a value already by calling map.values().contains(value). This is not as efficient as looking up a key in the map, but still, it's O(n), and you don't need to create a new set just in order to count its elements.
However, what you seem to need is a BiMap. There is no such thing in the Java standard library, but you can build one relatively easily by using two HashMaps: one which maps keys to values and one which maps values to keys. Every time you map a key to a value, you can then check in amortized O(1) whether the value already is mapped to, and if it isn't, map the key to the value in the one map and the value to the key in the other.
If it is an option to create a new dependency for your project, some third-party libraries contain ready-made bimaps, such as Guava (BiMap) and Apache Commons (BidiMap).
You could iterate over the keys and save the current value in the Set.
But, before inserting that value in a Set, check if the Set already contains that value.
If this is true, it means that a previous key already contains the same value.
Map<Integer, String> map = new HashMap<>();
Set<String> values = new HashSet<>();
Set<Integter> keysWithSameValue = new HashSet<>();
for(Integer key : map.keySet()) {
if(values.contains(map.get(key))) {
keysWithSameValue.add(key);
}
values.add(map.get(key));
}

Issue with null elements with synchronized and concurrent collection [duplicate]

This question already has answers here:
Why does ConcurrentHashMap prevent null keys and values?
(7 answers)
Closed 9 years ago.
Most of the synchronized and concurrent collections like HashTable, ConcurrentHashMap etc do not allow null values. Is there any specific issue with null elements?
Hashtable is somewhat obsolete so I won't comment on it. As for ConcurrentHashMap one of the important additions of the API vs. a standard HashMap are a few atomic methods such as putIfAbsent. Javadoc:
Returns the previous value associated with the specified key, or null if there was no mapping for the key
In particular, if the map allowed null keys, the method would be a lot more complicated to use. A typical pattern, where you need to make sure that values can't be overwritten, is:
ConcurrentMap<K,V> map = new ConcurrentHashMap<> ();
V value = map.get(key);
if (value == null) {
value = new V();
V previousValue = map.putIfAbsent(key, value);
if (previousValue != null) { //Here you need to be sure what that means
value = previousValue;
}
}
useValue(value);
Another example is how you check if a key is in a HashMap (and you need the return value):
V value = map.get(key);
if (value == null && !map.containsKey(key)) {
}
The problem in a concurrent environment is that the whole thing is not atomic.
See also this post and these comments by the author of CHM.
HashTable.get(key) method returns null if the specified key is not present in the HashTable. If HashTable allows null as values, there can be two possibilities if I am getting a null from HashTable.get(key) method.
The key is not present in HashTable
The key is present, but the value set was null
It may be confusing for the user of the API.
I believe they don't allow null values just to prevent this ambiguity.
This is a synchronized map that accepts null key and values
Map m = Collections.synchronizedMap(new HashMap());
From the Hashtable JavaDoc:
To successfully store and retrieve objects from a hashtable, the
objects used as keys must implement the hashCode method and the
equals method.
In a nutshell, since null isn't an object, you can't call .equals() or
.hashCode() on it, so the Hashtable can't compute a hash to use it as
a key.
HashMap is newer, and has more advanced capabilities, which are
basically just an improvement on the Hashtable functionality. As such,
when HashMap was created, it was specifically designed to handle null
values as keys and handles them as a special case.
Specifically, the use of null as a key is handled like this when
issuing a .get(key):
(key==null ? k==null : key.equals(k))
Source:Why does Hashtable not take null key?
When you synchronize it means you will obtain lock on that object or at some portion. If your object is null then how will you decide that in which portion you will obtain lock?
Say in concurrenthashmaps, you can divide it into 16 different locks. Now how will you decide where null will be placed?
Null has no value attached to it, it simply means no object. So to avoid ambiguity its better not to have it.

Understanding HashMap<K,V>

Ok, here is the bit I do not understand.
If you attempt to retrieve an object using the get() method and null is returned, it is still possible that null may be stored as the object associated with the key you supplied to the get() method. You can determine if this is the case by passing your key of the object to containsKey() method for map. This returns true if key is stored in the map
So, how is containsKey() supposed to tell me if the value associated with the key supplied is null?
This is the reference if you wanna check. Page 553
Map<String, Object> map = new HashMap<String, Object>();
map.put("Foo", null);
System.out.println(map.containsKey("Foo"));
System.out.println(map.containsKey("Boo"));
OUTPUT:
true
false
get() returns null in two cases:
The key does not exist in the map.
The key does exist but the associated value is null.
You can't tell from get() which is true. However, containsKey() will tell you if the key was present in the map, regardless of whether its associated value was null.
Consider this simple snippet of code:
Map<String, String> m = new HashMap<String, String>();
m.put("key1", "value1");
m.put("key2", null);
System.out.println("m.get(\"key1\")=" + m.get("key1"));
System.out.println("m.containsKey(\"key1\")=" + m.containsKey("key1"));
System.out.println("m.get(\"key2\")=" + m.get("key2"));
System.out.println("m.containsKey(\"key2\")=" + m.containsKey("key2"));
System.out.println("m.get(\"key3\")=" + m.get("key3"));
System.out.println("m.containsKey(\"key3\")=" + m.containsKey("key3"));
As you can see I put in the map two values, one of which is null. Thene i asked the map for three values: two of them are present (one is null), one is not. Look at the result:
m.get("key1")=value1
m.containsKey("key1")=true
m.get("key2")=null
m.containsKey("key2")=true
m.get("key3")=null
m.containsKey("key3")=false
The second and the third are the tricky part. key2 is present with null value so, using get() you cannot discriminate whether the element is not in the map or is in the map with a null value. But, using containsKey() you can, as it returns a boolean.
(get() == null && containsKey()) == value is null
containsKey would tell you if the key is in the hashmap at all. Consider the case where a key is present with null value and the other case in which the key which you are looking for is not at all in the hashmap.

Categories

Resources