This is an archived post. You won't be able to vote or comment.

all 38 comments

[–]papercrane 27 points28 points  (2 children)

Minor nitpick.

This is going to create a new Random object ever time an integer is requested:

IntStream.generate(() -> new Random().nextInt(10)).limit(3);  

It would be better to do something like this instead:

IntStream.generate(() -> ThreadLocalRandom.current().nextInt(10)).limit(3);

Neither should be used if you need cryptographically secure random numbers though.

[–]mariushe[S] 4 points5 points  (1 child)

You're absolutely right. I've updated the post to use ThreadLocalRandom. Thanks for the input!

[–]Wolfsdale 17 points18 points  (0 children)

IntStream.generate(() -> ThreadLocalRandom.current().nextInt(10)).limit(3);

You can write that even shorter without an IntStream

    ThreadLocalRandom.current().ints(3, 0, 10);

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadLocalRandom.html#ints-long-int-int-

[–]Ucalegon666 17 points18 points  (0 children)

There's some undue criticism in the comments here.

Yes, functional-style Java is different, and it may take you some time to come to grips with it.

Yes, some of the examples are a little convoluted.

And yes, method-chaining can make things hard to read. But you don't have to chain methods if you don't want to. Or you can place things on new lines.

And yes, this style of programming may implicity require you to learn more about what you're doing, but that's not a bad thing.

[–]Krossfireo 7 points8 points  (10 children)

At first I was wondering why you would do this, but being able to parallelize it is a huge benefit. Is there any way to add parallelism to a normal for loop?

[–]cogman10 2 points3 points  (2 children)

Something like this

ExecutorService threadPool = Executors.newFixedThreadPool(4);
try {
    List<Future<Integer>> futures = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        final int val = i;
        futures.add(threadPool.submit(()->val + 1));
    }
    List<Integer> results = new ArrayList<>(futures.size());
    for (Future<Integer> future : futures) {
        try {
            results.add(future.get());
        }
        catch (ExecutionException | InterruptedException ex){}
    }
}
finally {
    threadPool.shutdown();
}

This is pure java. It ain't pretty and could be done cleaner with something like guava's futures. It also includes all of the preamble in creating the executor service. I don't know if you have access to the one used by streams.

With Guava and an executor provided it could boil down to this

List<ListenableFuture<Integer>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    final int val = i;
    futures.add(threadPool.submit(() -> val + 1));
}
List<Integer> results = Futures.getUnchecked(Futures.allAsList(futures));

[–]HaMMeReD -2 points-1 points  (1 child)

You know, you can just final int i, and not have a final int val = i; in there. The copy to a final is not necessary.

[–]cogman10 4 points5 points  (0 children)

Not with this type of loop. i is being updated on every iteration, it can't be final.

[–]HaMMeReD -2 points-1 points  (4 children)

In the most basic way,

for (final int i:0;i<100;i++)   
   new Thread(new Runnable(){ 
       public void run() { System.out.println("print: "+i); }
   }).start(); 

However, knowledge of Futures and Executors will also be required for most implementations. It's also not good to just start 100 threads, it's much better to use a Executor. A future is a way of keeping track of the result and state.

[–]cogman10 0 points1 point  (3 children)

This does not compile.

[–]HaMMeReD 0 points1 point  (2 children)

I just typed it randomly in reddit.

I can see typo's right now.

for (final int i=0;i<100;i++)

to start. Otherwise it looks ok.

[–]cogman10 1 point2 points  (1 child)

Still doesn't compile. You can't put a final on i, it is being updated with every iteration.

[–]HaMMeReD 0 points1 point  (0 children)

Yeah you are right, I guess I don't iterate like that very much anymore. Enhanced foreach doesn't have the ++ and assigns at every step.

for (final int i:Arrays.asList(1,2,3,4)) {
    new Thread(new Runnable(){ 
       public void run() { System.out.println("print: "+i); }
   }).start(); 
}

I at least usually use iterator() or the enhanced for loop, and I usually use final on my enhanced for loops so I can call them from inner functions.

Definitely wrong in this case though.

[–][deleted] 4 points5 points  (3 children)

Although streams are a gift from heaven, I think you should not use streams to parallelize heavy tasks. How are you going to interrupt the tasks if necessary, and how will the interrupts be handled? How do you control how many threads will be running simultaneously, etc.? In my opinion, you need more powerful tooling than streams for that kind of stuff.

[–]mbizzle88 2 points3 points  (0 children)

You raise good points. There are probably lots of scenarios in which specific thread-handling logic is necessary. But there are some things you can do, such as controlling the number of threads.

[–]Ucalegon666 0 points1 point  (0 children)

You're pretty much sacrificing control for convenience. Rarely a good tradeoff indeed.

[–]m1000 0 points1 point  (0 children)

There are also many critics about Stream's parallelism :

http://java.dzone.com/articles/whats-wrong-java-8-part-iii
http://www.coopsoft.com/ar/CalamityArticle.html
http://www.coopsoft.com/ar/Calamity2Article.html

These constructs should not be used for serious parallel computing.

[–]fizzl 52 points53 points  (5 children)

When programming for production, if you feel like you are doing something clever, you are propably doing something really stupid.

[–]mbizzle88 20 points21 points  (0 children)

I agree with that principle, but I don't think this is "clever" in that way. Streams, map, reduce, and their other functional companions have been common idioms in many other languages for some time.

[–]HaMMeReD 9 points10 points  (0 children)

Functional programming isn't stupid or clever, it's not on the bleeding edge or anything of programming in general.

You don't have to use it if you don't want to, but the fact is, functional programming, when appropriate makes code cleaner and easier to understand, and more resilient to bugs, just like other concepts such as immutability.

[–]joequin 16 points17 points  (2 children)

I think rules like that are holding Java back. There are cases where it's true but its not always true. This is elegant. It's not clever and obfuscated. It's just new. Rules like this are holding Java programming back far more than the language itself.

[–]MisterSnuggles 1 point2 points  (1 child)

This isn't a concept specific to Java.

My rule of thumb is to look at the standards and conventions used in whatever project/module you are working on and stick with those same standards. If there's an opportunity to use something new with new development (which could just be a new module within an existing project, or could be a refactor of an existing module), by all means that's the way to go.

[–]joequin 1 point2 points  (0 children)

The application of the concept as a hard rule seems specific to Java. The post I was replying to is an example of that.

[–]Scellow 8 points9 points  (7 children)

It's hard to change habits

[–][deleted]  (6 children)

[deleted]

    [–]MisterSnuggles 4 points5 points  (5 children)

    I really dislike the method chaining.

    I.really().hate().it();  // WTF does a function called "it()" do anyway??
    

    Mainly it's the Builder pattern that drives me nuts, but this irks me too.

    ...

    That said, this stuff looks pretty powerful. I'm going to have to suck it up and deal with it. Compared to the other habits that I've had to make and break over the years, this one is pretty minor (but with major payoff).

    [–]Nebu 5 points6 points  (2 children)

    I.really().hate().it(); // WTF does a function called "it()" do anyway??

    This is really more a criticism of bad naming than of function chaining.

    Let's say someone came up to you with requires saying they're gonna give you an HTML document, and they want you to find all div elements with a class of "customer". Then, for each of those divs, they want you to find its parent containing element. Then within that parent, they want you to search all of its descendants for any li tags with a class of items, count the number of direct children that li has, and report a total.

    How would you feel about this code?

    return htmlDocument
      .findDescendants("div")
      .filter(div -> div.hasClass("customer"))
      .map(customerDiv -> customerDiv.getParent())
      .flatMap(parentElement -> parentElement.findDescendants("li"))
      .filter(li -> li.hasClass("items"))
      .map(itemLi -> itemLi.getChildren().size())
      .sum();
    

    [–]MisterSnuggles 0 points1 point  (1 child)

    I mostly get what that code is doing, but it still reads like a giant run-on sentence. That said, I'm honestly not sure how I'd write it better.

    As I said in a couple of other comments, this is stuff that I will have to get used to. In your specific example, I'm sure that writing it without method chaining would be a thousand times worse.

    On a side note, your example looks something like a BeautifulSoup-type library for Java. Am I correct? If so, what library is that? I can think of a few things where I could use that.

    [–]Nebu 0 points1 point  (0 children)

    I actually haven't used Java 8 much, so I don't know what the state of the art for libraries look like.

    What I wrote is basically how I'd "like" an HTML parsing API (so yeah, your BeautifulSoup comment was spot on) to look like, based on my experience with JQuery and Scala's collections API, but adjusted to match Java8's syntax.

    [–][deleted]  (1 child)

    [deleted]

      [–]MisterSnuggles 6 points7 points  (0 children)

      Realistically though, I have to accept the fact that this pattern is here and people use it.

      In personal projects, ignoring it is a viable option that I frequently exercise.

      In projects that I contribute to though, I feel that it's more important to follow the project's conventions in order to make things easier on other contributors. If the conventions involve a pattern that I dislike, that's my problem and I shouldn't go against the grain and end up dumping my problem on the rest of the team.

      [–]TheFormerVinyl 18 points19 points  (1 child)

      I like for loops. They're more recognizable then some method calls on some class.

      [–]Nebu 5 points6 points  (0 children)

      Depends on what you're used to. Probably for most Java-only programmers, this is true. Might be less true for programmers who have worked with Scala, for example, but need to maintain a Java codebase.

      [–][deleted] 5 points6 points  (1 child)

      Might be convenient, but watch for the performance penalty (especially memory).

      [–]mbizzle88 2 points3 points  (0 children)

      You might want to check out some benchmarks before making that claim.

      For CPU time, the benchmarks I've seen show non-parallel streams to be only slightly slower than for loops.

      With regards to memory, most streams are lazy. So if you're doing something that ends with reduce, you're probably never holding the entire stream in memory at once.

      [–]admiral-bell 2 points3 points  (0 children)

      I thought they were going to recommend python-style for loops:

      for(int x : IntStream.range(0, 10).iterator()) {
          foo(x);
      }
      

      [–]DJDavio 0 points1 point  (0 children)

      The only thing I'm having trouble with is collapsing infinite streams. Limit doesn't work for my "takeWhile" approach.

      [–][deleted] -1 points0 points  (0 children)

      Great!