比较两组不同类型

时间:2015-04-02 09:48:29

标签: java guava apache-commons

如果我可以说明这些元素类型之间的一对一关系,我正在寻找一种方法来判断两组不同的元素类型是否相同。在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;
    }
}));

我正在寻找这项任务的标准解决方案。或者欢迎任何有关如何改进此代码的建议。

4 个答案:

答案 0 :(得分: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);

答案 1 :(得分:0)

您可以在dto / value对象上覆盖equals和hashcode,然后执行:leftSet.containsAll(rightSet) && leftSet.size().equals(rightSet.size())

如果你不能改变元素类,那就做一个装饰器,让它们成为装饰器类型。

答案 2 :(得分:0)

另一种解决方案是使用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;
}

答案 3 :(得分:0)

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

想象一下:

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

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

现在如果你的id是第一个值,你的比较会返回true,但那些集合真的相同吗?问题是你无法保证集合中最多只有一个具有相同ID的元素,因为你不知道LR如何实现等于所以我的建议是不比较不同类型的集合。

如果您真的需要按照描述的方式比较两个集合,我会将所有元素从L复制到一个列表,然后通过R并且每次在{{{}}中找到元素1}}将其从L中删除。只需确保使用List代替LinkedList