Java 8 streams for string manipulation

I want to perform several tasks on one line. I need to get a string and extract different substrings using a separator ( "/" ), then cancel the list of substrings and finally join them using another separator ( "." ) So that /tmp/test/hello/world/ turns into : world.hello.test.tmp

Using Java 7, the code is as follows:

 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()); 

I was wondering how can I do the same using Java 8 threads and the join function that it has for strings

+6
source share
3 answers

The easiest way is to collect the terms into a list, cancel the list and join the new delimiter:

 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); 

You can do this without collecting an intermediate list, but it will be less readable and not worth the trouble for practical purposes.

Another issue to keep in mind is that your lines look like paths. It is usually better to use the Path class instead of manually splitting the "/". Here's how you do it (this approach also demonstrates how to use IntStream over indices to stream over the list back):

 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(".")); 

This will have an advantage regardless of the OS.

+7
source

If you don't need an intermediate list and just want to join String other way around:

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

Or just use .reduce((s1, s2) -> s2 + '.' + s1); , since it probably reads as String.join(".", s2, s1); (thanks to Holger for the suggestion).

From now on, you can do one of the following:

 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 

Another way to use StreamSupport and Spliterator (inspired by Mishas' suggestion to use Path ):

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

Of course, you can simplify it by omitting the intermediate Optional object and immediately name your desired method:

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

in the last example, you would add the following static import:

 import static java.lang.System.out; import static java.nio.file.Paths.get; import static java.util.stream.StreamSupport.stream; 
+4
source

Your Java 7 code is not what Id calls a direct solution.
This is how I will implement it in 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); 

and thinking about it again, this is also how Id implements it in Java 8, since using the Stream API does not really improve this operation.

+2
source

Source: https://habr.com/ru/post/1014865/


All Articles