简体   繁体   中英

Overriding equals and hashcode for a Triplet Class In java

I have a Triple class which is a class which can contain 3 integers (x,y,z). I want to override the equals/hashcode methods so that they can be used in a set. So an obj with (1,2,3) should be equal to (3,2,1) or (3,1,2) and so should be equal to any of it's permutation. I know how to do this for a Pair class with (x,y) - the code for a pair class I have for this is:

class Pair {
    int x;
    int y;

    public Pair(int x, int y) {
        this.x = x;
        this.y = y;
    }


    @Override
    public boolean equals(Object obj) {

        if(obj instanceof  Pair) {
            Pair p = (Pair) obj;

            if (this.x == p.x && p.y == this.y || this.x == p.y && this.y == p.x) {
                return true;
            }
        }

        return false;
    }

    @Override
    public int hashCode() {
        return Integer.hashCode(x) * Integer.hashCode(y);
    }
}

This works fine but if I want to extend this to a Triple class, I know I can edit the equals method and add more conditions to check but this seems really long. Is there any way I can do this without using external libraries in Java?

One solution is to keep a sorted array for comparison:

class Triple {
    private final int x, y, z;
    private final int[] sorted;

    public Triple(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.sorted = new int[] {x, y, z};
        Arrays.sort(sorted);
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Triple
                && Arrays.equals(((Triple)obj).sorted, this.sorted);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(sorted);
    }
}

To check the combinations, you can add the elements into list and invoke containsAll method to check the equality, eg:

public static void main(String[] args) throws IOException {
    List<Integer> list1 = Arrays.asList(new Integer[]{1,2,4});
    List<Integer> list2 = Arrays.asList(new Integer[]{4,2,1});
    System.out.println(list1.containsAll(list2) && list2.containsAll(list1));
}

The correct answer depends on how you'd want to use this class, if equality and hashCode needs to be inexpensive, then consider initialising an array on construction that can be easily compared. Something like this:

import java.util.Arrays;

public class Triple {
    // Use this array only for hashCode & equals.
    private final int[] values;
    private final int x;
    private final int y;
    private final int z;

    public Triple(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.values = new int[]{x, y, z};
        // Sort the values for simpler comparison of equality.
        Arrays.sort(values);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Triple triple = (Triple) o;
        return Arrays.equals(values, triple.values);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(values);
    }
}

Added some tests to prove equality and non equality:

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNot.not;

import org.junit.Assert;
import org.junit.Test;

public class TripleTest {

    @Test
    public void valuesInDifferentOrderAreEqual() {
        Triple sortedTriple = new Triple(1, 2, 3);
        Triple outOfOrderTriple = new Triple(3, 2, 1);
        Assert.assertThat(sortedTriple, equalTo(outOfOrderTriple));
        Assert.assertThat(sortedTriple.hashCode(), is(outOfOrderTriple.hashCode()));
    }

    @Test
    public void valuesInOrderAreEqual() {
        Triple sortedTriple = new Triple(1, 2, 3);
        Triple outOfOrderTriple = new Triple(1, 2, 3);
        Assert.assertThat(sortedTriple, equalTo(outOfOrderTriple));
        Assert.assertThat(sortedTriple.hashCode(), is(outOfOrderTriple.hashCode()));
    }

    @Test
    public void valuesThatAreDifferentAreNotEqual() {
        Triple sortedTriple = new Triple(1, 2, 3);
        Triple outOfOrderTriple = new Triple(7, 8, 9);
        Assert.assertThat(sortedTriple, not(outOfOrderTriple));
        Assert.assertThat(sortedTriple.hashCode(), not(outOfOrderTriple.hashCode()));
    }

    @Test
    public void valuesWithSameSumAreNotEqual() {
        Triple sortedTriple = new Triple(11, 21, 31);
        Triple outOfOrderTriple = new Triple(36, 12, 21);
        Assert.assertThat(sortedTriple, not(outOfOrderTriple));
        Assert.assertThat(sortedTriple.hashCode(), not(outOfOrderTriple.hashCode()));
    }

    @Test
    public void valuesWithSameProductAreNotEqual() {
        Triple sortedTriple = new Triple(11, 21, 31);
        Triple outOfOrderTriple = new Triple(33, 7, 31);
        Assert.assertThat(sortedTriple, not(outOfOrderTriple));
        Assert.assertThat(sortedTriple.hashCode(), not(outOfOrderTriple.hashCode()));
    }
}

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