简体   繁体   中英

Java TreeSet not adding object

I am trying to add objects into a Treeset but the objects not all are getting added.

class Fruits
{
     String name ;
     int weight;
     int price;

    Fruits(String n, int w, int p)
    { 
        this.name=n;
        this.weight=w;
        this.price =p;
    }

    @Override
    public int hashCode() {
        System.out.println("hashcode called");
        int prime =31;
        int result =1;
        result = prime*result +(this.name.hashCode()+this.price+this.weight);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("Equals called");
        if(null!=obj)
        {
            Fruits f= (Fruits) obj;
            if(this.name.equals(f.name) && this.price==f.price && this.weight == f.price)
            {
                return true;
            }
        }
        return false;
    }
}

class FruitsComparator implements Comparator<Fruits>
{
    //Order by Name, then quanity and then Price
    @Override
    public int compare(Fruits f1, Fruits f2)
    {
        if(f1.name.equals(f2.name) && f1.weight == f2.weight && f1.price == f2.price)
        {
            System.out.println(1);
            return 0;
        }
        else if(f1.name.equals(f2.name) && f1.weight==f2.weight && f1.price < f2.price)
        {
            System.out.println(2);
            return -1;
        }
        else if (f1.name.equals(f2.name) && f1.weight==f2.weight && f1.price > f2.price)
        {
            System.out.println(3);
            return 1;
        }
        else if (f1.name.equals(f2.name) && f1.weight<f2.weight && f1.price == f2.price)
        {
            System.out.println(4);
            return -1;
        }
        else if (f1.name.equals(f2.name) && f1.weight>f2.weight && f1.price == f2.price)
        {
            System.out.println(5);
            return 1;
        }
        else if (f1.name.compareTo(f2.name) <1 && f1.weight==f2.weight && f1.price == f2.price)
        {
            System.out.println(6);
            return -1;
        }
        else if (f1.name.compareTo(f2.name) >1 && f1.weight==f2.weight && f1.price == f2.price)
        {
            System.out.println(7);
            return 1;
        }
            return 0;
    }       
}

From public static void main of another class.

Fruits f1= new Fruits("Apple",1,3);
Fruits f2= new Fruits("Apple",10,1);
Fruits f3= new Fruits("Apple",15,2);
Set<Fruits> sf = new TreeSet<Fruits>(new FruitsComparator());
sf.add(f1);
sf.add(f2);
sf.add(f3);
System.out.println("--Fruits Example--");
for( Fruits f: sf)
{
    System.out.println(f.name+"-"+f.weight+"-"+f.price);
}

The output I get is :

--Fruits Example--
Apple-1-3

But when I have fruits objs as below i get the all the objects just keeping everything same but the third element. Fruits f1= new Fruits("Apple",1,3); Fruits f2= new Fruits("Apple",1,1); Fruits f3= new Fruits("Apple",1,2);

The output get for this is

--Fruits Example--
Apple-1-1
Apple-1-2
Apple-1-3

So somehow my objects are treated as same when I keep different elements on weight and price. I couldn't figure out as why the objects are treated as same. Please help.

Tree related collections don't use equals() or hashCode() . Those come into play for Map .

Your conditions in the compare result in a 0 , hence the fruit isn't inserted.

First Apple goes in as the tree is empty. The 2nd & 3rd Apple result in false in all the if conditions, thus returning the final 0 . Put a System.out.println() before the final return to confirm.

If you want to sort the fruits first by name, then by weight & then finally by price, here's a more compact way doing it:

@Override
public int compare(Fruits f1, Fruits f2) {
    if (f1.name.equals(f2.name)) {
        if (f1.weight < f2.weight) {
            return -1;
        } else if (f1.weight > f2.weight) {
            return 1;
        } else {
            if (f1.price < f2.price) {
                return -1;
            } else if (f1.price > f2.price) {
                return 1;
            } else {
                return 0;
            }
        }
    } else {
        return f1.name.compareTo(f2.name);
    }
}

The primary issue is, you are always checking two fields to be equal and only one to be different. At the final else, that happens if at least 2 fields are different, you return 0 which means they should be treated as equal, and that is the reason you have this issue.

Since the order you want is to first sort by name, then by quantity and then by price, remove the && f1.price == f2.price from the 4th condition onwards, and remove && f1.weight==f2.weight on the last two.


You can avoid this issue completely if you use Java 8 style.

Set<Fruits> sf = new TreeSet<Fruits>(Comparator.comparing(Fruits::getName)
    .thenComparing(Fruits::getWeight)
    .thenComparing(Fruits::getPrice)
    );

I have added the working code in codiva - online java compiler ide . I have also included a slightly cleaner implementation in FruitsComparator.java file.

You have an error in your equals method in class Fruits :

if(this.name.equals(f.name) && this.price==f.price && this.weight == f.price)

should have been:

if(this.name.equals(f.name) && this.price==f.price && this.weight == f.weight)

(note the last part).

TreeSet , when used with a Comparator , the elements' equality is decided by the compare method of the Comparator , otherwise would use the compareTo method of its element since they are required to implement the Comparable interface. The hashcode and equals methods will only be used by the Set interface itself (such as method contains uses equals method to check if the elements are presented). And hashcode is not something that a TreeSet to use while it is used by HashSet which is totally another way to implement Set interface. Thus, in your code, since the compare method you've overridden of the Comparator treats these elements equal, so they cannot be inserted for multiple times. One guideline that the Java Tutorial points out is, the compare method should comply with the equals methods, which is, the elements should be treated equal in the compare method if and only if the equals method do.

And in your equals method, you did use this.weight == f.price to compare two fruits, which I don't think is what you intended to do. This makes your equals methods not consistent with the compare method.

For your reference, see Java Object Ordering tutorial, and as well as a question I asked two days ago.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM