1

Let says i have a list of strings and i want to use those strings as input to a fluent builder.

List<String> scripts;

//initialize list

ScriptRunnerBuilder scriptRunnerBuilder = new ScriptRunnerBuilder();

BiFunction<String,ScriptRunnerBuilder,ScriptRunnerBuilder> addScript = 
(script,builder) -> builer.addScript(script);

scriptRunnerBuilder = scripts.stream.map(script -> 
addScript.apply(script,scriptRunnerBuilder)).......

scriptRunnerBuilder.build();

which terminal operation can i use so that the addScript function gets called for all elements in the list?

The issue is that the ScriptRunnerBuilder is immutable whereby ScriptRunnerBuilder.addScript() returns a new ScriptRunnerBuilder object rather than modifying existing – so i can't just us a foreach.

My intentions are to carry the result of the addScript() call and use that as input for the next element in the stream

Claudiga
  • 427
  • 1
  • 4
  • 15
  • scripts.forEach(script -> addScript.apply(script,scriptRunnerBuilder)); – Beri Mar 21 '19 at 11:40
  • Yes, but does it has to return? You have a reference to the builder. It does not have to return anything. – Beri Mar 21 '19 at 11:42
  • I know i could use reduce operation but that is unnecessary as we are not combining results – Claudiga Mar 21 '19 at 11:42

3 Answers3

5

In simplest way this should:

// create your builder
ScriptRunnerBuilder builder = new ScriptRunnerBuilder();

// add all scripts
scripts.forEach(script-> builder.addScript(script))

build results
scriptRunnerBuilder.build();

Because builder aggregates all data, and you have created it outside forEach lambda, you can access it directly. This will lead to less code and same result.

Or as @Holger suggested:

scripts.forEach(builder::addScript);
Beri
  • 11,470
  • 4
  • 35
  • 57
  • 3
    Or even `scripts.forEach(builder::addScript);` – Holger Mar 21 '19 at 12:20
  • The issue is that the ScriptRunnerBuilder is immutable whereby ScriptRunnerBuilder.addScript() returns a new ScriptRunnerBuilder object rather than modifying existing – Claudiga Mar 21 '19 at 13:16
  • @Claudiga then use an ordinary loop, `for(String s: scripts) builder = builder.addScript(s);` – Holger Mar 21 '19 at 15:19
  • @Holger yea that sounds like the only clean way to do it. I just wanted to see how it could be done using streams – Claudiga Mar 21 '19 at 15:54
2

Use forEach instead of map and don't assign the result of the stream anymore

scripts.forEach(script -> addScript.apply(script,scriptRunnerBuilder));
Yassin Hajaj
  • 21,337
  • 9
  • 51
  • 89
1

i could use reduce operation but that is unnecessary as we are not combining results

Combining is exactly what you are doing. You combine all scripts from List<String> to ScriptRunnerBuilder aren't you?

I agree that the @Beri's solution without stream probably is the simplest. But also there is a way with reduce(identity, accumulator, combiner) method where you don't need to create ScriptRunnerBuilder before:

ScriptRunnerBuilder builder = scripts.stream()
        .reduce(new ScriptRunnerBuilder(), ScriptRunnerBuilder::addScript, (b1, b2) -> b1);

See more: Why is a combiner needed for reduce method that converts type in java 8

Update To not to rely on the fact that combiner not being invoked for sequential stream and to make it works with parallel one you have to implement the real combiner.

If you could add an overrided method addScript(ScriptRunnerBuilder otherBuilder) then the reduce will look like:

.reduce(new ScriptRunnerBuilder(), ScriptRunnerBuilder::addScript, 
        ScriptRunnerBuilder::addScript)
Ruslan
  • 6,090
  • 1
  • 21
  • 36