Search code examples
javasetjava-stream

How can I bring the modification of a list from the values ​of a set to java stream?


I am currently working with Java and practicing my programming logic and as a good practice I am trying to apply stream in some operations with Java collections.

In this case I have a method that receives a string and a set list, the string must be taken to a list to replace its characters according to the values ​​found in the Set, once replaced they are taken back to string and It is returned:

private static String wordToGuess(String word,List<Integer> valueUniqueToRemplace){
    List<String> wordToList=new ArrayList<>(Arrays.asList(word.split("")));
    for (Integer integer : valueUniqueToRemplace) {
        wordToList.set(integer, "_");
    }
    return wordToList.stream().map(String::valueOf).collect(Collectors.joining(""));
}

How could I replace the for loop and what is done within it with a stream function?


Solution

  • as a good practice I am trying to apply stream in some operations with Java collections

    Practice is good but also notice when it makes sense to use a certain language construct and when not. Not every task is suitable to be solved with streams. If I see it correctly, you are trying to replace certain indices of a string with underscores. This is not clear, at least at first glance at your method. I would have written something like below, which is easier to understand:

    private static String wordToGuess1(String word,List<Integer> valueUniqueToReplace){
        char[] chars = word.toCharArray();
        valueUniqueToReplace.forEach(i -> chars[i] = '_');
        return String.valueOf(chars);
    }
    

    or

    private static String wordToGuess2(String word,List<Integer> valueUniqueToReplace){
        StringBuilder sb = new StringBuilder(word);
        valueUniqueToReplace.forEach(i -> sb.setCharAt(i, '_'));
        return sb.toString();
    }
    

    There are certainly better approaches but spontaneously I think of the following alternatives with streams:

    private static String wordToGuess3(String word,List<Integer> valueUniqueToReplace){
        return IntStream.range(0, word.length())
                        .map(i -> valueUniqueToReplace.contains(i) ? '_' : word.charAt(i))
                        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
                        .toString();
    }
    
    private static String wordToGuess4(String word,List<Integer> valueUniqueToReplace){
        return valueUniqueToReplace.stream()
                                   .reduce(new StringBuilder(word),
                                           (sb, i) -> sb.replace(i, i + 1, "_"),
                                           (a,b) -> a)
                                   .toString();
    }
    

    Decide for yourself what is more readable, I for one would prefer the non stream approach