我正在尝试制作StringTokenizer
的流媒体版本,但我有一些问题正确终止流。
public Stream<String> tokenize(String text) {
StringTokenizer tokenizer = new StringTokenizer(text);
return Stream.generate(tokenizer::nextToken)
.takeWhile(s -> tokenizer.hasMoreTokens());
}
但是当我运行这段代码时,最后一个令牌丢失了:
Stream<String> tokens = new DefaultTokenizer().tokenize(" a b c dte fn");
tokens.forEach(System.out::println);
结果:
a
b
c
d
e
我已经尝试使用Stream.iterate
这样:Stream.iterate(tokenizer.nextToken(), s -> tokenizer.hasMoreTokens(), s -> tokenizer.nextToken())
,但结果是相同的。
我显然终止流只要谓词hasMoreTokens
通过,但我需要采取最后一个元素之后,我怎么能在采取最后一个元素后终止?
就像所有其他答案一样,我会避免使用StringTokenizer
。
为了避免处理整个字符串,您可以使用Pattern.splitAsStream(String)
而不是通常的split
方法。
所以在你的情况下,这样的东西应该有类似的结果使用StringTokenizer
。(字符串是split at any (non-empty) sequence of white-space
)
Pattern.compile("\s+").splitAsStream(text)
一个小的调整,你的供应商返回null在结束和takeWhile(nonNull)将允许你继续使用Tokeniser:
public Stream<String> tokenize(String text) {
StringTokenizer tokenizer = new StringTokenizer(text);
return Stream.generate(() -> tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null)
.takeWhile(Objects::nonNull);
}
StringTokenizer
是一个遗留类,您可能想要考虑其他问题。编辑:我从这个答案中删除了我的解决方案,因为magicmn的答案要好得多。
编辑:你的代码出了什么问题?你试过:
Stream.iterate(tokenizer.nextToken(), s -> tokenizer.hasMoreTokens(), s -> tokenizer.nextToken())
这里发生的事情是流使用s -> tokenizer.hasMoreTokens()
来确定是否在流中包含当前令牌s
。当它对最后一个令牌f
执行此操作时,它显然已经从StringTokenizer
中提取了它,因此StringTokenizer
为hasMoreTokens()
返回false
,并且f
不包含在流中。
你试过吗?
return Stream.generate(tokenizer::nextToken).limit((long) tokenizer.countTokens());
用总令牌限制流
StringTokenizer是一个不建议再使用的遗留类。下面是一个使用split()
代替
public Stream<String> tokenize(String text) {
String[] split = text.split("\s");
return Arrays.stream(split).map(String::trim).filter( s -> (!s.isEmpty()));
}
或者更紧凑的
public Stream<String> tokenize(String text) {
return Arrays.stream(text.split("\s"))
.map(String::trim)
.filter( s -> (!s.isEmpty()));
}