【问题标题】:Compare two sets of different types比较两组不同类型
【发布时间】:2015-04-02 09:48:28
【问题描述】:

如果我可以说明这些元素类型之间的一对一关系,我正在寻找一种方法来判断两组不同的元素类型是否相同。在 java 或 guava 或 apache commons 中是否有标准的方法来执行此操作?

这是我自己对这个任务的实现。例如,我有两个我知道如何比较的元素类。为简单起见,我按 id 字段比较它们:

class ValueObject {
    public int id;
    public ValueObject(int id) { this.id=id; }
    public static ValueObject of(int id) { return new ValueObject(id); }
}

class DTO {
    public int id;
    public DTO(int id) { this.id=id; }
    public static DTO of(int id) { return new DTO(id); }
}

然后我定义一个进行比较的接口

interface TwoTypesComparator<L,R> {
    boolean areIdentical(L left, R right);
}

比较集合的实际方法是这样的

public static <L,R> boolean areIdentical(Set<L> left, Set<R> right, TwoTypesComparator<L,R> comparator) {
    if (left.size() != right.size()) return false;
    boolean found;
    for (L l : left) {
        found = false;
        for (R r : right) {
            if (comparator.areIdentical(l, r)) {
                found = true; break;
            }
        }
        if (!found) return false;
    }
    return true;
}

客户端代码示例

HashSet<ValueObject> valueObjects = new HashSet<ValueObject>();
valueObjects.add(ValueObject.of(1));
valueObjects.add(ValueObject.of(2));
valueObjects.add(ValueObject.of(3));

HashSet<DTO> dtos = new HashSet<DTO>();
dtos.add(DTO.of(1));
dtos.add(DTO.of(2));
dtos.add(DTO.of(34));

System.out.println(areIdentical(valueObjects, dtos, new TwoTypesComparator<ValueObject, DTO>() {
    @Override
    public boolean areIdentical(ValueObject left, DTO right) {
        return left.id == right.id;
    }
}));

我正在寻找该任务的标准解决方案。或者欢迎提出任何改进此代码的建议。

【问题讨论】:

    标签: java guava apache-commons


    【解决方案1】:

    这就是我在你的情况下会做的。你有套。集合很难比较,但最重要的是,您想比较它们的 id。

    我只看到一个正确的解决方案,您必须标准化所需的值(提取它们的 id)然后对这些 id 进行排序,然后按顺序比较它们,因为如果你不排序和比较你可能会跳过重复和/或值的传递。

    想一想 Java 8 允许您对流进行惰性处理这一事实。所以不要急于认为提取,然后排序然后复制很长。与迭代解决方案相比,惰性允许它相当快。

    HashSet<ValueObject> valueObjects = new HashSet<>();
    valueObjects.add(ValueObject.of(1));
    valueObjects.add(ValueObject.of(2));
    valueObjects.add(ValueObject.of(3));
    
    HashSet<DTO> dtos = new HashSet<>();
    dtos.add(DTO.of(1));
    dtos.add(DTO.of(2));
    dtos.add(DTO.of(34));
    
    boolean areIdentical = Arrays.equals(
        valueObjects.stream()
            .mapToInt((v) -> v.id)
            .sorted()
            .toArray(),
        dtos.stream()
            .mapToInt((d) -> d.id)
            .sorted()
            .toArray()
    );
    

    您想概括解决方案吗?没问题。

    public static <T extends Comparable<?>> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor) {
      return Arrays.equals(
        vos.stream()
          .map(voKeyExtractor)
          .sorted()
          .toArray(),
        dtos.stream()
          .map(dtoKeyExtractor)
          .sorted()
          .toArray()
      );
    }
    

    对于无法比较的T

    public static <T> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor, Comparator<T> comparator) {
      return Arrays.equals(
        vos.stream()
          .map(voKeyExtractor)
          .sorted(comparator)
          .toArray(),
        dtos.stream()
          .map(dtoKeyExtractor)
          .sorted(comparator)
          .toArray()
      );
    }
    

    您提到 Guava,如果您没有 Java 8,您可以使用相同的算法执行以下操作:

    List<Integer> voIds = FluentIterables.from(valueObjects)
      .transform(valueObjectIdGetter())
      .toSortedList(intComparator());
    List<Integer> dtoIds = FluentIterables.from(dtos)
      .transform(dtoIdGetter())
      .toSortedList(intComparator());
    return voIds.equals(dtoIds);
    

    【讨论】:

    • 集合实际上非常容易和快速地比较,但只有当它们属于同一类型时。
    • 如果真的有,就不会有so many helper methods in Guava。并不是说我反对你的话,但是Set 显然是在 Java 1.2 中首次设计时最缺乏思想的。
    • 嗯,这些方法中的大多数都是newXXXSet 的形式,我会不计其数。然后算出返回不可变视图的那些,实际上您并没有那么多方法可以提供不存在的功能。
    • 当然,你忽略了newXxxSet 和两个不可变的方法……这些完全是对论证的辅助。
    • 我认为当你调用 toArray() - 你失去了流懒惰的优势。 Arrays.equals 已经适用于复制和排序的数组。
    【解决方案2】:

    另一种解决方案是使用 List 而不是 Set(如果您被允许这样做)。 List 有一个名为 get(int index) 的方法,它检索指定索引处的元素,当两个列表的大小相同时,您可以逐个比较它们。更多列表:http://docs.oracle.com/javase/7/docs/api/java/util/List.html

    另外,避免在你的类中使用公共变量。一个好的做法是将变量设为私有并使用 getter 和 setter 方法。

    实例化列表并添加值

        List<ValueObject> list = new ArrayList<>();
        List<DTO> list2 = new ArrayList<>();
    
        list.add(ValueObject.of(1));
        list.add(ValueObject.of(2));
        list.add(ValueObject.of(3));
    
        list2.add(DTO.of(1));
        list2.add(DTO.of(2));
        list2.add(DTO.of(34));
    

    比较列表的方法

    public boolean compareLists(List<ValueObject> list, List<DTO> list2) {
        if(list.size() != list2.size()) {
            return false;
        }
        for(int i = 0; i < list.size(); i++) {
            if(list.get(i).id == list2.get(i).id) {
                continue;
            } else {
                return false;
            }
        }
        return true;
    }
    

    【讨论】:

    • 如果我一个一个地遍历一组元素,我实际上做了什么。
    • 列表保持插入顺序。从我收集的信息来看,只有当每个集合在完全相同的位置具有完全相同的元素时,您才希望您的结果为真。如果我误解了您的问题,请告诉我。
    • 不,这里不需要相同的位置。实际上我使用不需要保留位置的 Set 接口
    • 哦,好的。然后忽略解决方案。如果我想出适合套装的东西,我会发表评论。
    【解决方案3】:

    您当前的方法不正确或至少与一般集合不一致。

    想象一下:

    L 包含对 (1,1)、(1,2)、(2,1)。

    R 包含对 (1,1)、(2,1)、(2,2)。

    现在,如果您的 id 是第一个值,您的比较将返回 true,但这些集合真的相等吗?问题是您不能保证集合中最多有一个具有相同 id 的 Element 因为您不知道 LR 如何实现等于所以我的建议是不要比较不同类型的集合.

    如果您真的需要按照您描述的方式比较两个集合,我会将所有元素从 L 复制到列表,然后通过 R 并且每次在 L 中找到元素时将其从List。只要确保您使用 LinkedList 而不是 ArrayList 即可。

    【讨论】:

      【解决方案4】:

      您可以覆盖 dto/value 对象上的等号和哈希码,然后执行:leftSet.containsAll(rightSet) &amp;&amp; leftSet.size().equals(rightSet.size())

      如果您不能更改元素类,请制作一个装饰器并将集合设为装饰器类型。

      【讨论】:

      • 除了它是肮脏的解决方案之外,如果无法更改元素类我该怎么办
      • 为什么脏?如果你不能;不能改变元素类......好吧,那是一个不同的问题
      • @Kayaman 嗯?不,它没有。它以什么方式破坏任何东西,它的自反性、对称性等
      • @NimChimpsky 啊,你是对的,它不会违反合同。我在考虑对称要求,但它没有指定关于类的任何内容。
      • @VladislavLezhnin 为什么你觉得这很脏,如果你不能改变底层的元素类就做一个装饰器类
      猜你喜欢
      • 1970-01-01
      • 2020-10-12
      • 2018-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-18
      • 2019-08-06
      • 2014-11-12
      相关资源
      最近更新 更多