我必须使用第三方库中没有equalshashCode的类型.

在我的示例中,这将由包含一些数据的类Box播放.

问题是,我的实现仍然没有考虑到唯一实例的基数.

import org.junit.jupiter.api.Test;

import java.util.Collection;
import java.util.Objects;
import java.util.function.BiPredicate;

import static org.assertj.core.util.Lists.newArrayList;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class CustomEqualsTest {

    public static final BiPredicate<Box, Box> EQUALS = CustomEqualsTest::equals;

    static boolean equals(Box b1, Box b2) {
        if (b1 == b2) {
            return true;
        }
        if (b1 != null && b2 != null) {
            return Objects.equals(b1.data, b2.data);
        }
        return false;
    }

    static class Box {
        Integer data;

        private Box(Integer data) {
            this.data = data;
        }

        static Box of(Integer data) {
            return new Box(data);
        }
        // intentionally skip equals
        // intentionally skip hashCode
    }

    @Test
    void testCustomEquals() {
        assertTrue(equals(
                newArrayList(Box.of(1)),
                newArrayList(Box.of(1)), EQUALS));
        assertFalse(equals(
                newArrayList(Box.of(1)),
                newArrayList(), EQUALS));
        assertFalse(equals(
                newArrayList(Box.of(1)),
                newArrayList((Box) null), EQUALS));
        assertFalse(equals(
                newArrayList(Box.of(1)),
                newArrayList(Box.of(2)), EQUALS));
        assertFalse(equals(
                newArrayList(Box.of(1), Box.of(1)),
                newArrayList(Box.of(1)), EQUALS));

        // This fails:
        assertFalse(
                equals(
                        newArrayList(Box.of(1), Box.of(1), Box.of(2)),
                        newArrayList(Box.of(1), Box.of(2), Box.of(2)),
                        EQUALS));

    }

    static <T> boolean equals(Collection<T> collection1, Collection<T> collection2, BiPredicate<T, T> equals) {
        if (collection1.size() != collection2.size()) {
            return false;
        }

        for (T t1 : collection1) {
            boolean match = false;
            for (T t2 : collection2) {
                if (equals.test(t1, t2)) {
                    match = true;
                    break;
                }
            }
            if (!match) {
                return false;
            }
        }
        return true;
    }

}

推荐答案

由于您无法提供equalshashCode,因此大多数集合都是不可能的.这使我们只能使用树 struct (精确地说是TreeMap)和自定义Comparator.空安全比较器示例:

public static final Comparator<Box> COMPARATOR = Comparator.nullsLast(Comparator.comparing(Box::getData));

使用TreeMap对每个集合中的元素进行计数

static <T> boolean equals(Collection<T> collection1, Collection<T> collection2, BiPredicate<T, T> equals, Comparator<T> comparator) {
  if (collection1.size() != collection2.size()) {
    return false;
  }
  
  Map<T, Integer> m1 = new TreeMap<>(comparator);
  for (T t : collection1) {
    m1.merge(t, 1, Integer::sum);
  }
  Map<T, Integer> m2 = new TreeMap<>(comparator);
  for (T t : collection2) {
    m2.merge(t, 1, Integer::sum);
  }
  
  if (m1.size() != m2.size()) {
    return false;
  }
  Iterator<Map.Entry<T, Integer>> m2Iterator = m2.entrySet().iterator();
  for (Map.Entry<T, Integer> m1Entry : m1.entrySet()) {
    Map.Entry<T, Integer> m2Entry = m2Iterator.next();
    if (!equals.test(m1Entry.getKey(), m2Entry.getKey())) {
      return false;
    }
    if (!m1Entry.getValue().equals(m2Entry.getValue())) {
      return false;
    }
  }
  return true;
}

条目现在的顺序相同,因此我们可以安全地将当前键(框)与当前计数进行比较.

另一种 Select 是将集合收集到已排序的列表中,并比较每个元素.

static <T> boolean equals(Collection<T> collection1, Collection<T> collection2, BiPredicate<T, T> equals, Comparator<T> comparator) {
  if (collection1.size() != collection2.size()) {
    return false;
  }
  
  List<T> l1 = collection1.stream().sorted(comparator).collect(Collectors.toList());
  List<T> l2 = collection2.stream().sorted(comparator).collect(Collectors.toList());
  for (int i = 0; i < l1.size(); i++) {
    if (!equals.test(l1.get(i), l2.get(i))) {
      return false;
    }
  }
  return true;
}

如果集合保证是列表,并且您不需要按输入顺序对其进行排序,那么就可以直接排序,而无需流式处理.我会坚持第二种 Select ,如果可能的话,我认为它会更简短,更具可读性.

Java相关问答推荐

将偶数元素移动到数组的前面,同时保持相对顺序

伪类focus-in不适用于PFA中的选项卡

我可以从Java模块中排除maven资源文件夹吗?

条件加载@ManyToMany JPA

使用意向过滤器从另一个应用程序启动服务

当我已经安装了其他版本的Java时,如何在Mac OSX 14.3.1上安装Java 6?

为什么我的在一个范围内寻找素数的程序不能像S所期望的那样工作

Com.example.service.QuestionService中的构造函数的参数0需要找不到的类型为';com.example.Dao.QuestionDao;的Bean

Spring Data JPA慢慢地创建了太多非活动会话

Java ArrayList的整数和数组的泛型

Java Mooc.fi Part 12_01.Hideout -返回和删除方法

有效的公式或值列表必须少于或等于255个字符

在处理2个映射表时,没有更多的数据可从套接字读取

try 在两个不同数组的数字之间求平均值

我的代码是线程安全的吗?[Java、CAS、转账]

在Eclipse中可以使用外部字体吗?

为什么使用lo索引来解决二进制搜索问题不同于使用hi索引?

错误:JOIN/ON的参数必须是boolean类型,而不是bigint类型.Java Spring启动应用程序

如何从指定某些字段的父对象创建子对象

@此处不能应用可为null的批注