用于字符串操作的Java 8 Streams

Hos*_*ein 7 java-8 java-stream

我想在一个字符串上执行多个任务.我需要使用分隔符("/")获取一个字符串并提取不同的子字符串,然后反转子字符串列表,最后使用另一个分隔符(".")/tmp/test/hello/world/将它们连接起来,这样就会变成:world.hello.test.tmp

使用Java 7代码如下:

String str ="/tmp/test/";
List<String> elephantList = new ArrayList<String>(Arrays.asList(str.split("/")));

StringBuilder sb = new StringBuilder();
for (int i=elephantList.size()-1; i>-1; i--) {
    String a = elephantList.get(i);
    if (a.equals(""))
    {
        elephantList.remove(i);
    }
    else
    {
        sb.append(a);
        sb.append('.');
    }
}
sb.setLength(sb.length() - 1);
System.out.println("result" + elephantList + "   " + sb.toString());
Run Code Online (Sandbox Code Playgroud)

我想知道如何使用Java 8流和它对字符串的连接功能做同样的事情

Mis*_*sha 10

最直接的方法是将术语收集到列表中,反转列表并加入新的分隔符:

import static java.util.stream.Collectors.toCollection;

List<String> terms = Pattern.compile("/")
        .splitAsStream(str)
        .filter(s -> !s.isEmpty())
        .collect(toCollection(ArrayList::new));

Collections.reverse(terms);

String result = String.join(".", terms);
Run Code Online (Sandbox Code Playgroud)

你可以在不收集中间列表的情况下完成它,但它的可读性较差,不值得为实际目的而烦恼.

要考虑的另一个问题是您的字符串似乎是路径.通常最好使用Path类而不是手动分割"/".以下是您将如何执行此操作(此方法还演示了如何使用IntStreamover索引向后流式传输列表):

Path p = Paths.get(str);

result = IntStream.rangeClosed(1, p.getNameCount())
        .map(i -> p.getNameCount() - i)  // becomes a stream of count-1 to 0
        .mapToObj(p::getName)
        .map(Path::toString)
        .collect(joining("."));
Run Code Online (Sandbox Code Playgroud)

这将具有独立于操作系统的优点.

  • 如果任务确实是要替换默认`FileSystem`的分隔符,而不是按问题所要求的'/'替换,则使用`Path`是独立于操作系统的。如果我们在谈论例如URI部分或ZIP文件条目,那么这可能不是正确的解决方案(实际上是“添加”了os依赖项)。 (2认同)

Rol*_*and 5

如果您不需要中间列表,而只想String反向加入:

String delimiter = ".";
Optional<String> result = Pattern.compile("/")
                                 .splitAsStream(str)
                                 .filter(s -> ! s.isEmpty())
                                 .reduce((s, s2) -> String.join(delimiter, s2, s));
Run Code Online (Sandbox Code Playgroud)

或者只是使用.reduce((s1, s2) -> s2 + '.' + s1);它可能具有的可读性String.join(".", s2, s1);(感谢Holger的建议)。

从那时起,您可以执行以下任一操作:

result.ifPresent(System.out::println); // print the result
String resultAsString = result.orElse(""); // get the value or default to empty string
resultAsString = result.orElseThrow(() -> new RuntimeException("not a valid path?")); // get the value or throw an exception
Run Code Online (Sandbox Code Playgroud)

使用StreamSupport和的另一种方式Spliterator(受Mishas建议使用的启发Path):

Optional<String> result = StreamSupport.stream(Paths.get(str).spliterator(), false)
                                       .map(Path::getFileName)
                                       .map(Path::toString)
                                       .reduce((s, s2) -> s2 + '.' + s);
Run Code Online (Sandbox Code Playgroud)

当然,您可以通过省略中间对象来简化它,Optional而只需立即调用所需的方法即可:

stream(get(str).spliterator(), false)
    .map(Path::getFileName)
    .map(Path::toString)
    .reduce((s, s2) -> s2 + '.' + s)
    .ifPresent(out::println); // orElse... orElseThrow
Run Code Online (Sandbox Code Playgroud)

在上一个示例中,您将添加以下静态导入:

import static java.lang.System.out;
import static java.nio.file.Paths.get;
import static java.util.stream.StreamSupport.stream;
Run Code Online (Sandbox Code Playgroud)