Группировка Java по пользовательской переменной и возврат одного и того же объекта

Я хочу подсчитать количество дубликатов в моем списке с помощью пользовательской переменной (myHash)

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

Это будет считать дубликаты по идентификатору, который является значением в хеше и равен функции. Как я могу посчитать это по пользовательской переменной? В моем случае это byte[] myHash

мой поджо:

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

По ссылке на метод (object::method)

OliAtMtrail 22.03.2022 11:32

У массива нет правильного равенства и хеш-функции, поэтому вам нужно определить какой-то объект вокруг него, чтобы сделать это.

RealSkeptic 22.03.2022 11:32

Мне кажется, у вас небольшая логическая ошибка. Представьте, что у вас есть два объекта PersonHash, скажем, personHash1 и personHash2 с идентификаторами 1 и 2 соответственно и с одинаковым массивом байтов myHash; Какой из них должен появиться на карте результатов и почему? Как вы хотите их считать?

Eritrean 22.03.2022 11:56

@Eritrean это не имеет значения, для меня это один и тот же объект, даже если у них разные идентификаторы

hudi 22.03.2022 12:10

Предполагая, что вы хотите сгруппировать по содержимому массивов, используйте Map<ByteBuffer, Long> result = list.stream() .collect(Collectors.groupingBy(p -> ByteBuffer.wrap(p.getMyHash()), Collectors.counting()));

Holger 23.03.2022 13:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
5
79
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы не можете сгруппировать по myHash и получить экземпляр PersonHash в качестве ключа, если myHash не является идентификатором и частью equals и hashCode.

Если myHash не является частью equals и hashCode, добавить геттер для myHash

PersonHash {
   getMyHash() {...}
}

и использовать

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

После этого вы можете сопоставить list с results, чтобы найти объекты с заданным хешем.

Или используйте

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

чтобы получить список PersonHash с тем же значением myHash.

@ Эритрея, конечно, ты прав! Спасибо!

Datz 22.03.2022 11:56
Ответ принят как подходящий

Вы должны переопределить функции equals и hashCode вашего объекта. Тогда вы можете сделать это с помощью 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, чтобы сделать это правильно

hudi 22.03.2022 12:13

@hudi Добро пожаловать. Пожалуйста, отметьте это как ответ, если он отвечает на ваш вопрос.

Lrrr 22.03.2022 12:51

Вам действительно не нужен метод hashCompare. Вы можете использовать return Arrays.equals(hash,((PersonHash) ob).getHash()); напрямую в методе equals.

WJS 22.03.2022 13:28

@WJS да, это было бы лучше. Я отредактирую свой ответ и изменю метод equals.

Lrrr 22.03.2022 14:12

Другой подход без изменения вашего текущего 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);

Другие вопросы по теме