6

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.

Ijaz
  • 421
  • 1
  • 6
  • 23
  • 2
    "I know Set and Map accepts null values" -- not necessarily; implementations of these interfaces are free to reject null values. – dnault Jul 09 '20 at 17:59
  • @dnault there are example [since java-9](https://stackoverflow.com/a/62826234/1059372) – Eugene Jul 10 '20 at 01:56

3 Answers3

9

Set and Map are interfaces. They have several implementations in Java.

Popular implementations of the Map are:

  1. HashMap - accepts one null key
  2. Hashtable - doesn't accept any null key
  3. TreeMap - doesn't accept any null key
  4. 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:

  1. HashSet - accepts one null element
  2. TreeSet - doesn't accept any null element
  3. LinkedHashSet - accepts one null element

Any implementations of List, like ArrayList or LinkedList can accept nulls.

Giorgi Tsiklauri
  • 9,715
  • 8
  • 45
  • 66
  • 3
    Without a doubt, it's true the popular List implementations accept null values. However, the [documentation for List.add()](https://docs.oracle.com/javase/8/docs/api/java/util/List.html#add-E-) say, "Lists that support this operation may place limitations on what elements may be added to this list. In particular, some lists will refuse to add null elements...". It goes on to say, "List classes should clearly specify in their documentation any restrictions on what elements may be added." So, when in doubt, consult the Javadoc. – dnault Jul 09 '20 at 22:27
  • See also: https://stackoverflow.com/questions/6433478/is-there-a-standard-java-list-implementation-that-doesnt-allow-adding-null-to-i – dnault Jul 09 '20 at 22:31
  • Note that it's "Hashtable" with a lowercase T in Java. – esotericpig May 10 '21 at 10:10
  • How to remember it in logical way rather than literally letters one by one – http8086 Aug 29 '21 at 15:14
3

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]]
2

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.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Well, I don't completely agree here. Yes, `null`s are rarely useful in any collection; however, I've seen several times they were used deliberately for implementing different algorithms. – Giorgi Tsiklauri Jul 10 '20 at 06:50
  • @GiorgiTsiklauri the fact that `HashMap` allows a `null` key is still exploited by different algorithms. Or the fact that it had some traversal order until java-8 was used by many people too, nevertheless both were admitted as mistakes by `HasMap` creators. – Eugene Jul 10 '20 at 10:26
  • Can you provide any reference where `HashMap` creators say that? – Giorgi Tsiklauri Jul 10 '20 at 11:58
  • 2
    @GiorgiTsiklauri [here you go](https://www.youtube.com/watch?v=OJrIMv4dAek&t=31m9s) : _generally regarded as a mistake_. You do know who Stuart Marks is, right? This is (1) and (2) is the fact that when you admit a mistake you do not it again (that is why immutable collections prohibit it) – Eugene Jul 10 '20 at 13:22