Hatefiend
Hatefiend

Reputation: 3596

Java - Basic streams usage of forEach

I have a class called Data which has only one method:

public boolean isValid()

I have a Listof Dataand I want to loop through them via a Java 8 stream. I need to count how many valid Data objects there are in this List and print out only the valid entries.

Below is how far I've gotten but I don't understand how.

List<Data> ar = new ArrayList<>();
...
// ar is now full of Data objects.
...

int count = ar.stream()
            .filter(Data::isValid)
            .forEach(System.out::println)
            .count(); // Compiler error, forEach() does not return type stream.

My second attempt: (horrible code)

List<Data> ar = new ArrayList<>();
...
// Must be final or compiler error will happen via inner class.
final AtomicInteger counter = new AtomicInteger();
ar.stream()
    .filter(Data:isValid)
    .forEach(d -> 
    {
        System.out.println(d);
        counter.incrementAndGet();
    };
System.out.printf("There are %d/%d valid Data objects.%n", counter.get(), ar.size());

Upvotes: 0

Views: 551

Answers (2)

Holger
Holger

Reputation: 298103

If you don’t need the original ArrayList, containing a mixture of valid and invalid objects, later-on, you might simply perform a Collection operation instead of the Stream operation:

ar.removeIf(d -> !d.isValid());
ar.forEach(System.out::println);
int count = ar.size();

Otherwise, you can implement it like

List<Data> valid = ar.stream().filter(Data::isValid).collect(Collectors.toList());
valid.forEach(System.out::println);
int count = valid.size();

Having a storage for something you need multiple times is not so bad. If the list is really large, you can reduce the storage memory by (typically) factor 32, using

BitSet valid = IntStream.range(0, ar.size())
    .filter(index -> ar.get(index).isValid())
    .collect(BitSet::new, BitSet::set, BitSet::or);
valid.stream().mapToObj(ar::get).forEach(System.out::println);
int count = valid.cardinality();

Though, of course, you can also use

int count = 0;
for(Data d: ar) {
    if(d.isValid()) {
        System.out.println(d);
        count++;
    }
}

Upvotes: 5

Kishore Bandi
Kishore Bandi

Reputation: 5701

Peek is similar to foreach, except that it lets you continue the stream.

ar.stream().filter(Data::isValid)
            .peek(System.out::println)
            .count();

Upvotes: 4

Related Questions