It's not entirely clear to me what's required by "using only the Set
interface" but I'll assume that this means that the duplicate characters are to be returned in a Set
. There are several ways of doing this. The first is the straightforward loop over the chars of the input string. It takes advantage of the feature of Set.add
which returns true
if the set was modified and false
if it wasn't; that means that an add
operation that returns false
is a duplicate.
static Set<Character> dups0(String input) {
Set<Character> dups = new HashSet<>();
Set<Character> seen = new HashSet<>();
for (char ch : input.toCharArray()) {
if (! seen.add(ch)) {
dups.add(ch);
}
}
return dups;
}
There's a streamy way to do this, which is essentially the same thing expressed in stream form:
static Set<Character> dups1(String input) {
Set<Character> seen = new HashSet<>();
return input.chars()
.mapToObj(ch -> (char)ch)
.filter(ch -> !seen.add(ch))
.collect(toSet());
}
Some people might find this distasteful, as its filter operation performs side effects. In addition, if this is run in parallel, the result would need to be something like a ConcurrentHashMap.newKeySet
.
An alternative is to generate a frequency table of characters and remove all the entries that occur just once:
static Set<Character> dups2(String input) {
Map<Character, Long> map = input.chars()
.mapToObj(i -> (char)i)
.collect(groupingBy(ch -> ch, HashMap::new, counting()));
map.values().removeIf(v -> v == 1);
return map.keySet();
}
Note that this uses a collections bulk mutation operation on the values-collection view of the map. To ensure that the map is mutable, I've used the three-arg overload of groupingBy
to specify the map's implementation type.
If you don't like mutation, there's a pure-streams way to do this:
static Set<Character> dups3(String input) {
Map<Character, Long> map = input.chars()
.mapToObj(i -> (char)i)
.collect(groupingBy(ch -> ch, counting()));
return map.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(toSet());
}