1

So I've got a list as follows, with the lambda expression:

ArrayList<String> array = new ArrayList<>();
...
StringBuffer buf = new StringBuffer("");
array.stream().forEach((item) -> {
    buf.append("Counter here: " + item.toString() + "\n");
});
return buf.toString();

But i can't instantiate and modify a variable within the forEach lambda expression because it has to be final. Is there a way i can do this?

madcrazydrumma
  • 1,847
  • 3
  • 20
  • 38
  • 1
    I don't understand. Do you want the index of each element? – Savior Apr 18 '16 at 14:17
  • Yeah or just a counter like '0: ...' and '1: ...' – madcrazydrumma Apr 18 '16 at 14:17
  • 1
    MutableInteger is available in apache-lang that has less overhead than AtomicInteger. Also avoid using StringBuffer as a local variable use StringBuilder (StringBuffer is synchronized and StringBuilder is not, unless you are going to update that buffer with multiple threads you are incurring the extra cost without benefit). – AlexC Apr 18 '16 at 14:21

2 Answers2

2

Use atomics for this:

AtomicInteger cnt = new AtomicInteger(0);
StringBuilder buf = new StringBuilder("");
array.stream().forEach((item) -> {
    buf.append("Counter here: " + cnt.incrementAndGet() + "\n");
});
return buf.toString();
Alex
  • 7,460
  • 2
  • 40
  • 51
  • Wouldnt this code throw error for making `buf` `final` or _effectively final_ ?? – Lalit Rao Apr 19 '16 at 11:56
  • 1
    @LalitRao `cnt` and `buf` are effectively final, no error here. It'll work fine on Java 8. – Alex Apr 19 '16 at 11:59
  • Would u please explain me this in detail? I mean we are mutating the state of `buf` inside lambda right? So how it remains effectively final then ? – Lalit Rao Apr 19 '16 at 12:04
  • 1
    @LalitRao In few words: you're allowed to modify internal state of the objects you're referencing from lambda expression. But you're not allowed to change the object references themself, e.g. `buf.toString()` and `cnt.incrementAndGet()` are allowed but `buf = buf.append("test")` or for example `i = i + 1`, `i++` are not – Alex Apr 19 '16 at 12:25
  • Thanks mate :) Well explained – Lalit Rao Apr 19 '16 at 12:28
0

Use an int[] of size 1 for the counter:

ArrayList<String> array = new ArrayList<>();
int[] count = {0};
return array.stream().map(item -> count[0]++ + ":" + item)
  .collect(Collectors.joining("\n", "", "\n"));

The int[] is effectively final. It's contents don't have to be.

Note how you don't even need the StringBuilder if you use the joining() collector.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • ``I wish there was a feature that allowed folks to mark the place in the answer that is "not useful" when downvoting`` – Bohemian Apr 18 '16 at 14:42
  • 2
    `` I wish moderators and Java gold badge holders could vote to close duplicates appropriately.`` – Savior Apr 18 '16 at 14:47
  • @Pillar hmmm, well this answer isn't in the dupe, but ok.... – Bohemian Apr 18 '16 at 14:52
  • Isn’t the policy to add the answer there instead of not closing a dupe question? Anyway, you should consider that there might be a good reason for such an answer not being there… – Holger Apr 18 '16 at 16:42