Java有限流终止



我正在尝试制作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中提取了它,因此StringTokenizerhasMoreTokens()返回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()));
}

最新更新