3

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

1
  • How did you manage to add the items to the TreeSet in the first place? Commented Dec 21, 2015 at 0:20

1 Answer 1

3

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]))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.