'Java group by custom variable and return same object
I want to count number of duplicate in my list by custom variable (myHash)
Map<PersonHash, Long> result = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
This will count duplicate by id which is value in hash and equals function. How I can count it by custom variable ? In my case it is byte[] myHash
my pojo:
public class PersonHash implements Serializable {
private Long id;
private byte[] myHash;
....
}
Solution 1:[1]
You have to override the equals and hashCode function of your object. Then you can do this with Function.identity(). I have overrides those functions like below:
@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;
}
And now with the following code:
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));
You will get the following output:
[4, 5, 6] 1
[1, 2, 3] 2
PS: Please write better hashCode() function, I just want to demonstrate.
Edit : As @WJS Commented we could override the equals method like this, and we don't need the hashCompare function anymore:
@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());
}
Solution 2:[2]
You can't group by myHash and get an instance of PersonHash as key, if myHash is not the identifier and part of equals and hashCode.
If myHash is not part of equals and hashCode,
add a getter for myHash
PersonHash {
getMyHash() {...}
}
and use
Map<byte[], Long> result = list.stream()
.collect(Collectors.groupingBy(PersonHash::getMyHash, Collectors.counting()));
Afterwards you can match the list with the results to find the objects with the given hash.
Or use
Map<byte[], List<PersonHash>> result = list.stream()
.collect(Collectors.groupingBy(PersonHash::getMyHash));
to get the list of PersonHash with the same myHash value.
Solution 3:[3]
Another approach without changing your current pojo (changes to equals and hashcode might cause errors some where else) could be to sort your list by your myHash field, then you could use an atomic reference to build your map
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);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Eritrean |
