Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Streams (FP in java) is slower than for loops for a couple reasons. A big one is java doesn't natively support real closures or lambdas. It does have syntax for them, but that is just syntactic sugar for an class with a single method under the hood. So streams end up doing lot of object allocation and garbage for the fake closures.

Also, streams operate on objects, so they have to be on the heap. You can't use them with primitives on the stack. Though with autoboxing, the JVM may play some tricks with a list of Integer objects really being primitives on the stack, but I would never count on it.

As for SIMD, Java isn't going to parallelize anything automatically. You need to tell it you run the steam in parallel which will split it into threads. Java doesn't have lightweight threads like coroutines.

I know lightweight threads are on the roadmap and maybe available in Java 21 or newer. I know real closures have been considered, but I don't if it's gone anywhere. It's hard to do a quick search because we got "closures" in Java 8 so theres a lot of noise.

And as a caveat, I am most familiar with Java 17 (and older). I expect we'll look at moving to Java 21 (current LTS) next year.



The big differences are: 1. Rust closures are by-value structs; whereas Java closures are heap objects. 2. Rust generics are monomorphized; whereas Java type-erases them -> lots of virtual call overhead when passing a closure to a generic function.

Sometimes, if the Java JIT manages to inline absolutely everything, it can optimize away these overheads. But in practice, Rust FP gets optimized a lot more reliably than Java FP.


The other problem with the parallel streams is that it’s badly implemented. Threads for parallel streams are pulled from a single thread pool shared across the whole application so if you have multiple parallel streams in an application that’s already inherently multithreaded (e.g. a web service), you end up with severe resource contention that makes the parallelism work poorly if at all and can end up causing your app to deadlock because all the threads are in use somewhere else. There’s a workaround for it, but it ends up requiring some ugly boilerplate code to work.


> It does have syntax for them, but that is just syntactic sugar for an class with a single method under the hood. So streams end up doing lot of object allocation and garbage for the fake closures.

In Rust a closure is really just a struct that implements up to three closure traits, each of which provide a single function. So from that side of things, what Java is doing for them isn't inherently different from Rust.


Huh, interesting. Thanks for that! Today, I learned.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: