Java-Stream -使用单个Stream拆分、分组和映射来自String的数据



我有一个字符串如下:

String data = "010$$fengtai,010$$chaoyang,010$$haidain,027$$wuchang,027$$hongshan,027$$caidan,021$$changnin,021$$xuhui,020$$tianhe";

我想通过执行以下步骤将其转换为类型为Map<String,List<String>>的映射(如下所示):

  • 先用,拆分字符串,再用$$拆分字符串;
  • $$之前的子字符串作为分组数据时的Key,$$之后的子字符串需要放入一个列表中,该列表将是Map的Value。

生成的Map示例:

{ 
027=[wuchang, hongshan, caidan],
020=[tianhe],
010=[fengtai, chaoyang, haidain],
021=[changnin, xuhui]
}

我使用了一种传统的方法:

private Map<String, List<String>> parseParametersByIterate(String sensors) {
List<String[]> dataList = Arrays.stream(sensors.split(","))
.map(s -> s.split("\$\$"))
.collect(Collectors.toList());

Map<String, List<String>> resultMap = new HashMap<>();
for (String[] d : dataList) {
List<String> list = resultMap.get(d[0]);
if (list == null) {
list = new ArrayList<>();
list.add(d[1]);
resultMap.put(d[0], list);
} else {
list.add(d[1]);
}
}
return resultMap;
}

但是它看起来更复杂和冗长。因此,我想实现这个逻辑一行代码(即单个流语句)。

到目前为止我已经尝试过了

Map<String, List<String>> result =  Arrays.stream(data.split(","))
.collect(Collectors.groupingBy(s -> s.split("\$\$")[0]));

但是输出与我想要的不匹配。如何生成如上所述结构的Map ?

您只需要映射映射的值。您可以通过为Collectors.groupingBy指定第二个参数来实现:

Collectors.groupingBy(s -> s.split("\$\$")[0],
Collectors.mapping(s -> s.split("\$\$")[1],
Collectors.toList()
))

与其拆分两次,不如先拆分后分组:

Arrays.stream(data.split(","))
.map(s -> s.split("\$\$"))
.collect(Collectors.groupingBy(s -> s[0],
Collectors.mapping(s -> s[1],Collectors.toList())
));

现在输出:

{027=[wuchang, hongshan, caidan], 020=[tianhe], 021=[changnin, xuhui], 010=[fengtai, chaoyang, haidain]}

您可以从字符串中提取所需的信息,而不需要分配中间数组,并且只对字符串迭代一次并且只使用一次正则表达式引擎而不是做多个String.split()调用,并先由,分裂,然后由$$。我们可以一次得到所有需要的数据。

既然您已经在使用正则表达式(,因为解释"\s\s"需要利用正则表达式引擎),那么充分利用它们将是明智的。

Matcher.results()

我们可以定义以下模式来捕获您感兴趣的部分:

public static final Pattern DATA = // use the proper name to describe a piece of information (like "027$$hongshan") that the pattern captures
Pattern.compile("(\d+)\$\$(\w+)");

使用此模式,我们可以生成Matcher的实例并应用Java 9方法Matcher.result(),该方法生成MatchResults流。

MatchResult是一个对象,封装了捕获的字符序列的信息。我们可以使用MatchResult.group()方法访问组。

private static Map<String, List<String>> parseParametersByIterate(String sensors) {

return DATA.matcher(sensors).results() // Stream<MatchResult>
.collect(Collectors.groupingBy(
matchResult -> matchResult.group(1),     // extracting "027" from "027$$hongshan"
Collectors.mapping(
matchResult -> matchResult.group(2), // extracting "hongshan" from "027$$hongshan"
Collectors.toList())
));
}

main()

public static void main(String[] args) {
String data = "010$$fengtai,010$$chaoyang,010$$haidain,027$$wuchang,027$$hongshan,027$$caidan,021$$changnin,021$$xuhui,020$$tianhe";

parseParametersByIterate(data)
.forEach((k, v) -> System.out.println(k + " -> " + v));
}

输出:

027 -> [wuchang, hongshan, caidan]
020 -> [tianhe]
021 -> [changnin, xuhui]
010 -> [fengtai, chaoyang, haidain]

最新更新