在 java 中覆盖三元组 Class 的等于和哈希码

Overriding equals and hashcode for a Triplet Class In java

我有一个三元组 class,它是一个 class,可以包含 3 个整数 (x,y,z)。我想覆盖 equals/hashcode 方法,以便它们可以在集合中使用。因此具有 (1,2,3) 的对象应该等于 (3,2,1) 或 (3,1,2),因此应该等于它的任何排列。我知道如何为一对 class 和 (x,y) 执行此操作 - 我拥有的一对 class 的代码是:

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

这很好用,但如果我想将其扩展到三元组 class,我知道我可以编辑 equals 方法并添加更多条件来检查,但这看起来真的很长。有什么方法可以在不使用 Java 中的外部库的情况下做到这一点?

一个解决方案是保留一个排序数组以供比较:

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

要检查组合,您可以将元素添加到 list 并调用 containsAll 方法来检查是否相等,例如:

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

正确的答案取决于你想如何使用它class,如果相等和 hashCode 需要便宜,那么考虑在构造时初始化一个可以很容易比较的数组。像这样:

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

添加了一些测试来证明相等和不相等:

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