Class implementing Comparator produces IllegalStateException when probing spliterator().getComparator

192 Views Asked by At

I have following class

class A implements Comparable<A> {

    private String name;

    public A(String name) {
        this.name = name;
    }

    public String getName() {
       return name;
    }

    @Override
    public int compareTo(A o) {
        return o.getName().compareTo(this.name);
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        A a = (A) o;
        return name.equals(a.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

Now my understanding is that following code should produce some class name or null but not IllegalStateException as stated in the java document which says

If this Spliterator's source is SORTED by a Comparator, returns that Comparator. If the source is SORTED in natural order, returns null. Otherwise, if the source is not SORTED, throws IllegalStateException.

List<A> arrayList = new ArrayList<>();
arrayList.add(new A("Y"));
arrayList.add(new A("G"));
arrayList.add(new A("J"));
arrayList.add(new A("A"));
arrayList.add(new A("Z"));

arrayList.sort(A::compareTo);
Comparator<? super A> comparator = arrayList.spliterator().getComparator();
System.out.println(comparator);

EDIT 2

I believe i am not able to make you understand what i am looking for. Take this as example:

SortedSet<String> set = new TreeSet<>( Collections.reverseOrder() );
set.add("A");
set.add("D");
set.add("C");
set.add("B");
System.out.println(set);
System.out.println(set.spliterator().getComparator());

This outputs as

[D, C, B, A]
java.util.Collections$ReverseComparator@7852e922

Now Collections.reverseOrder() is just an implementation of Comparator

Since this is producing the right answer, my expectation from my code is also it should output a class name like above.

So what is that I am not doing right ?

1

There are 1 best solutions below

0
On

While the characteristics of a Spliterator may reflect the current contents of a collection, they are usually depending on the type of the source collection only. So all standard List implementations never report the SORTED characteristic, even when their elements happen to be currently sorted, whereas all SortedSet implementations always report the SORTED characteristic.

You may rarely need to use this method on your own. Data processing APIs, like the Stream API may use the characteristics behind the scenes, to optimize the execution. For example, the sorted operation of a Stream may get skipped when it detects that the data is already sorted. But to name a less obvious example, distinct may work differently when the data is sorted by the element type’s natural order.

Further, the state of a stream pipeline may serve as an example of a case, where the characteristics are not determined by the type:

public static void main(String[] args) {
    check(Stream.of("foo", "bar").filter(x -> true));
    check(Stream.of("foo", "bar").sorted().filter(x -> true));
}
private static void check(Stream<?> s) {
    System.out.println("Type: "+s.getClass());
    System.out.println("sorted: "+s.spliterator().hasCharacteristics(Spliterator.SORTED));
    System.out.println();
}
Type: class java.util.stream.ReferencePipeline$2
sorted: false

Type: class java.util.stream.ReferencePipeline$2
sorted: true

Normally, you wouldn’t use this API to get the comparator of a collection you created yourself, as you already know the comparator. But there are cases where a spliterator has a comparator not originating from your code (directly):

TreeMap<String, Integer> map = new TreeMap<>(Comparator.comparingInt(String::length));

Spliterator<Map.Entry<String, Integer>> sp = map.entrySet().spliterator();
if(sp.hasCharacteristics(Spliterator.SORTED)) {
    Comparator<? super Map.Entry<String, Integer>> comparator = sp.getComparator();
    System.out.println("Entry comparator: " + comparator);
    Map.Entry<String, Integer> e1 = new AbstractMap.SimpleEntry<>("some", 5);
    Map.Entry<String, Integer> e2 = new AbstractMap.SimpleEntry<>("string", 3);
    System.out.println(BinaryOperator.maxBy(comparator).apply(e1, e2));
}