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.
Related
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
I know Set and Map accepts null values and today I just found out LinkedList also accepts null values like
map.put(null, 1);
set.add(null);
linkedList.add(null)
Is there any other collections that allow null values to be stored?. Just posting to get a comprehensive list in one place and reason for each for them.
Set and Map are interfaces. They have several implementations in Java.
Popular implementations of the Map are:
HashMap - accepts one null key
Hashtable - doesn't accept any null key
TreeMap - doesn't accept any null key
LinkedHashMap - accepts one null key
Any number of null values can be added as value in any of above implementations
Popular implementations of the Set are:
HashSet - accepts one null element
TreeSet - doesn't accept any null element
LinkedHashSet - accepts one null element
Any implementations of List, like ArrayList or LinkedList can accept nulls.
This problem can be resolved by using placeholder class Optional from JDK.
The principle is to package a nullable value within an wrapper instance using Optional class that will carry values (null values also).
It can be annoying and little heavy in the code but surely can be suitable in many cases.
Here is an example with a non-null value :
Optional<Integer> valuedInt = Optional.ofNullable(123);
assertTrue(valuedInt.isPresent());
assertEquals(Integer.valueOf(123), valuedInt.get());
Here is an example with a null value :
Optional<Integer> nullInt = Optional.ofNullable(null);
assertTrue(nullInt.isEmpty());
try {
var val = nullInt.get();
} catch (Exception e) {
// Invocation of nullInt.get() throws an exception.
assertTrue(e instanceof NoSuchElementException);
}
// However, the method Optional.orElse(T) can be used as
// a getter that supplies the value wether it is valued or null.
assertEquals(Integer.valueOf(123), valuedInt.orElse(null));
assertEquals(null, nullInt.orElse(null));
We can initialize a list as so :
// Our hacked list
List<Optional<Integer>> integersOpt;
First way to populate the list :
integersOpt = new ArrayList<>();
integersOpt.add(Optional.ofNullable(1));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(2));
integersOpt.add(Optional.ofNullable(null));
integersOpt.add(Optional.ofNullable(3));
Second way to populate the list (unmodifiable) :
integersOpt =
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList());
Third way to populate the list (modifiable) :
integersOpt =
new ArrayList<>(
Arrays.asList(1, null, 2, null, 3).stream()
.map(x -> Optional.ofNullable(x))
.collect(Collectors.toList())
);
Count and print values :
int countNulls = 0, countNonNulls = 0;
for(Optional<Integer> opt : integersOpt) {
Integer optVal = opt.orElse(null); // Our famous "getter" function.
if(optVal == null) {countNulls++;}
else {countNonNulls++;}
System.out.println("Original value = " + optVal);
}
assertEquals(2, countNulls);
assertEquals(3, countNonNulls);
integersOpt.toString() will return this value :
[Optional[1], Optional.empty, Optional[2], Optional.empty, Optional[3]]
Accepting nulls was a design mistake, as time has proven. First of all this complicates implementations, because at some point in time you might need to be able to compare null to null, for example; and this sometimes require special handling (or special branches in the code at least).
Since java-9, immutable collections all reject null and even document it properly. One minor example (but there are many more) is Set:of, that says:
#throws NullPointerException if the element is null
Not only that, but they also don't allow null checks, so you might be surprised by this a bit:
Set<Object> oldS = new HashSet<>();
oldS.add(new Object());
System.out.println(oldS.contains(null)); // false
Set<Object> newS = Set.of(new Object());
System.out.println(newS.contains(null)); // NPE
Every new collection or implementation that comes to JDK prohibits null.
I have two string value ID and Value coming from my code, I need to store this in a map without iterating over the map but by checking by the key value if it exists in the map.
The ID can come many times with different value and in that case I have add it to the already existing entry of the ID and add the value along with the existing value
I created a Map with String and List to add the values but I am facing difficulties,
Map < String, List< String >> accessMap = new HashMap < String, List< String>>();
If the key is not present, add a new entry with ID and Value (as List).
How to find the key in the map and get the entrySet without iterating over the map and add the value alone, if the ID is already present in the map.
Example,
while(accessList.hasNext()){
....
....
String id = accessEntry.get(ID);
String value = accessEntry.get(Value);
/*Add the id and value to the map if not present, if ID is present add the *value alone to the entrySet.Value (which is a list)
*/
}
The id and value has to be added into a map checking if the ID is already present in the map, if not create a new entry.
The accessList might have many ID references with different value, in that case the value should be added to the entrySet of the already existing entry, the value would be a list with single value or multiple value.
public static void add(Map<String, Set<String>> map, String key, String value) {
map.compute(key, (id, values) -> {
(values = Optional.ofNullable(values).orElseGet(LinkedHashSet::new)).add(value);
return values;
});
}
For values it is better to use Set to exclude duplications.
Use computeIfAbsent() to initialise a List for the id if one doesn't exist already, then just add the value to that:
entitlementMap.computeIfAbsent(id, s -> new ArrayList<>()).add(value);
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.
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?
}
}