2

I'm looking to replace the following for loop with an elegant java 8 stream or lambda solution. Is there anything concise and efficient?

    public static void main(String[] args) {
        ArrayList<Integer> myList = new ArrayList<>( Arrays.asList( 10,-3,5));

        // add 1/2 of previous element to each element
        for(int i =1 ;i < myList.size();  ++i )
            myList.set(i, myList.get(i)+myList.get(i-1)/2);

        // myList.skip(1).forEach( e -> e + prevE/2 );  // looking for something in this spirit
    }
3
  • 9
    You try to change an element depending on some other element. But the stream API is part of Javas functional programming facility and a major concept of FP is that each element is processd independend of the others. Therefore javas stream API is not the right tool. Commented Feb 1, 2018 at 11:10
  • 1
    Try use replaceAll method Commented Feb 1, 2018 at 11:12
  • 5
    @HadiJ not really, since it requires some previous state Commented Feb 1, 2018 at 11:13

3 Answers 3

8

Your loop evaluation has a dependency to the result of the previous evaluation. It is equivalent to

for(int i = 1, value = myList.get(0); i < myList.size(); i++ ) {
    value = myList.get(i) + value/2;
    myList.set(i, value);
}

There is no real simplification by using the Stream API or lambda expressions possible. In fact, I would prefer the variant shown above, even if it’s bigger rather than smaller, as it makes clear what actually happens (and may be slightly more efficient by avoiding multiple List lookups).

It also allows you to program position independent, if you create a new List:

List<Integer> srcList = Arrays.asList(10, -3, 5), dstList = new ArrayList<>();

int value = 0;
for(Integer i: srcList) {
    value = i + value/2;
    dstList.add(value);
}
Sign up to request clarification or add additional context in comments.

4 Comments

I prefer your invariant approach, since it never change the states of the original list. :)
hi, @Holger. excuse me, after read your answer, I have another idea, how about writing a custom Spliterator? Am I overhead again?
@holi-java that’s what I mean with “no real simplification”. You can do it, but since the function is not associative, there is no way to provide efficient splitting support, so you end up with more complicated code for no real benefit.
oh, my god. I finally understand what you have said to me last days. please forgive my bad english, I admit I think it overhead from here to there, but there is no significant benefits. thanks very much.
7

There is no way to access previous element in the java stream. You might use IntStream#range but it doesn't look elegant:

IntStream.range(1, myList.size())
        .forEachOrdered(i -> myList.set(i, myList.get(i) + myList.get(i - 1) / 2));

1 Comment

Agreed, sometimes a for loop is all you need.
2

Yes you can achieve this using this line of code:

IntStream.range(1,myList.size())
    .forEachOrdered(i->myList.set(i,(myList.get(i)+myList.get(i-1)/2)));
System.out.println(myList);

4 Comments

@Eugene If I add parallel(), for this particular list, I achieve the same result.
You're right. Updated my answer. I haven't tested, i was just writing code from my tablet.
So you think this is more elegant?
@Jean-BaptisteYunès IMHO, the regular for loop looks better, but I only showed to the OP the way we can achieve this using forEach() method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.