Я хочу подсчитать количество дубликатов в моем списке с помощью пользовательской переменной (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;
....
}
У массива нет правильного равенства и хеш-функции, поэтому вам нужно определить какой-то объект вокруг него, чтобы сделать это.
Мне кажется, у вас небольшая логическая ошибка. Представьте, что у вас есть два объекта PersonHash
, скажем, personHash1
и personHash2
с идентификаторами 1 и 2 соответственно и с одинаковым массивом байтов myHash
; Какой из них должен появиться на карте результатов и почему? Как вы хотите их считать?
@Eritrean это не имеет значения, для меня это один и тот же объект, даже если у них разные идентификаторы
Предполагая, что вы хотите сгруппировать по содержимому массивов, используйте Map<ByteBuffer, Long> result = list.stream() .collect(Collectors.groupingBy(p -> ByteBuffer.wrap(p.getMyHash()), Collectors.counting()));
Вы не можете сгруппировать по 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
.
@ Эритрея, конечно, ты прав! Спасибо!
Вы должны переопределить функции 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 Добро пожаловать. Пожалуйста, отметьте это как ответ, если он отвечает на ваш вопрос.
Вам действительно не нужен метод hashCompare
. Вы можете использовать return Arrays.equals(hash,((PersonHash) ob).getHash());
напрямую в методе equals.
@WJS да, это было бы лучше. Я отредактирую свой ответ и изменю метод equals.
Другой подход без изменения вашего текущего 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);
По ссылке на метод (object::method)