我想在一个字符串上执行多个任务。
我需要使用分隔符("/"
)获取字符串并提取不同的子字符串,然后反转子字符串列表,最后使用另一个分隔符("."
)连接它们,使{{1}会变成:/tmp/test/hello/world/
使用Java 7代码如下:
world.hello.test.tmp
我想知道如何使用Java 8流和它对字符串的连接函数做同样的事情
答案 0 :(得分: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);
你可以在不收集中间列表的情况下进行,但它的可读性较差,不值得为实际目的而烦恼。
要考虑的另一个问题是您的字符串似乎是路径。通常最好使用Path
类而不是手动分割“/”。以下是您将如何执行此操作(此方法还演示了如何使用IntStream
对索引向后流式传输列表):
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("."));
这将具有独立于操作系统的优势。
答案 1 :(得分:5)
如果您不想要中间列表,只想反向加入String
:
String delimiter = ".";
Optional<String> result = Pattern.compile("/")
.splitAsStream(str)
.filter(s -> ! s.isEmpty())
.reduce((s, s2) -> String.join(delimiter, s2, s));
或者只使用.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
使用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);
当然,您可以通过省略中间Optional
- 对象来简化它,并立即调用您想要的方法:
stream(get(str).spliterator(), false)
.map(Path::getFileName)
.map(Path::toString)
.reduce((s, s2) -> s2 + '.' + s)
.ifPresent(out::println); // orElse... orElseThrow
在上一个示例中,您将添加以下静态导入:
import static java.lang.System.out;
import static java.nio.file.Paths.get;
import static java.util.stream.StreamSupport.stream;
答案 2 :(得分:2)
您的Java 7代码不是我称之为直接解决方案的代码 这就是我将如何在Java 7中实现它:
String str = "/tmp/test/";
StringBuilder sb = new StringBuilder(str.length()+1);
for(int s=str.lastIndexOf('/'), e=str.length(); e>=0; e=s, s=str.lastIndexOf('/', e-1)) {
if(s+1<e) sb.append(str, s+1, e).append('.');
}
if(sb.length()>0) sb.setLength(sb.length() - 1);
System.out.println("result " + sb);
再次考虑它,这也是我在Java 8中实现它的方式,因为使用Stream API并没有真正改善这种操作。
答案 3 :(得分:0)
您可以这样写:
String newPath = Arrays.asList(path.split("/")).stream()
.filter(x -> !x.isEmpty())
.reduce("",(cc,ss)->{
if(!cc.isEmpty())
return ss+"."+cc;
else return ss;
},(s1,s2)->s2+s1);
过滤器消除了第一个反斜杠,并且reduce方法必须控制是否还有其他最后一个空字符串。