Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ClassCastException in Lambda stream

Tags:

java

lambda

I have been playing a little with lambda statements in Java 8, and am stuck: I don't know why this isn't working.

I want the lambda statement to return the array where the sum of the first element, multiplied with the second element, is the highest, in this case {2,3}.

Any help is appreciated.

public static void main(String[] args)
{
    //Test data
    final TreeSet<Integer[]> test = new TreeSet<Integer[]>();
    test.add( new Integer[]{1,2} );
    test.add( new Integer[]{1,3} );
    test.add( new Integer[]{1,4} );
    test.add( new Integer[]{2,1} );
    test.add( new Integer[]{2,2} );
    test.add( new Integer[]{2,3} );

    Integer[] test1 = test.stream()
        .min((Integer[] l , Integer[] r) -> { return Integer.compare( l[0] * l[1] , r[0] * r[1] ); })
        .get(); //Run-time error: java.lang.ClassCastException: [Ljava.lang.Integer; cannot be cast to java.lang.Comparable

    Integer[] test2 = test.stream()
        .min((Integer[] l , Integer[] r) -> { return Integer.valueOf( l[0] * l[1] ).compareTo( r[0] * r[1] ); })
        .get(); //Run-time error: java.lang.ClassCastException: [Ljava.lang.Integer; cannot be cast to java.lang.Comparable
}

Thanks,

-PStiger

like image 561
PStigerID Avatar asked Aug 07 '26 18:08

PStigerID


1 Answers

The problem is when you put the elements in the tree set. If you don't provide explicitly a comparator, it assumes that the elements it contains have a natural order (so they implement the comparable interface). This is not the case for arrays.

This is what the documentation states about the no-args constructor you are using:

Constructs a new, empty tree set, sorted according to the natural ordering of its elements. All elements inserted into the set must implement the Comparable interface.

The stacktrace clearly indicates that the exception is thrown at the line where you add the first array instance (and not when the code in the stream pipeline is executed because you never get to this point).

You can even browse the source. A TreeSet<E> is using a TreeMap<E, Object> internally to store the elements. When you call add, it calls m.put(e, PRESENT).

public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

Since there is no elements (the root is null), it calls compare(key, key) (where key is the integer array). This method compare is a utility method:

final int compare(Object k1, Object k2) {
    return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
            : comparator.compare((K)k1, (K)k2);
}

As you didn't provide a comparator, it tries to cast the key as a comparable, hence the ClassCastException.

To fix that you need to provide a comparator. Here's one that compares the arrays by their first element:

final TreeSet<Integer[]> test = new TreeSet<>(Comparator.comparing(a -> a[0]));


Concerning the stream pipeline, note that the compiler is usually able to infer the correct types for the parameters of the corresponding lambda expression. So you could rewrite your comparator as :
.min((l , r) -> Integer.compare( l[0] * l[1] , r[0] * r[1]))

or even better:

.min(Comparator.comparing(a -> a[0] * a[1]))
like image 104
Alexis C. Avatar answered Aug 09 '26 11:08

Alexis C.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!