Search code examples
javacollectionsimmutability

Is it safe to assume "java.util.ImmutableCollections" are always ordered?


java.util.ImmutableCollections provides implementations for immutable Collections, as generated by static methods like List.of(), Set.of(), Map.of(); it's array-based internally. Is it safe to assume that Collections produced by said methods are always ordered?

If I have e. g. a method argument T t of Class type java.util.ImmutableCollections.MapN, can I (since it's a hidden internal Class) rely on

public static <T> boolean isOrderedMap(final T t) {
    final boolean isOrderedMap = "java.util.ImmutableCollections.MapN".equals(t.getClass().getCanonicalName()) 
       || t instanceof LinkedHashMap 
       || t instanceof SortedMap
       || t instanceof EnumMap
       || t instanceof Attributes
    return isOrderedMap ? true : false;
}

and assume that the result is indeed always true for such standard Maps, if they're supposed to be ordered (and similar for Sets and Lists)?


Solution

  • It seems I've been looking in the wrong place of the documentation - in the method's documentations instead of the Interface specifications, where the immutable variants are explicitly described - and indeed the ImmutableCollections' static methods mentioned above generate Sets and Maps without explicit ordering, which can be verified, e. g.:

      final Map<String, String> map1 = Map.of("","", " ", " ");
      final boolean isOrderedM1a = map1.keySet().spliterator().hasCharacteristics(ORDERED);
      final boolean isOrderedM1b = map1.values().spliterator().hasCharacteristics(ORDERED);
      final boolean isOrderedM1c = map1.entrySet().spliterator().hasCharacteristics(ORDERED);
      final Map map2 = Map.ofEntries(new SimpleEntry("",""), new SimpleEntry(" ", " "));
      final boolean isOrderedM2a = map2.keySet().spliterator().hasCharacteristics(ORDERED);
      final boolean isOrderedM2b = map2.values().spliterator().hasCharacteristics(ORDERED);
      final boolean isOrderedM2c = map2.entrySet().spliterator().hasCharacteristics(ORDERED);
      final Set set1 = Set.of(""," ");
      final boolean isOrderedS1 = set1.spliterator().hasCharacteristics(ORDERED);
      final Set set2 = Set.of("");
      final boolean isOrderedS2 = set2.spliterator().hasCharacteristics(ORDERED);
    

    The results all are false, as to be expected. In contrast, immutable Collections generated from List.of() are always ordered, which wasn't questioned here, though. Thanks for the clarification.

    Initially, I've spotted a Spliterator result of true in the code somewhere while looking for an answer, which made me believe that these static methods might produce ordered results in general. Perhaps that was due to mixing up wrapped results of Collections.unmodifiableSet() and similar generated test data from already ordered Collections.

    EDIT:

    While working on the problem, I came across a solution how to create a Spliterator from an Iterator, by converting it into an Iterable via Lambda first and get the ORDERED characteristic from that like so:

    Iterator iter = new ArrayList().iterator();
    boolean isOrdered = ((Iterable)() -> ((Iterator)t1)).spliterator().hasCharacteristics(ORDERED);
    

    Unfortunately, this won't preserve the ORDERED nature from the original ArrayItr and isOrdered will be false.

    I'd have expected it to give the same result (true) as

    boolean isOrdered = new ArrayList().spliterator().hasCharacteristics(ORDERED);
    

    but it doesn't. Why is that and how to prevent losing the ordering in the first case - and is creating a spliterator 'expensive'?

    EDIT: From what I see in the Spliterator doc, the reason should be:

    Impl Note: The default implementation should usually be overridden. The spliterator returned by the default implementation [...] does not report any spliterator characteristics.

    So then the question would boil down to: "Is it possible to create a Spliterator with preserved characteristics from the first case, whithout a custom overridden implementation?".