如果我可以说明这些元素类型之间的一对一关系,我正在寻找一种方法来判断两组不同的元素类型是否相同。在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;
}
}));
我正在寻找这项任务的标准解决方案。或者欢迎任何有关如何改进此代码的建议。
答案 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的元素,因为你不知道L
和R
如何实现等于所以我的建议是不比较不同类型的集合。
如果您真的需要按照描述的方式比较两个集合,我会将所有元素从L
复制到一个列表,然后通过R
并且每次在{{{}}中找到元素1}}将其从L
中删除。只需确保使用List
代替LinkedList
。