拆分 java.util.stream.Stream
Split java.util.stream.Stream
我有一个包含 URL 和电子邮件的文本文件。我需要从文件中提取所有这些。每个 URL 和电子邮件都可以找到不止一次,但结果不应包含重复项。
我可以使用以下代码提取所有 URLs:
Files.lines(filePath).
.map(urlPattern::matcher)
.filter(Matcher::find)
.map(Matcher::group)
.distinct();
我可以使用以下代码提取所有电子邮件:
Files.lines(filePath).
.map(emailPattern::matcher)
.filter(Matcher::find)
.map(Matcher::group)
.distinct();
我可以提取所有 URL 和电子邮件读取 Files.lines(filePath)
返回的流一次吗?
类似于将行流拆分为 URL 流和电子邮件流。
因为你不能重用 Stream,我认为唯一的选择是 "do it manually"。
File.lines(filePath).forEach(s -> /** match and sort into two lists */ );
如果有其他解决方案,我很乐意了解它!
你可以使用 partitioningBy
收集器,虽然它仍然不是很优雅的解决方案。
Map<Boolean, List<String>> map = Files.lines(filePath)
.filter(str -> urlPattern.matcher(str).matches() ||
emailPattern.matcher(str).matches())
.distinct()
.collect(Collectors.partitioningBy(str -> urlPattern.matcher(str).matches()));
List<String> urls = map.get(true);
List<String> emails = map.get(false);
如果你不想两次应用正则表达式,你可以使用中间对对象(例如,SimpleEntry
):
public static String classify(String str) {
return urlPattern.matcher(str).matches() ? "url" :
emailPattern.matcher(str).matches() ? "email" : null;
}
Map<String, Set<String>> map = Files.lines(filePath)
.map(str -> new AbstractMap.SimpleEntry<>(classify(str), str))
.filter(e -> e.getKey() != null)
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e -> e.getValue(), Collectors.toSet())));
使用我的免费 StreamEx 库,最后一步会更短:
Map<String, Set<String>> map = StreamEx.of(Files.lines(filePath))
.mapToEntry(str -> classify(str), Function.identity())
.nonNullKeys()
.grouping(Collectors.toSet());
总体问题应该是:您为什么只想流式传输一次?
提取 URL 和提取电子邮件是不同的操作,因此应该在它们自己的流操作中处理。即使底层流源包含数十万条记录,迭代的时间与映射和过滤操作相比可以忽略不计。
您应该考虑的唯一可能的性能问题是 IO 操作。因此,最干净的解决方案是只读取文件一次,然后在结果集合上流式传输两次:
List<String> allLines = Files.readAllLines(filePath);
allLines.stream() ... // here do the URLs
allLines.stream() ... // here do the emails
当然这需要一些内存。
您可以在 Collector
:
中执行匹配
Map<String,Set<String>> map=Files.lines(filePath)
.collect(HashMap::new,
(hm,line)-> {
Matcher m=emailPattern.matcher(line);
if(m.matches())
hm.computeIfAbsent("mail", x->new HashSet<>()).add(line);
else if(m.usePattern(urlPattern).matches())
hm.computeIfAbsent("url", x->new HashSet<>()).add(line);
},
(m1,m2)-> m2.forEach((k,v)->m1.merge(k, v,
(s1,s2)->{s1.addAll(s2); return s1;}))
);
Set<String> mail=map.get("mail"), url=map.get("url");
请注意,这很容易适用于在一行中查找多个匹配项:
Map<String,Set<String>> map=Files.lines(filePath)
.collect(HashMap::new,
(hm,line)-> {
Matcher m=emailPattern.matcher(line);
while(m.find())
hm.computeIfAbsent("mail", x->new HashSet<>()).add(m.group());
m.usePattern(urlPattern).reset();
while(m.find())
hm.computeIfAbsent("url", x->new HashSet<>()).add(m.group());
},
(m1,m2)-> m2.forEach((k,v)->m1.merge(k, v,
(s1,s2)->{s1.addAll(s2); return s1;}))
);
我有一个包含 URL 和电子邮件的文本文件。我需要从文件中提取所有这些。每个 URL 和电子邮件都可以找到不止一次,但结果不应包含重复项。 我可以使用以下代码提取所有 URLs:
Files.lines(filePath).
.map(urlPattern::matcher)
.filter(Matcher::find)
.map(Matcher::group)
.distinct();
我可以使用以下代码提取所有电子邮件:
Files.lines(filePath).
.map(emailPattern::matcher)
.filter(Matcher::find)
.map(Matcher::group)
.distinct();
我可以提取所有 URL 和电子邮件读取 Files.lines(filePath)
返回的流一次吗?
类似于将行流拆分为 URL 流和电子邮件流。
因为你不能重用 Stream,我认为唯一的选择是 "do it manually"。
File.lines(filePath).forEach(s -> /** match and sort into two lists */ );
如果有其他解决方案,我很乐意了解它!
你可以使用 partitioningBy
收集器,虽然它仍然不是很优雅的解决方案。
Map<Boolean, List<String>> map = Files.lines(filePath)
.filter(str -> urlPattern.matcher(str).matches() ||
emailPattern.matcher(str).matches())
.distinct()
.collect(Collectors.partitioningBy(str -> urlPattern.matcher(str).matches()));
List<String> urls = map.get(true);
List<String> emails = map.get(false);
如果你不想两次应用正则表达式,你可以使用中间对对象(例如,SimpleEntry
):
public static String classify(String str) {
return urlPattern.matcher(str).matches() ? "url" :
emailPattern.matcher(str).matches() ? "email" : null;
}
Map<String, Set<String>> map = Files.lines(filePath)
.map(str -> new AbstractMap.SimpleEntry<>(classify(str), str))
.filter(e -> e.getKey() != null)
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e -> e.getValue(), Collectors.toSet())));
使用我的免费 StreamEx 库,最后一步会更短:
Map<String, Set<String>> map = StreamEx.of(Files.lines(filePath))
.mapToEntry(str -> classify(str), Function.identity())
.nonNullKeys()
.grouping(Collectors.toSet());
总体问题应该是:您为什么只想流式传输一次?
提取 URL 和提取电子邮件是不同的操作,因此应该在它们自己的流操作中处理。即使底层流源包含数十万条记录,迭代的时间与映射和过滤操作相比可以忽略不计。
您应该考虑的唯一可能的性能问题是 IO 操作。因此,最干净的解决方案是只读取文件一次,然后在结果集合上流式传输两次:
List<String> allLines = Files.readAllLines(filePath);
allLines.stream() ... // here do the URLs
allLines.stream() ... // here do the emails
当然这需要一些内存。
您可以在 Collector
:
Map<String,Set<String>> map=Files.lines(filePath)
.collect(HashMap::new,
(hm,line)-> {
Matcher m=emailPattern.matcher(line);
if(m.matches())
hm.computeIfAbsent("mail", x->new HashSet<>()).add(line);
else if(m.usePattern(urlPattern).matches())
hm.computeIfAbsent("url", x->new HashSet<>()).add(line);
},
(m1,m2)-> m2.forEach((k,v)->m1.merge(k, v,
(s1,s2)->{s1.addAll(s2); return s1;}))
);
Set<String> mail=map.get("mail"), url=map.get("url");
请注意,这很容易适用于在一行中查找多个匹配项:
Map<String,Set<String>> map=Files.lines(filePath)
.collect(HashMap::new,
(hm,line)-> {
Matcher m=emailPattern.matcher(line);
while(m.find())
hm.computeIfAbsent("mail", x->new HashSet<>()).add(m.group());
m.usePattern(urlPattern).reset();
while(m.find())
hm.computeIfAbsent("url", x->new HashSet<>()).add(m.group());
},
(m1,m2)-> m2.forEach((k,v)->m1.merge(k, v,
(s1,s2)->{s1.addAll(s2); return s1;}))
);