Strange results with null in Map - java

I ran the following code
public class MapTest {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put(null, null);
System.out.println(map.get(null));
System.out.println(map.containsKey(null));
System.out.println(map.containsValue(null));
}
}
And it gave this output
null
true
true
However, when I remove the line map.put(null, null), map.get(null) still returns null, but map.containsKey(null) & map.containsValue(null) return false. Now, if the value null is not associated with any key, how come it is possible that map.get(null) still returns null?

The Javadoc for the Map interface states that get should return null for any key that does not have a mapping:
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
Note however that some Map implementations may not allow null keys, in which case map.get(null) will throw a NullPointerException. An example of this is the ConcurrentHashMap.

If the map doesn't find the value in the map, then get(object) still returns null.
So for any arbitrary values you will get null as well.

Here's an except of the get() source code of HashMap:
public V get(Object key) {
if (key == null)
return getForNullKey();
...
}
If the key passed is null, it gets that key and returns the value associated to it, else it returns null.
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}

map.get(null) returns null because:
Java Hash Map get Method
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
More formally, if this map contains a mapping from a key k to a value v such that (key==null ? k==null : key.equals(k)), then this method returns v; otherwise it returns null. (There can be at most one such mapping.)
A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.
This is possible:
map.put(null, null);
because:
Java Hash Map
This implementation provides all of the optional map operations, and permits null values and the null key.

There is no Strange behavior here. Mapis a interface in java collection framework and HashMap is implementaion.
Simply map is a data structure allows to store key,value pair.
in here you are putting null as key and null as value. your HashMap<String,Integer> accept these value since String and Integers default value is null.
Then your are calling
map.get(null) // argument is a key and return value assign to that key. So you are getting null.
map.containsKey(null)// this method return true if particular key is there. In this case null is your key since return true
map.containsKey(null) // this method return true if there is null as value in your may. This case this is also true.
And the next time your are getting null since map is empty and Integers default value is null. and obviously other two method return false since map is empty.

Related

Difference between map.replace() and map.put() for updating a value in Map in Collection Framework [duplicate]

I want to make a histogram by using a HashMap, the key should be the delay, the value the amount of times this delay occurs. I am doubting to use the HashMap replace or the HashMap put function if an already existing delay has an new occurence. I did it by this way:
int delay = (int) (loopcount-packetServed.getArrivalTime());
if(histogramType1.containsKey(delay)) {
histogramType1.replace(delay, histogramType1.get(delay) + 1);
} else {
histogramType1.put(delay, 1);
}
Is this correct? or should I use two times the put function?
There is absolutely no difference in put and replace when there is a current mapping for the wanted key. From replace:
Replaces the entry for the specified key only if it is currently mapped to some value.
This means that if there is already a mapping for the given key, both put and replace will update the map in the same way. Both will also return the previous value associated with the key. However, if there is no mapping for that key, then replace will be a no-op (will do nothing) whereas put will still update the map.
Starting with Java 8, note that you can just use
histogramType1.merge(delay, 1, Integer::sum);
This will take care of every condition. From merge:
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null.
In this case, we are creating the entry delay -> 1 if the entry didn't exist. If it did exist, it is updated by incrementing the value by 1.
In your case, since you first check if the value is contained in the map, using put or replace leads to the same result.
You can use either, based on what is more readable to you.
If you look at the sources you can see the following (this is from update 11 but probably hasn't changed much):
replace:
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
put (internal method putVal):
//some code before this to find the node e (similar to getNode(hash(key)))
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null) //onlyIfAbsent is false here
e.value = value;
afterNodeAccess(e);
return oldValue;
}
As you can see, the relevant parts of the code do basically the same thing since onlyIfAbsent is false for put and thus always will replace the value.
You can verify the behavior the others have described, with this:
public class Main {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.replace("a", "1");
System.out.println(map.get("a"));
map.put("a", "1");
System.out.println(map.get("a"));
map.replace("a", "2");
System.out.println(map.get("a"));
}
}

Hashtable - Get the 1st element before index

I have this hashtable :
private final Hashtable<Integer,Character> htable = new Hashtable<>();
I am storing some element in the table with indexes that may reach high ranges.
Then when i want to get an item, if it does not exist, i would like the get the first existing one before.
A naive way to do it could be :
int index = given_index;
while(htable.get(index) == null && index >= 0)
index --;
Doing this may compute a big amount of values.
Is there a better strategy, or maybe another sort of table allowing to compute less ?
NavigableMap (as mentioned in comment by user15358848)
Ref: NavigableMap
Generally, implementations should not support null values. If there are implementations that supports null, it will be impossible to check whether the response null was due to absence or an actual value.
lowerEntry
Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.
get value for key
if fetched value is null, then fetch lowerEntry
getOrDefault(key, navigableMap.lowerEntry(index)) will be costly if actual key is present mostly due to additional navigableMap.lowerEntry call
Character value = navigableMap.get(index);
if (value == null) {
value = navigableMap.lowerEntry(index);
}
return value;
floorEntry
Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key.
read using floorEntry
if non-null entry, return value
else return null
Map.Entry<Integer,​ Character> entry = navigableMap.floorEntry(index);
return null != entry ? entry.getValue() : null;
get the index or the index in front of it with a lambda
htable.keySet().stream().sorted()
.filter(i -> i <= target).max(Integer::compare).orElse(-1);
… where target is the index You are searching

Iteration on lazy map implementation with fixed key set

I'm working on a project where I need a lazy map that generates values on demand based on a fixed key set. Both whether a key is in the key set and the value associated to a key shall only be computed on demand.
Without a fixed key set, map.containsKey(key) only returns true if a value for the key has been computed and added to the map before (via calling get(key)). This is not the desired behavior - map.containsKey(key) should return true if the key is part of the key set.
An example key set would be all integers from 0 to 1.000. I do not want to fill the map with all keys until it is necessary (e.g. because of an map.containsValue(value) call). The key set itself is easy to calculate, but I only want to fill the map if necessary to save memory. It is also simple to find out whether a key should be contained in the set.
Using a wrapper around an existing HashMap, I created an implementation that fulfills the above requirements.
Example map.containsKey(key) implementation:
private final Map<Integer, String> intern = new HashMap<>();
#Override
public synchronized boolean containsKey(Object key) {
if (intern.containsKey(key)) {
return true;
} else {
if (key instanceof Integer) {
Integer i = (Integer) key;
boolean contained = isKeyInKeySet(i);
if (contained) {
intern.put(i, null);
}
return true;
} else {
return false;
}
}
}
(Note that map.get(key) computes values that are null.)
I can't get iterations on the collection views of the map (map.keySet(), map.values() and map.entrySet()) to work. How do I adapt the default Iterator to also include all values in the key set - even if they are not part of the map yet?

How to retrieve a collection of values from a HashMap

I'm writing a function to test if a HashMap has null values. The method .values() SHOULD return a collection of just the values, but instead I receive a map with both the keys and values stored inside. This is no good as the purpose of my function is to check if the values are null, but if I return a map with keys AND values then .values().isEmpty() returns false if I have a key stored with no value.
public Map<KEY, List<VALUES>> methodName() {
if (MAPNAME.values().isEmpty()) {
throw new CustomErrorException(ExceptionHandler.getErrorWithDescription(ErrorConstants.ERROR_MSG_01));
} else {
return MAPNAME;
}
}
In the above example, .values() always returns a map containing all the keys and values. My method never throws a CustomErrorException if the HashMap has a key, which is bad since it's supposed to detect if there are no values. Help!
There's no such thing as a Map implementation that has a key stored without a value. All Map implementations either:
throw an exception in put when the value is null
Add an entry with a key and a value of null
A key that maps to null is very different than a key without a value. The key has a value, and that value is null (and that means that the values collection won't be empty, unless the map is empty). A key without a value is a key that's not contained in the map.
Long story short, you probably want to use MAPNAME.values().contains(null) or even just MAPNAME.containsValue(null) to do what you want. Alternatively, if you're checking that every key maps to null, check that by iterating over the .values() collection.
You're returning the map -- MAPNAME, not the values:
return MAPNAME.values();
If you're trying to determine if the map contains any null values, you should iterate over the collection of values and check each one to see if its null.
A map that contains an entry with a null value is not empty.
You're not being very clear about what you want -- your map values are lists -- considering that, there are three ways to have a key map to "no values":
A key mapped to null (then the test is map.values().contains(null) )
A key mapped to an empty list (then the test is map.values().contains(Collections.emptyList()) )
A key mapped to a list full of nulls.
What your method above is doing right now is throwing an exception if the map is truly empty (no keys), and returning the map otherwise.
It is not clear what you want. If you want the method to throw an exception only if the map has no meaningful values (all keys map either to null or to empty lists) then something like this is what you need:
public Map<KEY, List<VALUES>> methodName() {
for( List<VALUES> values : MAPNAME.values() ) // 1
if( null != values ) // 2
for( VALUES value : values ) // 3
if( null != value ) // 4
return MAPNAME;
throw new CustomErrorException(ExceptionHandler.getErrorWithDescription(ErrorConstants.ERROR_MSG_01));
}
This throws an exception in the all reasonably conceivable "empty map" scenarios -- if (1) the map is truly empty, or (2) it contains only null keys, or (3) it only contains only null values or empty lists, or (4) it contains only null values or empty lists or lists of nulls.
(Levels of "emptiness" tests in the text above correspond to the comment labels in the code).
Use the values() method in this way:-
Collection set=MAPNAME.values();
And then use a foreach loop to check if every value is null or not.

What is inside an empty index of a Hashmap?

I have a hashmap that is 101 keys in size, but I know for sure about 6 of those have no data inside, and there may be more without data as well. What exactly is inside the empty indexes? Is it null? or is there a Hash(index).isEmpty() method that I can use to see if its empty?
I realize there is a isEmpty method inside hashmap, but I thought that only checked if the entire map was empty not just a single index.
I realize there is a isEmpty method
inside hashmap, but I thought that
only checked if the entire map was
empty not just a single index.
I think what you're looking for is the containsKey(Object) method. According to the documentation:
Returns true if this map contains a
mapping for the specified key. More
formally, returns true if and only if
this map contains a mapping for a key
k such that (key==null ? k==null :
key.equals(k)). (There can be at most
one such mapping.)
Parameters:
key - key whose presence in this map is to be tested
Returns:
true if this map contains a mapping for the specified key
Well, for the keys to arrive there with no data, you have to put them there.
If you did map.put(key, null) then yes the data for that key is null. You always have to give the second parameter to the method, you can't just map.put(key).
If you know for sure that a certain key should have no data you could try going into debug mode and putting a watch for myMap.get(myEmptyKey) and see what you get (in case that no data is an empty object or something else, you should be able to see that).
Edit: Some code would be useful to help you, but if I understand correctly you do something like this:
for (Object obj : list) {
if (matchesCriteriaX(obj)) {
map.put("X", obj);
else if (matchesCriteriaY(obj)) {
map.put("Y", obj);
}
}
Well, if you do that and try to do map.get("X"), but you never actually put anything for that key (becaus no object matched criteria X), you will most definitely get back a null.
On the other hand, if you did something like
Map<String, List<Object>> map = new HashMap<String, List<Object>>();
map.add("X", new ArrayList<Object>());
map.add("Y", new ArrayList<Object>());
for (Object obj : list) {
if (matchesCriteriaX(obj)) {
List<Object> list = map.get("X");
list.add(obj);
else if (matchesCriteriaY(obj)) {
List<Object> list = map.get("Y");
list.add(obj);
}
}
then you could check if a category is empty by doing map.get("x").isEmpty() since List has that method (and it would be empty if no object matched the key criteria).
Judging from what you said, I'm suspecting something like this:
Map<SomeKey, List<SomeValue>> yourMap;
If this is the case, what you can do is
if( yourMap.contains(someKey) ){
List<SomeValue> someList = yourMap.get(someKey);
if(someList.size() == 0){
// it's empty, do something?
}
}

Categories