pink_panda
pink_panda

Reputation: 100

ClassCastException when using a set of pairs

I would like to use a set of pairs in Java, but when I call contains() to see if it already contains a specific pair then I always get a ClassCastException. Is there a way to avoid this kind of behaviour?

It is instantiated like this:

private static final Set<Pair<String, String>> BLACKLIST = new TreeSet<>();

BLACKLIST.add(new Pair<String, String>("anytext", "anytext"));

Calling contains() here leads to following ClassCastException:

if (!blacklist.contains(new Pair<String, String>(localizedFile.getName(), key)))

java.lang.ClassCastException: .common.util.Pair cannot be cast to java.lang.Comparable
        at java.util.TreeMap.compare(TreeMap.java:1294)
        at java.util.TreeMap.put(TreeMap.java:538)
        at java.util.TreeSet.add(TreeSet.java:255)

I use java.util classes instead of my own. Is there an elegant way to avoid to implement a new class pair which overloads common.util.Pair?

Upvotes: 0

Views: 336

Answers (2)

Debapriya Biswas
Debapriya Biswas

Reputation: 1339

TreeSet uses the natuarl ordering of its elements unless you passed any explicit comparator to the constructor while creating the Set .

The contract says that all elements inserted into the set must implement the Comparable interface.Furthermore, all such elements must be mutually comparable e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the set.

You code is

  private static final Set<Pair<String, String>> BLACKLIST = new TreeSet<>();
BLACKLIST.add(new Pair<String, String>("anytext", "anytext"));

Since your have not passed a explicit Comparator to the constructor and neither your elements (Pair) implements a good Comparable you are getting a class cast exception while adding a element to the set.

My suggestion is (i) pass a explicit comparator

or(ii)make Pair implements Comparable as below

public class Pair<T1 extends Comparable<T1>, T2 extends Comparable<T2>> implements Comparable<Pair<T1, T2>> {
    T1 firstName;
    T2 secondName;

    public Pair(T1 firstName, T2 secondName) {
        this.firstName = firstName;
        this.secondName = secondName;
    }

    public T1 getFirstName() {
        return firstName;
    }

    public T2 getSecondName() {
        return secondName;
    }

    // elements ordered with first parameter .you can make a better
    // comparable as well
    @Override
    public int compareTo(Pair<T1, T2> o) {
        // TODO Auto-generated method stub
        return this.firstName.compareTo(o.firstName);
    }

}

Upvotes: 1

daniu
daniu

Reputation: 14999

TreeSet uses the natural ordering of its elements, and it seems to cast to Comparable to do that.

What you can do to make this code work is to give the TreeSet your own Comparator so it can use that instead of natural ordering.

Set<Pair<String, String>> blacklist = new TreeSet<>(
    Comparator.comparing(Pair::getFirst)
              .thenComparing(Pair::getSecond));

which will order by first, then second item of the Pair.

Upvotes: 0

Related Questions