0

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());
1
  • 1
    Either peek or collect to a List, get its size and print it out. Commented Nov 22, 2016 at 16:52

2 Answers 2

5

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++;
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

neat solution (as usual from u). one question still. where did the 32 appear here? it's not like we really know the Data internals and how much that class weights.
@Eugene: the BitSet uses exactly one bit per entry, whereas an ArrayList stores a reference per entry, which is typically 32 bit (either matching the architecture or due to the use of compressed OOPs). It’s not about the per-object overhead, but how these data structures scale with large sizes. Of course, we should be careful with assumptions about internals, but certain implementation classes bear their fundamental nature even in its name (“bit” or “array”) to allow the developer to make an appropriate choice. The factor may be even 64 instead of 32, but that’s why I wrote “typically”…
so 32 comes from diff between the reference storage and one single bit in BitSet. Would it be appropriate to call this factor the shallow difference, as opposed to deep difference?
@Eugene: well, here it is the only difference…
4

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

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

5 Comments

Why would I ever use forEach then?
@Hatefiend you use foreach when you want to iterate over a list, perform some operations on each of its objects and leave. Say you want to compute the total interest for a list of customers. You could just use foreach and on each object call calculateInterest(). The result of this is anyways saved on the Original Customer Object, so you don't have to collect it either.
But by this logic I could just do list.peek(x -> x.calculateInterest(); }; No? How does that differ from forEach()
Also I see that peek is a suggest debug only method. See the following api link

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.