Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EnumSet.spliterator without characteristic Spliterator.NONNULL

I was thinking about an answer to the question: How to test for null keys on any Java map implementation?

My first thought was to check if the Spliterator of the keyset of a map has the characteristic Spliterator.NONNULL:

map.keySet().spliterator().hasCharacteristics(Spliterator.NONNULL)

The JavaDoc says:

Characteristic value signifying that the source guarantees that encountered elements will not be null. (This applies, for example, to most concurrent collections, queues, and maps.)

Before answering I did some checks:

The Spliterator of aTreeMap without a provided Compararator does not have this characteristic even though the natural ordering does not allow null-keys.

new TreeMap<>().keySet().spliterator().hasCharacteristics(Spliterator.NONNULL); // false 

Even more suprising was the fact that Spliterators of an EnumMap keyset and of EnumSet itself do not have this characteristic.

EnumSet.allOf(DayOfWeek.class).spliterator().hasCharacteristics(Spliterator.NONNULL); // false

I understand that the result of spliterator().hasCharacteristics(Spliterator.NONNULL) in the above cases returns false as the default implementation of Set.spliterator() is evaluated.

But is there a reason why the spliterators of these sets do not override Set.spliterator() to create a Spliterator with Spliterator.NONNULL? Would this break a specification I am not aware of?

like image 544
LuCio Avatar asked Mar 05 '23 11:03

LuCio


2 Answers

Even worse:

System.out.println(Set.of(1)
             .spliterator()
             .hasCharacteristics(Spliterator.NONNULL)); // false

Even if those Set::of methods are documented as:

throws NullPointerException if the element is null

So there is no way to end up with a null in that Set. I guess the real answer is that this is not yet done.

EDIT

See the comment from Stuart Marks about this

like image 79
Eugene Avatar answered Apr 07 '23 13:04

Eugene


But is there a reason why the spliterators of these sets do not override Set.spliterator() to create a Spliterator with Spliterator.NONNULL?

We can only speculate, but certainly it is the case that some TreeMap instances that use Comparators for ordering do accommodate null keys, and therefore their key sets' spliterators must not have characteristic Spliterator.NONNULL. Although it is true that a TreeMap that uses its keys' natural ordering cannot accommodate null keys, I do not personally find it surprising that TreeMap's key sets do not use that to make a distinction. This is the kind of property that I would expect to be driven exclusively by the classes involved, not by per-instance details.

Would this break a specification I am not aware of?

Perhaps. The docs for Set.spliterator() specify

The Spliterator reports Spliterator.DISTINCT. Implementations should document the reporting of additional characteristic values.

(Emphasis added.) The docs for TreeMap.keySet() say

The set's spliterator is late-binding, fail-fast, and additionally reports Spliterator.SORTED and Spliterator.ORDERED with an encounter order that is ascending key order. The spliterator's comparator (see Spliterator.getComparator()) is null if the tree map's comparator (see SortedMap.comparator()) is null. Otherwise, the spliterator's comparator is the same as or imposes the same total ordering as the tree map's comparator.

Note well that the docs for this set comply with the expectation set in the Set.spliterator() docs without designating, even conditionally, that Spliterator.NONNULL is among the characteristics that key sets' spliterators will report. Note further that those same docs do describe other characteristics of those sets that vary depending on whether the map's order is based on a comparator.

Thus, no, you should not expect TreeMap key sets', spliterators to have report Spliterator.NONNULL under any circumstances. I cannot definitively speak to why this choice was made, but it is consistent with my perception of Java design philosophy.

You also wrote,

Even more suprising was the fact that Spliterators of an EnumMap keyset and of EnumSet itself do not have this characteristic.

I agree that these spliterators could reasonably report Spliterator.NONNULL. I do not know why the choice was made that they would not do so, unless it was simply an oversight. Nevertheless, I observe that their docs indeed do not specify that these spliterators will report Spliterator.NONNULL. With that being the case, it is to be expected that those spliterators will not report that characteristic.

like image 21
John Bollinger Avatar answered Apr 07 '23 13:04

John Bollinger