0

Java 8 here. I have a method checkDupeKeys that will take three different SortedMap instances as its arguments and needs to verify that no two SortedMap instances have identical keys. My best attempt thus far:

private void checkDupeKeys(SortedMap<String, Fizz> fizzes, SortedMap<String, Buzz> buzzes,
        SortedMap<String, Foobar> foobars) {}
    List<String> keyNames = new ArrayList<>();

    keyNames.addAll(fizzes.keySet().stream().collect(Collectors.toList()));
    keyNames.addAll(buzzes.keySet().stream().collect(Collectors.toList()));
    keyNames.addAll(foobars.keySet().stream().collect(Collectors.toList()));

    if(keyNames.size() > keyNames.stream().collect(Collectors.toSet()).size()) {
        throw new IllegalArgumentException("Duplicate key names are not allowed.");
    }
}

I believe this works, however there might very well be a better way of going about it (efficiency, etc.).

My main concern is that this method doesn't allow me to identify which key names are duplicates. I'd ideally like the exception message to be:

Duplicate key names are not allowed. You have the following duplicate key names: (1) fizzes["derp"] and buzzes["derp"]. (2) fizzes["flim"] and foobars["flim"]. (3) buzzes["flam"] and foobars["flam"].

How can I modify my (non-static) checkDupeKeys method to throw an exception that meets this criteria? That is, how do I get access to which keys inside the streams are duplicates of each other. I'm sure I could do this the hard way using older Java collections APIs, but efficiency and leveraging Java 8 APIs are important to me in this solution.

smeeb
  • 27,777
  • 57
  • 250
  • 447
  • 1
    Why doesn't https://stackoverflow.com/questions/27677256/java-8-streams-to-find-the-duplicate-elements work for you and why not throw the exception while adding the keys itself? Mind sharing the *hard way using older Java collection* that you've tried? – Naman Nov 08 '17 at 16:57
  • Possible duplicate of [How to check if exists any duplicate in Java 8 Streams?](https://stackoverflow.com/questions/30053487/how-to-check-if-exists-any-duplicate-in-java-8-streams) – JiriS Nov 08 '17 at 17:18

2 Answers2

1

Without much use for Java 8's functional idioms, I would simply go with Set#retainAll for each comparison (3 in total).

See draft code below:

private void checkDupeKeys(SortedMap<String, Fizz> fizzes, 
    SortedMap<String, Buzz> buzzes, 
    SortedMap<String, Foobar> foobars) {

    // copies the key set
    Set<String> fizBuzSet = new HashSet<>(fizzes.keySet());

    // this removes all elements of the set that aren't present in the given key set
    fizBuzSet.retainAll(buzzes.keySet());

    // aggregating dupes if needed
    Map<String, Collection<String>> dupes = new HashMap<>();
    // flag to throw exception if needed
    boolean areThereDupes = false;

    if (!fizBuzSet.isEmpty()) {
        areThereDupes = true;
        // TODO log fizBuzSet as set of duplicates between fizzes and buzzes
        // or...
        dupes.put("Fizzes vs Buzzes", fizBuzSet);
    }
    // TODO repeat with fizzes vs foobars, then again with buzzes vs foobars

    // you can either log the dupes separately, or use a Map<String,Collection<String>> where the  
    // keys represent a compound of the two SortedMaps being compared and the values represent the actual duplicates  
    // e.g...
    if (areThereDupes) {
        // TODO throw exception with dupes map representation in message
    }

}
Mena
  • 47,782
  • 11
  • 87
  • 106
  • this isn't doing what is desired in the question. Please take a look at the exception part in the question. – Naman Nov 08 '17 at 17:07
  • @nullpointer yes, this isn't throwing an exception, but if you look at the comment it does give a suggestion on how to aggregate the duplicates by comparison (which is actually what OP wants). In turn, this could be passed as an exception message, i.e. if the map isn't empty, an exception with the map representation could be thrown. – Mena Nov 08 '17 at 17:09
  • @nullpointer I've edited to substantiate on the mapping and exception throwing, hopefully it's a little more specific now. – Mena Nov 08 '17 at 17:14
0

The first thing you'll want to do is gather all the keys into a single stream:

 Stream<String> keys = Stream.of(
       fizzes.keySet().stream(), 
       buzzes.keySet().stream(), 
       foobars.keySet().stream())
    .flatMap(s -> s);

Now you can collect them into a Map of counts:

 Map<String, Long> counts = keys.collect(
     Collectors.groupingBy(Function.identity(),
     Collectors.counting()));

And you can filter the entries with counts > 1:

 Set<String> duplicates = counts.entrySet().stream()
            .filter( e -> e.getValue() > 0)
            .map(Entry::getKey)
            .collect(Collectors.toSet());

Throw your exception if this set is not empty.

slim
  • 40,215
  • 13
  • 94
  • 127