What are the java collections accepts adding null values? - java

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.

Related

Cannot unbox null value

I have the following code:
List<Details> detailsList = new ArrayList<>();
List<String[]> csv = csvReader.readAll();
final Map<String, Integer> mappedHeaders = mapHeaders(csv.get(0));
List<String[]> data = csv.subList(1, csv.size());
for (String[] entry : data) {
Details details = new Details(
entry[mappedHeaders.get("A")],
entry[mappedHeaders.get("B")],
entry[mappedHeaders.get("C")]);
detailsList.add(details);
I'm essentially reading in a CSV file as a list of string arrays where the first list item is the CSV file headers and all remaining elements correspond to the data rows. However, since different CSV files of the same features might have different feature column ordering I don't know the ordering in advance. For that, I have a mapHeaders method which maps the headers to indices so I can later properly put together the Details object (for example, if headers are ["B", "A", "C"], the mappedHeaders would correspond to {B: 0; A: 1; C: 2}.
I also have some test data files of different column orderings and all but one of them work as they should. However, the one that doesn't work gives me
java.lang.NullPointerException: cannot unbox null value
when trying to evaluate entry[mappedHeaders.get("A")]. Additionally, when running the code in debugging mode, the mappedHeaders contains the correct keys and values and the value for "A" isn't null.
I have also tried entry[mappedHeaders.getOrDefault("A", Arrays.asList(csv.get(0)).indexOf("A"))] which returns -1. The only thing that works is entry[mappedHeaders.getOrDefault("A", 0)] since A is the first column in the failing case, but that workaround don't seem very feasible as there might be more failing cases that I don't know about, but where the ordering is different. What might be the reason for such behavior? Might it be some weird encoding issue?
That's because you are trying to unbox a null value.
A method like intValue, longValue() or doubleValue() is being called on a null object.
Integer val = null;
if (val == 1) {
// NullPointerException
}
Integer val = null;
if (val == null) {
// This works
}
Integer val = 0;
if (val == 1) {
// This works
}

Iterate efficiently through 2 different List with same Type of Object(Java8)

I have two list containing an important number of object with each N elements:
List<Foo> objectsFromDB = {{MailId=100, Status=""}, {{MailId=200, Status=""}, {MailId=300, Status=""} ... {MailId=N , Status= N}}
List <Foo> feedBackStatusFromCsvFiles = {{MailId=100, Status= "OPENED"}, {{MailId=200, Status="CLICKED"}, {MailId=300, Status="HARDBOUNCED"} ... {MailId=N , Status= N}}
Little Insights:
objectFromDB retrieves row of my database by calling a Hibernate method.
feedBackStatusFromCsvFiles calls a CSVparser method and unmarshall to Java objects.
My entity class Foo has all setters and getters. So I know that the basic idea is to use a foreach like this:
for (Foo fooDB : objectsFromDB) {
for(Foo fooStatus: feedBackStatusFromCsvFiles){
if(fooDB.getMailId().equals(fooStatus.getMailId())){
fooDB.setStatus(fooStatus.getStatus());
}
}
}
As far as my modest knowledge of junior developer is, I think it is a very bad practice doing it like this? Should I implement a Comparator and use it for iterating on my list of objects? Should I also check for null cases?
Thanks to all of you for your answers!
Assuming Java 8 and considering the fact that feedbackStatus may contain more than one element with the same ID.
Transform the list into a Map using ID as key and having a list of elements.
Iterate the list and use the Map to find all messages.
The code would be:
final Map<String, List<Foo>> listMap =
objectsFromDB.stream().collect(
Collectors.groupingBy(item -> item.getMailId())
);
for (final Foo feedBackStatus : feedBackStatusFromCsvFiles) {
listMap.getOrDefault(feedBackStatus.getMailId(), Colleactions.emptyList()).forEach(item -> item.setStatus(feedBackStatus.getStatus()));
}
Use maps from collections to avoid the nested loops.
List<Foo> aList = new ArrayList<>();
List<Foo> bList = new ArrayList<>();
for(int i = 0;i<5;i++){
Foo foo = new Foo();
foo.setId((long) i);
foo.setValue("FooA"+String.valueOf(i));
aList.add(foo);
foo = new Foo();
foo.setId((long) i);
foo.setValue("FooB"+String.valueOf(i));
bList.add(foo);
}
final Map<Long,Foo> bMap = bList.stream().collect(Collectors.toMap(Foo::getId, Function.identity()));
aList.stream().forEach(it->{
Foo bFoo = bMap.get(it.getId());
if( bFoo != null){
it.setValue(bFoo.getValue());
}
});
The only other solution would be to have the DTO layer return a map of the MailId->Foo object, as you could then use the CVS list to stream, and simply look up the DB Foo object. Otherwise, the expense of sorting or iterating over both of the lists is not worth the trade-offs in performance time. The previous statement holds true until it definitively causes a memory constraint on the platform, until then let the garbage collector do its job, and you do yours as easy as possible.
Given that your lists may contain tens of thousands of elements, you should be concerned that you simple nested-loop approach will be too slow. It will certainly perform a lot more comparisons than it needs to do.
If memory is comparatively abundant, then the fastest suitable approach would probably be to form a Map from mailId to (list of) corresponding Foo from one of your lists, somewhat as #MichaelH suggested, and to use that to match mailIds. If mailId values are not certain to be unique in one or both lists, however, then you'll need something a bit different than Michael's specific approach. Even if mailIds are sure to be unique within both lists, it will be a bit more efficient to form only one map.
For the most general case, you might do something like this:
// The initial capacity is set (more than) large enough to avoid any rehashing
Map<Long, List<Foo>> dbMap = new HashMap<>(3 * objectFromDb.size() / 2);
// Populate the map
// This could be done more effciently if the objects were ordered by mailId,
// which perhaps the DB could be enlisted to ensure.
for (Foo foo : objectsFromDb) {
Long mailId = foo.getMailId();
List<Foo> foos = dbMap.get(mailId);
if (foos == null) {
foos = new ArrayList<>();
dbMap.put(mailId, foos);
}
foos.add(foo);
}
// Use the map
for (Foo fooStatus: feedBackStatusFromCsvFiles) {
List<Foo> dbFoos = dbMap.get(fooStatus.getMailId());
if (dbFoos != null) {
String status = fooStatus.getStatus();
// Iterate over only the Foos that we already know have matching Ids
for (Foo fooDB : dbFoos) {
fooDB.setStatus(status);
}
}
}
On the other hand, if you are space-constrained, so that creating the map is not viable, yet it is acceptable to reorder your two lists, then you should still get a performance improvement by sorting both lists first. Presumably you would use Collections.sort() with an appropriate Comparator for this purpose. Then you would obtain an Iterator over each list, and use them to iterate cooperatively over the two lists. I present no code, but it would be reminiscent of the merge step of a merge sort (but the two lists are not actually merged; you only copy status information from one to the other). But this makes sense only if the mailIds from feedBackStatusFromCsvFiles are all distinct, for otherwise the expected result of the whole task is not well determined.
your problem is merging Foo's last status into Database objects.so you can do it in two steps that will make it more clearly & readable.
filtering Foos that need to merge.
merging Foos with last status.
//because the status always the last,so you needn't use groupingBy methods to create a complex Map.
Map<String, String> lastStatus = feedBackStatusFromCsvFiles.stream()
.collect(toMap(Foo::getMailId, Foo::getStatus
, (previous, current) -> current));
//find out Foos in Database that need to merge
Predicate<Foo> fooThatNeedMerge = it -> lastStatus.containsKey(it.getMailId());
//merge Foo's last status from cvs.
Consumer<Foo> mergingFoo = it -> it.setStatus(lastStatus.get(it.getMailId()));
objectsFromDB.stream().filter(fooThatNeedMerge).forEach(mergingFoo);

Scala: Check for null for List

I am new to Scala after coding for 10 years in Java. Still getting hold of functional programming.
How can I check if the list is null or not?
Code looks something like this:
val filterList = filters.map { filter =>
//some operations
}
//Other function
filterList.foldLeft(true)((result1, result2) => {
Now if filters is null then filterList is going to be null too.
If filters is null (which is different from being empty) then that indicates some pretty careless programing, but it can be handled.
val filterList = Option(filters).map(_.map { ...
Now filterList is of type Option[X] where X is the collection type for filters. Note the 1st map is to unwrap the Option and the 2nd map maps over the collection, except if filters was null, then the 2nd map is never invoked and the whole result is None.
val filterList = if(filters == null) Seq.empty[SomeType] else filters.map {...}
However, you should try to make sure it never gets to be null, because we try to avoid null variables in Scala. Use the Option[T] type, or empty collections instead

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