Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use stream to do one thing on elements not-in-index-range and another on yes-in-range?

This question

Is there a concise way to iterate over a stream with indices in Java 8?

describes how to drive one stream based on another stream of indexes. My goal is to take an array of strings

one two three four five six seven eight

and all-cap the elements having an array index between 2 and 5:

one two THREE FOUR FIVE SIX seven eight

I've figured out how to do this, but my instinct says there should be a more elegant way of going about it. In the below example, the first stream filters out all elements except those in range. It's not directly what I want, of course, but it's similar in how it uses a stream to filter by index-range. The second alters the array elements in place, capitalizing the ones I want.

I don't like how it alters the array in place, and how it requires two streams. Is there a better way?

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

import java.util.Arrays;
import java.util.stream.IntStream;

public class UpperCaseElementsInIndexRangeViaIntStream {

   public static void main(String[] ignored) {
      final String input = "one two three four five six seven eight";
      final int MIN_IDX = 2;
      final int MAX_IDX = 5;
      final String[] splits = input.split(" ");

      //Filter only those in range

      String output = IntStream.range(0, splits.length).
            filter(idx -> MIN_IDX <= idx && idx <= MAX_IDX).
            mapToObj(i -> splits[i]).
            collect(joining(" "));

      System.out.println(output);

      //Change the array in place, capitalizing only those in range

      IntStream.range(0, splits.length).forEach(idx -> {
         final String split = splits[idx];
         splits[idx] = (MIN_IDX <= idx && idx <= MAX_IDX)
                       ? split.toUpperCase() : split;
      });

      output = Arrays.stream(splits).collect(joining(" "));

      System.out.println(output);
   }
}

Output:

three four five six
one two THREE FOUR FIVE SIX seven eight
like image 896
aliteralmind Avatar asked Mar 16 '23 03:03

aliteralmind


2 Answers

Here's a solution without Stream. Use subList to get a view of the range you want as a List. Then use replaceAll to transform the elements in place.

List<String> all = Arrays.asList(splits);
all.subList(MIN_IDX, MAX_IDX + 1).replaceAll(String::toUpperCase);
// use 'all' as needed

Note that the second parameter of replaceAll is an exclusive index value.

like image 59
Sotirios Delimanolis Avatar answered Apr 08 '23 05:04

Sotirios Delimanolis


Here's the Stream API-based solution which is parallel friendly:

final String[] splits = input.split(" ");

String output = IntStream.range(0, splits.length).parallel()
    .mapToObj(idx -> MIN_IDX <= idx && idx <= MAX_IDX ?
              splits[idx].toUpperCase() : splits[idx])
    .collect(Collectors.joining(" "));

Note that I'm posting this answer just for the completeness. Streams are not well-suitable for indexed operations. I vote for @SotiriosDelimanolis solution, it's really simple and elegant.

like image 31
Tagir Valeev Avatar answered Apr 08 '23 06:04

Tagir Valeev