Search code examples
javacomparatorcomparabletreeset

Java Comparator, Comparable and TreeSet.contains


edit - slightly simplified example below (any simpler and i'm not sure it contains all of the elements of the desired behavior)

Below is a code snippet that represents one thing I'm trying to do with Comparator. I would like for contains to return true once the second time that doRSM is called:

package comparisonTest;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.SortedSet;
import java.util.TreeSet;

public class ComparisonTest {

    private static class ArbitraryItem {
        String node;
        ArbitraryItem(String node) {
            this.node = node;
        }
        public String getNode() {
            return node;
        }
    }

    private static final Map<String, ResultSet> idToArbitraryItems = new HashMap<>();


    private static class ArbitraryItemComparable implements Comparable<ArbitraryItemComparable> {
        ArbitraryItem item;
        Comparator<ArbitraryItemComparable> c;
        ArbitraryItemComparable(ArbitraryItem item, Comparator<ArbitraryItemComparable> c) {
            this.item = item;
            this.c = c;
        }
        @Override
        public boolean equals(Object o) {
            if (!(o instanceof ArbitraryItemComparable)) {
                System.out.println("not a ArbitraryItemComparable="+o);
                return false;
            }
            boolean eq = this.c.compare(this, ((ArbitraryItemComparable)o)) == 0;
            System.out.println("   equality val="+eq);
            return eq;
        }
        @Override
        public int compareTo(ArbitraryItemComparable o) {
            int compVal = this.c.compare(this, ((ArbitraryItemComparable)o));
            System.out.println("   comparison val="+compVal);
            return compVal;
        }

    }

    private static class ResultSet {
        SortedSet<ArbitraryItemComparable> usedResults;
        String node;
        Comparator<ArbitraryItemComparable> comparator;
        ResultSet(String node, Comparator<ArbitraryItemComparable> comparator) {
            this.usedResults = new TreeSet<ArbitraryItemComparable>();
            this.node = node;
            this.comparator = comparator;
        }
    }

    static private void doRSM(Collection <ArbitraryItem> foobar, ResultSet set, int max, boolean close, String id) {
        Iterator<ArbitraryItem> items = foobar.iterator();
        for (;items.hasNext();) {
            ArbitraryItem item = (ArbitraryItem) items.next();
            ArbitraryItemComparable itemComparable = new ArbitraryItemComparable(item, set.comparator);
            System.out.println("*** looking at node "+itemComparable.item.getNode()+"***, sur size="+set.usedResults.size());
            if (!set.usedResults.contains(itemComparable)); {
                System.out.println("*** node "+itemComparable.item.getNode()+" not in usedResults");
            }
            set.usedResults.add(itemComparable);
        }
    }

    public static void main(String [] args)
    {
        Collection<ArbitraryItem> items = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            items.add(new ArbitraryItem(""+i));
        }

        Comparator<ArbitraryItemComparable> comparator = new Comparator<ArbitraryItemComparable>() {
            @Override
            public int compare(ArbitraryItemComparable o1, ArbitraryItemComparable o2) {
                // this is where the magic needs to happen!!
                System.out.println("calling compare: o1 node="+o1.item.getNode()+" "+o1.item.getNode().hashCode()+" o2 node="+o2.item.getNode()+" "+o2.item.getNode().hashCode());
                return o1.item.getNode().hashCode() - o2.item.getNode().hashCode();
            }
            @Override
            public boolean equals(Object o) {
                System.out.println("why is this called?");
                return false;
            }
        };
        ResultSet set = new ResultSet("3", comparator);
        idToArbitraryItems.put("q", set);
        doRSM(items, set, 1000, false, "q");
        doRSM(items, set, 1000, false, "q");
    }
}

However, the log shows this:

*** looking at node 0***, sur size=0
*** node 0 not in usedResults
calling compare: o1 node=0 48 o2 node=0 48
   comparison val=0
*** looking at node 1***, sur size=1
calling compare: o1 node=1 49 o2 node=0 48
   comparison val=1
*** node 1 not in usedResults
calling compare: o1 node=1 49 o2 node=0 48
   comparison val=1
*** looking at node 2***, sur size=2
calling compare: o1 node=2 50 o2 node=0 48
   comparison val=2
calling compare: o1 node=2 50 o2 node=1 49
   comparison val=1
*** node 2 not in usedResults
calling compare: o1 node=2 50 o2 node=0 48
   comparison val=2
calling compare: o1 node=2 50 o2 node=1 49
   comparison val=1
*** looking at node 0***, sur size=3
calling compare: o1 node=0 48 o2 node=1 49
   comparison val=-1
calling compare: o1 node=0 48 o2 node=0 48
   comparison val=0
*** node 0 not in usedResults
calling compare: o1 node=0 48 o2 node=1 49
   comparison val=-1
calling compare: o1 node=0 48 o2 node=0 48
   comparison val=0
*** looking at node 1***, sur size=3
calling compare: o1 node=1 49 o2 node=1 49
   comparison val=0
*** node 1 not in usedResults
calling compare: o1 node=1 49 o2 node=1 49
   comparison val=0
*** looking at node 2***, sur size=3
calling compare: o1 node=2 50 o2 node=1 49
   comparison val=1
calling compare: o1 node=2 50 o2 node=2 50
   comparison val=0
*** node 2 not in usedResults
calling compare: o1 node=2 50 o2 node=1 49
   comparison val=1
calling compare: o1 node=2 50 o2 node=2 50
   comparison val=0

The offending lines start here:

*** looking at node 0***, sur size=3
calling compare: o1 node=0 48 o2 node=1 49
   comparison val=-1
calling compare: o1 node=0 48 o2 node=0 48
   comparison val=0
*** node 0 not in usedResults

Meaning that the comparison value is 0 (which means they are equal) and yet the adding code is still being called, meaning that the list is not saying that it contains the value. Why? I thought that returning a compareTo of 0 meant equality? Furthermore, why is equals is never being called? The TreeSet documentation states for contains:

Returns true if this set contains the specified element. More formally, returns true if and only if this set contains an element e such that (o==null ? e==null : o.equals(e)).

Which would lead me to believe that ArbitraryItemComparable.equals would be called, but it is not? Any ideas on how to get the usedResults.contains function to return true?


Solution

  • You have this:

    ...
    if (!set.usedResults.contains(itemComparable)); { // NOTE THE SEMICOLON!!!!
    

    That basically means "ignore this condition and unconditionally execute the following block".