patternjavaMinor
Obtaining all repeated characters' position in a string using Java Stream API
Viewed 0 times
obtainingstreamrepeatedallpositionjavausingcharactersapistring
Problem
Input is a string, and the method should return repeated characters in that string and the position of these characters.
I'm currently using Java 8 Stream. The problem is I have to do it by calling 2 times collectors. I am trying to find a way to combine all these operations in 1 single collector operations if possible.
Furthermore, the operation of mapping of characters and their position in a string is still performed in another method since I have not found a way to perform that.
Please give suggestion of improvement for the code:
I'm currently using Java 8 Stream. The problem is I have to do it by calling 2 times collectors. I am trying to find a way to combine all these operations in 1 single collector operations if possible.
Furthermore, the operation of mapping of characters and their position in a string is still performed in another method since I have not found a way to perform that.
Please give suggestion of improvement for the code:
public static void main(String args[]) {
String str = "acbdaghfb";
Map> map=charMaps(str);
map.forEach((k,v)->System.out.println(k+"_"+v));
}
public static Map> charMaps(String str) {
Map> maps = encodeString(str)
.stream()
.collect(Collectors.groupingBy(Pair::getKey, Collectors.mapping(Pair::getValue, Collectors.toList())));
maps = maps.entrySet()
.stream()
.filter(e -> e.getValue().size() > 1)
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
return maps;
}
public static List> encodeString(String str) {
List> pairs = new LinkedList>();
char chars[] = str.toCharArray();
for (int a = 0; a (chars[a], a));
}
return pairs;
}Solution
You do not need
This creates an
This collector groups each element of the Stream by the classifier given. In this case, the classifier is a function returning the character at the index considered. It is using the method-reference
The Stream API doesn't offer a way to do a
to remove every entry from the map where the value is a list that has a size less than 1. This works because
So all in all, you can have:
encodeString and you do not even need a Pair class. You only use this method to build a list of each index with the character of the string at that index. Instead, you can directly use an IntStream that will go over the indexes of each character of the String.Map> map =
IntStream.range(0, str.length())
.boxed()
.collect(Collectors.groupingBy(str::charAt, HashMap::new, Collectors.toList()));This creates an
IntStream going from 0 to the length of the String (excluded). We turn this primitive Stream into a Stream using boxed: we will need to box each index to store them in a list anyway, and having a Stream of objects enable us to use the built-in groupingBy(classifier, mapFactory, downstream) collector.This collector groups each element of the Stream by the classifier given. In this case, the classifier is a function returning the character at the index considered. It is using the method-reference
str::charAt (which would be equivalent to the lambda expression i -> str.charAt(i)). The downstream collector is used to collect all elements that are classified to the same key; in this case, it is simply toList() which will collect the indexes into a list. By default, groupingBy isn't specified to return a specific Map implementation (the current Oracle JDK returns a HashMap but that cannot be relied upon). Since we're going to post-process that map, giving HashMap::new as the map supplier makes sure that we end up with a HashMap.The Stream API doesn't offer a way to do a
GROUP BY HAVING, like we would like here (in SQL jargon), so 2 passes are needed. The second one can be simplified though: instead of creating a new Stream pipeline, we can usemap.values().removeIf(l -> l.size() <= 1);to remove every entry from the map where the value is a list that has a size less than 1. This works because
values() returns a view of the map, so changes to it reflect through the map. And then removeIf gives us the ability to remove the elements matching the given predicate.So all in all, you can have:
public static Map> charMaps(String str) {
Map> map =
IntStream.range(0, str.length())
.boxed()
.collect(Collectors.groupingBy(str::charAt, HashMap::new, Collectors.toList()));
map.values().removeIf(l -> l.size() <= 1);
return map;
}Code Snippets
Map<Character, List<Integer>> map =
IntStream.range(0, str.length())
.boxed()
.collect(Collectors.groupingBy(str::charAt, HashMap::new, Collectors.toList()));map.values().removeIf(l -> l.size() <= 1);public static Map<Character,List<Integer>> charMaps(String str) {
Map<Character, List<Integer>> map =
IntStream.range(0, str.length())
.boxed()
.collect(Collectors.groupingBy(str::charAt, HashMap::new, Collectors.toList()));
map.values().removeIf(l -> l.size() <= 1);
return map;
}Context
StackExchange Code Review Q#138539, answer score: 3
Revisions (0)
No revisions yet.