4

I have the following code for reading lines of a file:

String fileName = "dataset/ANC-all-count.txt";
Integer i=0;
//read file into stream, try-with-resources
try (Stream<String> stream = Files.lines(Paths.get(fileName), StandardCharsets.ISO_8859_1)) {

    stream.forEach(System.out::println);
    i++;

} catch (IOException e) {
    e.printStackTrace();
}
System.out.println("count is : "+i);

but the problem is I need to put i++ inside the following line:

stream.forEach(System.out::println);

So I want something like this:

stream.forEach(System.out::println; i++);

But it does not work this way so can anyone help how I can make it work?

HMdeveloper
  • 2,772
  • 9
  • 45
  • 74
  • create your own lambda doing what you want. –  Dec 10 '15 at 18:03
  • @vaxquis a lambda expression may not assign a new value to i. – JB Nizet Dec 10 '15 at 18:08
  • @JBNizet in general, lamdba *may* assign new value to `i` - it just can't do that directly, if `i` is not effectively final. if he had declared it as a field, there shouldn't be any real problem with it. he asked "[how to have] multiple lines of code in stream.forEach" - the answer is "create a lamdba instead of using method reference"; while in this particular case your answer is excellent, it wouldn't help him in a more general case. –  Dec 10 '15 at 18:09

3 Answers3

8

There are two completely different things you should ask here:

a) how do I place multiple lines of code in stream.forEach()?

b) what should I do to count the number of lines in a Stream?

The question b) is answered already by other posters; on the other hand, the general question a) has a quite different answer:

use a (possibly multi-line) lambda expression or pass a reference to multi-line method.

In this particular case, you'd either declare i a field or use a counter/wrapper object instead of i.

For example, if you want to have multiple lines in forEach() explicitly, you can use

class Counter { // wrapper class
    private int count;
    public int getCount() { return count; }
    public void increaseCount() { count++; }
}

and then

Counter counter = new Counter();
lines.stream().forEach( e -> {
    System.out.println(e);
    counter.increaseCounter(); // or i++; if you decided i is worth being a field
} );

Another way to do it, this time hiding those multiple lines in a method:

class Counter { // wrapper class
    private int count;
    public int getCount() { return count; }
    public void increaseCount( Object o ) {
        System.out.println(o);
        count++;
    }
}

and then

Counter counter = new Counter();
lines.stream().forEach( counter::increaseCount );

or even

Counter counter = new Counter();
lines.stream().forEach( e -> counter.increaseCount(e) );

The second syntax comes in handy if you need a consumer having more than one parameter; the first syntax is still the shortest and simplest though.

6

The forEach method takes an instance of any class that implements Consumer. So here is an example of using a custom Consumer implementation that keeps up with the count. Later you can call getCount() on the Consumer implementation to get the count.

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        List<String> lines = new ArrayList<String>();
        lines.add("line 1");
        lines.add("line 2");

        MyConsumer countingConsumer = new MyConsumer();
        lines.stream().forEach(countingConsumer);
        System.out.println("Count: " + countingConsumer.getCount());
    }

    private static class MyConsumer implements Consumer<String> {
        private int count;

        @Override
        public void accept(String t) {
            System.out.println(t);
            count++;
        }

        public int getCount() {
            return count;
        }
    }
}
Josh Chappelle
  • 1,558
  • 2
  • 15
  • 37
  • Yea I'm a lambda novice to be honest, but I've been doing Java for 10 years so I figured I would give a solution with what I know. – Josh Chappelle Dec 10 '15 at 18:41
  • @vaxquis I didn't mention lambdas in my answer and the question didn't require an answer use lambdas. So I'm not sure why you want me to add it to my answer. – Josh Chappelle Dec 10 '15 at 19:58
  • @vaxquis I actually created the class with the intention of using lambdas but didn't end up doing it. I just needed a class with a main. It could be called XYZ for all I care. My main goal was to inform about the Consumer interface. Good change though, it could be misleading otherwise to future viewers. – Josh Chappelle Dec 10 '15 at 21:13
5

Use peek() and count():

i = (int) stream.peek(System.out::println) 
                .count();
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 5
    +1 but note that in the future, `count()` will be optimized to directly return the size if the spliterator reports the SIZED characteristic. Thus the `peek` operation may not be performed. This is probably not the case when you get a stream from a file but some using this code should be aware of that. – Alexis C. Dec 10 '15 at 18:17
  • 4
    I would not recommend using `peek()`, according to JavaDoc `peek`exists mainly to support debugging https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer- – user140547 Dec 10 '15 at 21:15
  • 1
    The problems with `peek` have been discussed [here](http://stackoverflow.com/a/33636377/2711488)… – Holger Dec 11 '15 at 09:33
  • @AlexisC. that would drastically change the behavior of the method, which is explicitely documented as doing the equivalent of a reduce(). Is that really something that will happen? – JB Nizet Dec 11 '15 at 10:07
  • @AlexisC. BTW, would this optimization still be done if the stream has an intermediate operation (like peek()) before the count? I'd be OK with the optimization if it was done when there is no intermediate op, but would find it an unwise choice if there is one. – JB Nizet Dec 11 '15 at 10:12
  • 1
    @JBNizet See the api note: http://download.java.net/jdk9/docs/api/java/util/stream/Stream.html#count-- – Alexis C. Dec 13 '15 at 21:33
  • @AlexisC. Good to know thanks. One more pain point when migrating to Java 9. They should put that warning NOW in the Java 8 javadoc. – JB Nizet Dec 13 '15 at 22:29