I would like to know how to print all repeated items in an array of objects. For ex:
[
{
"key": "KEY1",
"value": "123",
},
{
"key": "KEY2",
"value": "234",
},
{
"key": "KEY1",
"value": "456"
}
]
code: I have this logic but it will exclude the first occurrence of KEY1 and prints only a duplicate of it.
Set<String> duplicates = new HashSet();
..for(i in items)..{
if (duplicates.contains(i.val())) {
System.out.println('duplicates found');
}
}
I want to print both items as below:
duplicate items found, [KEY1] with values ["123", "456"]
CodePudding user response:
I think that for what you're trying to achieve a Map<String, List<String>>
would be better suited rather than a HashSet<String>
. In fact, a Set
would only tell you whether a key is already contained in the Set
or not, but it won't keep track of how many values a particular key is bound to.
Here is an implementation where the key's object is used as the key for the Map
and its value added to the Map
's list of values:
class MyObject {
private String key;
private String value;
//... contrustcor & getters ...
}
public class Main {
public static void main(String[] args) {
MyObject[] items = new MyObject[]{new MyObject("KEY1", "123"), new MyObject("KEY2", "234"), new MyObject("KEY1", "456")};
HashMap<String, List<String>> map = new HashMap<>();
for (MyObject obj : items) {
if (!map.containsKey(obj.getKey())) {
map.put(obj.getKey(), new ArrayList<>(List.of(obj.getValue())));
} else {
map.get(obj.getKey()).add(obj.getValue());
}
}
for (Map.Entry<String, List<String>> e : map.entrySet()) {
if (e.getValue().size() > 1) {
System.out.printf("Duplicate items found, %s with values %s%n", e.getKey(), e.getValue());
}
}
}
}
Instead, if you need to add the actual object within the data structure, then regardless of using a HashSet
or a HashMap
, your class needs to honor the general equals and hashcode contracts by providing a proper definition for the corresponding methods.
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()
In fact, a HashSet
uses a HashMap
under the hood, so in both cases is necessary to override said methods. This is because a HashMap
is implemented as an array of buckets, where each entry is stored within a bucket based on the key's hashCode()
. However, different keys may yield same hashcodes, so multiple entries may be listed within a same bucket. At that point, the HashMap
has to resort to the equals()
method to find the exact key within a bucket which corresponds to the inputted entry in order to retrieve or replace an element. This is why it is so crucial to provide a proper definition of the hashCode()
and equals()
methods.
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
In your case, a possible implementation could be:
class MyObject {
private String key;
private String value;
//... contrustcor & getters ...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyObject myObject = (MyObject) o;
return Objects.equals(key, myObject.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
@Override
public String toString() {
return value;
}
}
public class Main {
public static void main(String[] args) {
MyObject[] items = new MyObject[]{new MyObject("KEY1", "123"), new MyObject("KEY2", "234"), new MyObject("KEY1", "456")};
HashMap<MyObject, List<MyObject>> map = new HashMap<>();
for (MyObject obj : items) {
if (!map.containsKey(obj)) {
map.put(obj, new ArrayList<>(List.of(obj)));
} else {
map.get(obj).add(obj);
}
}
for (Map.Entry<MyObject, List<MyObject>> e : map.entrySet()) {
if (e.getValue().size() > 1) {
System.out.printf("Duplicate items found, %s with values %s%n", e.getKey().getKey(), e.getValue());
}
}
}
}
CodePudding user response:
Prepare a HashMap
of List
like HashMap<String,List<String>>
if(!map.contains(key)) {
map.put(key,new ArrayList<String>());
}
map.get(key).add(value);
In the end, you can iterate HashMap, check if the ArrayList size >1 and print the message with ArrayList
CodePudding user response:
Try this.
static void printAllRepeatedItems(Item[] items) {
Arrays.stream(items)
.collect(groupingBy(Item::key, mapping(Item::value, toList())))
.entrySet().stream()
.filter(e -> e.getValue().size() > 1)
.forEach(e -> System.out.printf("duplicate items found, %s with values %s%n",
e.getKey(), e.getValue()));
}
And
Item[] items = {
new Item("KEY1", "123"),
new Item("KEY2", "234"),
new Item("KEY1", "456"),
};
printAllRepeatedItems(items);
output:
duplicate items found, KEY1 with values [123, 456]
CodePudding user response:
Can be done in one statement using Java 8 streams:
import java.util.List;
import static java.util.stream.Collectors.*;
// ...
record Item(String key, String value) { }
List<String> items = List.of(
new Item("KEY1", "123"),
new Item("KEY2", "234"),
new Item("KEY1", "456")
);
// ...
items
.stream()
.collect(groupingBy(Item::key,
mapping(Item::value, toList()))) // Map<String, List<String>>
.entrySet()
.stream()
.filter(e -> e.getValue().size() > 1) // only duplicates
.map(e -> String.format("Duplicate items found, %s with values %s",
e.getKey(),
e.getValue()))
.forEach(System.out::println);