0

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?

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
pink_panda
  • 60
  • 6
  • 1
    does Pair implement Comparable? – Stultuske Jun 15 '18 at 11:06
  • 1
    Can you share your `Pair` class? – Mureinik Jun 15 '18 at 11:08
  • Possible duplicate of [MyClass cannot be cast to java.lang.Comparable: java.lang.ClassCastException](https://stackoverflow.com/questions/19716790/myclass-cannot-be-cast-to-java-lang-comparable-java-lang-classcastexception) – AxelH Jun 15 '18 at 11:14
  • The duplicate don't use a `Set` but the reason is the same and the solution too. – AxelH Jun 15 '18 at 11:14
  • Duplicate: https://stackoverflow.com/questions/13890542/creating-a-treeset-with-a-non-comparable-class-why-a-run-time-exception-rather – Pavel Jun 15 '18 at 11:38

2 Answers2

1

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);
    }

}
Debapriya Biswas
  • 907
  • 7
  • 18
0

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.

daniu
  • 12,131
  • 3
  • 23
  • 46