Java 8中两个HashSet之间的交集

时间:2017-12-22 16:22:48

标签: collections java-8 java-stream hashset

我有一个如下对象:

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)

我该如何解决这个问题?

3 个答案:

答案 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);

然后,您可以使用双循环过滤具有相同idchecked值的对象: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());