18

Here's my code as per now.

List<Cat> cats = petStore.getCatsForSale();

if (!cats.empty) 
    logger.info("Processing for cats: " + cats.size());

for (Cat cat : cats) {
    cat.giveFood();
}

My colleague writes realy nice code using the Java stream API. I tried to rewrite it as one streaming statement, but I got stuck.

petStore.getCatsForSale().stream.forEach(cat -> cat.giveFood)
    .countTheCats().thenDo(logger.info("Total number of cats: " + x)); // Incorrect... is this possible?

How can I do this? Ideally I want a single streaming statement...

1
  • 2
    What is count? Do you expect a log statement for every non empty cat? Commented Feb 5, 2019 at 8:26

4 Answers 4

18

Your current code is much better without a stream and can further be cutshort to:

if (!cats.isEmpty()) {
    logger.info("Processing for cats: " + cats.size());
}
cats.forEach(Cat::giveFood); // Assuming giveFood is a stateless operation
Sign up to request clarification or add additional context in comments.

2 Comments

Hi and thx for answer. Can this be done in a one liner? Of course I can write "if (!cats.isEmpty()) logger.info("...") but the purpose of this question is to write it with streams
In my opinion, is this the only acceptable solution. Don't do everything with Streams just because a tutorial told you so, just by looking at every answer here it's clear what the intentions of this one here is, but for every other? It's just a mess
11

I am not sure why you want to use streams as the current loop solutions works, but you may as well use a Stream<List<Cat>>:

Stream.of(petStore.getCatsForSale())
    .filter(cats -> !cats.isEmpty())
    .flatMap(cats -> {
        logger.info("Processing for cats: " + cats.size());
        return cats.stream();
    })
    .forEach(Cat::giveFood);

Maybe an optimization:

Stream.of(petStore.getCatsForSale())
    .filter(cats -> !cats.isEmpty())
    .peek(cats -> logger.info("Processing for cats: " + cats.size()))
    .flatMap(Collection::stream)
    .forEach(Cat::giveFood);

Or use this other variant:

Stream.of(petStore.getCatsForSale())
    .filter(cats -> !cats.isEmpty())
    .mapToInt(cats -> {
        cats.forEach(Cat::giveFood);
        return cats.size();
    })
    .findAny()
    .ifPresent(count -> logger.info("Processing for cats: " + count));

13 Comments

petStore.getCatsForSale() is a List<Cat>
@nullpointer I know and with this ugly piece of code consisting of Java8 streams I think it works, correct me if I'm wrong of course :)
@CommonMan not really, its empty vs non-empty here. I doubt Optional would be useful here much.
@nullpointer I don't really like any of the solutions here except the one from you, I think using Streams for everyday logic is just overkill and makes it extremly hard to understand the simplest of logic
@Spara so that I have a "wrapper"-stream around the Cat-list (Stream<List<Cat>>) which allows me to operate on the whole list at once, but still inside the stream. Using cats.stream() though will give you a stream over all cats (Stream<Cat>)
|
4
cats.stream()
    .peek(Cat::giveFood)
    .findAny().ifPresent(cat -> logger.info("Processing for cats: " + cats.size()));

8 Comments

Starting from java-9 you may leave your cats hungry ))). Execution of peek is not guaranteed anymore. Please read javadoc for more details.
@ETO Please look at tags. It says java-8!
@oleg.cherednik I think ETO wanted to say that this solution may only work for java-8 which makes it not a favorable solution when one wants to migrate the version upwards :)
@oleg.cherednik Yes, I know. Using peek for non-debugging purposes is considered harmful anyway.
Moreover, starting from February 2019 there is no long-term support of java-8 for cemmercial users. So if you write java-8 code, consider avoiding potential migration bugs. One may not notice that java-9's peek is not working as it did before. Thus your code will have a hidden unobvious bug.
|
2

I agree with @Lino. This is another alternative based on his idea:

List<Cat> cats = petStore.getCatsForSale();

cats.stream().limit(1)
    .flatMap(c -> {
        logger.info("Processing for cats: " + cats.size());
        return cats.stream();
    }).forEach(Cat::giveFood);

2 Comments

Why not use an IntStream.range(1)? No need to use cats.stream().limit(1) that way
@Lino because of empty list

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.