Home > Net >  print all repeated items in array object using java
print all repeated items in array object using java

Time:06-14

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);
  • Related