Java 按自定义变量和 return 同一对象分组

Java group by custom variable and return same object

我想通过自定义变量 (myHash) 计算列表中重复项的数量

Map<PersonHash, Long> result = list.stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

这将通过 id 计算重复项,它是散列和 equals 函数中的值。我如何通过自定义变量来计算它?在我的例子中是 byte[] myHash

我的POJO:

public class PersonHash implements Serializable {
    
    private Long id;
    private byte[] myHash;    
    ....
}

如果 myHash 不是 equals 和 [= 的标识符和一部分,则不能按 myHash 分组并获取 PersonHash 的实例作为键17=].

如果 myHash 不是 equalshashCode 的一部分, 为 myHash

添加 getter
PersonHash {
   getMyHash() {...}
}

并使用

Map<byte[], Long> result = list.stream()
        .collect(Collectors.groupingBy(PersonHash::getMyHash, Collectors.counting()));

之后,您可以将 listresults 进行匹配,以找到具有给定哈希值的对象。

或使用

Map<byte[], List<PersonHash>> result = list.stream()
        .collect(Collectors.groupingBy(PersonHash::getMyHash));

获取具有相同 myHash 值的 PersonHash 的列表。

您必须覆盖对象的 equalshashCode 函数。然后你可以用 Function.identity() 来做到这一点。我已经覆盖了如下这些功能:

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    PersonHash personHash = (PersonHash) o;
    return hashCompare(personHash) == 0;
}

@Override
public int hashCode() {
    return myHash.length;
}

public int hashCompare(PersonHash other) {
    int i = this.myHash.length - other.myHash.length;
    if (i != 0) {
        return i;
    }
    for (int j = 0; j < this.myHash.length; j++) {
        i = this.myHash[j] - other.myHash[j];
        if (i != 0) {
            return i;
        }
    }
    return 0;
}

现在使用以下代码:

    PersonHash personHash1 = new PersonHash();
    personHash1.setId(1L);
    personHash1.setMyHash(new byte[]{1, 2, 3});
    PersonHash personHash1_2 = new PersonHash();
    personHash1_2.setId(3L);
    personHash1_2.setMyHash(new byte[]{1, 2, 3});
    PersonHash personHash2 = new PersonHash();
    personHash2.setId(2L);
    personHash2.setMyHash(new byte[]{4, 5, 6});
    List<PersonHash> list = new LinkedList<>();
    list.add(personHash1);
    list.add(personHash1_2);
    list.add(personHash2);

    Map<PersonHash, Long> result = list.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

    result.forEach((k, v) -> System.out.println(Arrays.toString(k.getMyHash()) + " " + v));

您将得到以下输出:

[4, 5, 6] 1
[1, 2, 3] 2

PS:请写出更好的hashCode()函数,我只是想演示一下。

编辑: 正如@WJS 评论的那样,我们可以像这样覆盖 equals 方法,我们不再需要 hashCompare 函数:

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    return Arrays.equals(myHash,((PersonHash) ob).getHash());
}

另一种不更改当前 pojo 的方法(对 equals 和 hashcode 的更改可能会在其他地方导致错误)可能是按 myHash 字段对列表进行排序,然后您可以使用原子引用来构建您的地图

List<PersonHash> list // your list

Comparator<PersonHash> byMyHash = (a,b) -> Arrays.compare(a.getMyHash(),b.getMyHash());
BiPredicate<PersonHash,PersonHash> pred = (a,b) -> Arrays.equals(a.getMyHash(),b.getMyHash());

list.sort(byMyHash);

AtomicReference<PersonHash> ai = new AtomicReference<>(list.get(0));

Map<PersonHash, Long> result = list.stream()
        .collect(Collectors.groupingBy(ph -> {
            if (pred.test(ph,ai.get())){
                return ai.get();
            }
            else {
                ai.set(ph);
                return ph;
            }
        } , Collectors.counting()));

System.out.println(result);