我有一个如下对象:
Security Credentials
我有两个哈希集,其中包含来自此对象的一些实例:
public Class MyObjDTO {
private Long id;
private Boolean checked;
//getter and setters
@Override
public final int hashCode() {
Long id = getId();
return (id == null ? super.hashCode() : id.hashCode());
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (!(obj instanceof MyObjDTO))
return false;
Long id = getId();
Long objId = ((MyObjDTO) obj).getId();
if (id.equals(objId)) {
return true;
} else {
return false;
}
}
}
所以我想在这里做的是选择HashSet oldSet = new HashSet();
oldSet.add(new MyObjDTO(1,true));
oldSet.add(new MyObjDTO(2,true));
oldSet.add(new MyObjDTO(3,false));
HashSet newSet = new HashSet();
newSet.add(new MyObjDTO(1,false));
newSet.add(new MyObjDTO(2,true));
newSet.add(new MyObjDTO(4,true));
而不是newSet
中的对象,在这种情况下是oldSet
我用它做的:new MyObjDTO(4,true)
:< / p>
Stream<MyObjDTO> toInsert = newSet.stream().filter(e -> !oldSet.contains(e));
然后我想选择oldSet
而不是newSet
中的对象,在这种情况下是new MyObjDTO(3,false)
,我用它做了:{/ p>
Stream<MyObjDTO> toRemove = oldSet.stream().filter(e -> !newSet.contains(e));
最后一步是我要选择newSet和oldSet中的对象,但它们对属性checked
具有不同的值,在这种情况下它是new MyObjDTO(1,false)
。
我试过的是:
Stream<MyObjDTO> toUpdate = oldSet.stream().filter(newSet::contains);
但是这个会返回new MyObjDTO(1,false)
和new MyObjDTO(2,true)
。
我该如何解决这个问题?
答案 0 :(得分:3)
一种方法是首先使用地图,然后调整过滤条件:
Map<MyObjDTO, Boolean> map = newSet.stream()
.collect(Collectors.toMap(Function.identity(), MyObjDTO::getChecked));
Stream<MyObjDTO> toUpdate = oldSet.stream()
.filter(old -> newSet.contains(old) && old.getChecked() != map.get(old));
答案 1 :(得分:1)
在最后一步中,您依赖于DTO的equals()
方法:
Stream<FonctionnaliteDTO> toUpdate = oldSet.stream().filter(newSet::contains);
该方法仅使用id field
来确定对象相等性
你不想这样做。
您想要对特定字段进行过滤:checked
。
此外,您应该对两个集合的交集结果执行操作。
请注意,您应该只使用Collection.retainAll()
来计算两个集合之间的交集:
Set<MyObjDTO> set = ...
Set<MyObjDTO> setTwo = ...
set.retainAll(setTwo);
然后,您可以使用双循环过滤具有相同id
和checked
值的对象:for + iterator。
for (MyObjDTO dto : set){
for (Iterator<MyObjDTO> it = set.iterator(); it.hasNext();){
MyObjDTO otherDto = it.next();
if (otherDto.getId().equals(dto.getId()) &&
otherDto.getChecked() == dto.getChecked()){
it.remove();
}
}
}
你可以用Stream做到这一点,但IHMO可能会降低可读性。
答案 2 :(得分:1)
首先,您的equals()
和hashCode()
方法违反了他们的基本合同。根据{{3}}:
如果两个对象根据equals(Object)方法相等,则对两个对象中的每一个调用hashCode方法必须产生相同的整数结果。
您hashCode()
的实施不遵循此合同。你的第一步应该是解决这个问题。
其次,从Java 1.2(差不多20年前)开始,java提供了方法javadoc of hashCode()
,它完全符合你想要的第一部分:
// Given these 2 sets:
HashSet<MyObjDTO> oldSet = new HashSet<>();
HashSet<MyObjDTO> newSet = new HashSet<>();
HashSet<MyObjDTO> onlyInNew = new HashSet<>(newSet);
onlyInNew.removeAll(oldSet);
// similar for onlyInOld
对于第二部分,您需要创建一个Map
来查找并获取该对象:
Map<MyObjDTO, MyObjDTO> map = new HashMap<>O;
oldSet.forEach(o -> map.put(o, o);
HashSet<MyObjDTO> updated = new HashSet<>(newSet);
updated.removeIf(o -> oldSet.contains(o) && o.getChecked()() != map.get(o).getChecked());