如何使用 Stream 从 Java 中的字符串中提取三元组
how to extract a trigrams from a string in Java with Stream
假设我有一个 String = "abcabc"。三字母组都是三个字母的唯一连续组,在这种情况下
abc
bca
cab
我想提取它们并将它们放入地图中,最好是对它们进行计数。
所以我会得到
abc, 2
bca, 1
cab, 1
我想用 Streams 来做,而不是用循环。
我的想法是
- 将字符串转换为 HashMap:(索引,3 个字符的字符串)
- 对地图值流进行收集、分组和计数
但我无法将其放入代码中...
我想到了这样的东西:
String testString = "abcabc";
//I can't get point 1...this doesn't compile
Map trigrams = IntStream.range(0, testString.length()-2).collect(Collectors.toMap(i->i, testString.substring(i,i+3));
//point 2 seems to work
//Map<Integer,String> trigrams = Map.of(0,"abc", 1, "bca",2,"cab",3,"abc");
List<String> trigramsList = trigrams.values().stream().collect(Collectors.toList());
Map<String, Long> result = trigramsList.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
另外我对IntStream不太信服,也许还有别的办法。
这可以稍微简化。
import java.util.Map;
import java.util.function.Function;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.*;
public class Main {
public static void main(String[] args) {
String s = "a";
Map<String, Long> map = IntStream.range(0, s.length() - 2)
.mapToObj(i -> s.substring(i, i + 3))
.collect(groupingBy(Function.identity(), counting()));
System.out.println(map);
}
}
但是,请记住,如果第二个索引大于字符串的长度或第二个索引小于第一个,substring()
方法将抛出异常。这仅适用于 IntStream.range()
按升序生成数字。所以,IntStream.range(0, -2)
returns 一个空流。
如果您明确检查字符串中至少有 3 个字符会更好。
一种可能是使用 Guava Mutliset:
Mutliset trigrams = IntStream.range(0, testString.length()-2)
.mapToObj(i->testString.substring(i,i+3))
.collect(Collectors.toCollection(Mutltiset::new));
如果您不想使用 Google 库,可以这样做:
Map trigrams = IntStream.range(0, testString.length()-2)
.mapToObj(i->testString.substring(i,i+3))
.collect(Collectors.groupingBy(x->x, Collectors.counting()));
流很可能:
String testString = "abcabc";
Map<String, Long> trigramCnt =
// range through index 0-3
IntStream.rangeClosed(0, testString.length() - 3)
// create substrings abc (idx 0-2), bca (idx 1-3), cab (idx 2-4), abc (idx 3-5)
.mapToObj(i -> testString.substring(i, i + 3))
// count them
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(trigramCnt);
//-> {bca=1, abc=2, cab=1}
假设我有一个 String = "abcabc"。三字母组都是三个字母的唯一连续组,在这种情况下
abc
bca
cab
我想提取它们并将它们放入地图中,最好是对它们进行计数。 所以我会得到
abc, 2
bca, 1
cab, 1
我想用 Streams 来做,而不是用循环。
我的想法是
- 将字符串转换为 HashMap:(索引,3 个字符的字符串)
- 对地图值流进行收集、分组和计数
但我无法将其放入代码中...
我想到了这样的东西:
String testString = "abcabc";
//I can't get point 1...this doesn't compile
Map trigrams = IntStream.range(0, testString.length()-2).collect(Collectors.toMap(i->i, testString.substring(i,i+3));
//point 2 seems to work
//Map<Integer,String> trigrams = Map.of(0,"abc", 1, "bca",2,"cab",3,"abc");
List<String> trigramsList = trigrams.values().stream().collect(Collectors.toList());
Map<String, Long> result = trigramsList.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
另外我对IntStream不太信服,也许还有别的办法。
这可以稍微简化。
import java.util.Map;
import java.util.function.Function;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.*;
public class Main {
public static void main(String[] args) {
String s = "a";
Map<String, Long> map = IntStream.range(0, s.length() - 2)
.mapToObj(i -> s.substring(i, i + 3))
.collect(groupingBy(Function.identity(), counting()));
System.out.println(map);
}
}
但是,请记住,如果第二个索引大于字符串的长度或第二个索引小于第一个,substring()
方法将抛出异常。这仅适用于 IntStream.range()
按升序生成数字。所以,IntStream.range(0, -2)
returns 一个空流。
如果您明确检查字符串中至少有 3 个字符会更好。
一种可能是使用 Guava Mutliset:
Mutliset trigrams = IntStream.range(0, testString.length()-2)
.mapToObj(i->testString.substring(i,i+3))
.collect(Collectors.toCollection(Mutltiset::new));
如果您不想使用 Google 库,可以这样做:
Map trigrams = IntStream.range(0, testString.length()-2)
.mapToObj(i->testString.substring(i,i+3))
.collect(Collectors.groupingBy(x->x, Collectors.counting()));
流很可能:
String testString = "abcabc";
Map<String, Long> trigramCnt =
// range through index 0-3
IntStream.rangeClosed(0, testString.length() - 3)
// create substrings abc (idx 0-2), bca (idx 1-3), cab (idx 2-4), abc (idx 3-5)
.mapToObj(i -> testString.substring(i, i + 3))
// count them
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(trigramCnt);
//-> {bca=1, abc=2, cab=1}