3 ms·
Well, what's the alternative? In a more functional language we have two options: have a map() function we apply first and then call forEach() on the result, and
by openasocket 10y ago
Well, what's the alternative? In a more functional language we have two options: have a map() function we apply first and then call forEach() on the result, and function composition. The problem with the map() function is we have to return a ConcurrentHashMap, which means we're effectively making a copy of the data, which isn't space efficient. The function composition operation is more tenable (in Java, this is the andThen() or compose() methods, which are duals), but kind of ugly to write out if both the transformer and action are lambda expressions. Truth be told, I don't see the disadvantage of having extra method signatures: their usage is optional, and I imagine they have default implementations that refer to each other using function composition on the back end.
There's also one annoyance that neither function composition nor extra method signatures solve, which is boxing primitives. The transformer has to return an object, and the action has to accept one, so the example forEach(4, List::size, ...) will generate a bunch of boxed integers. Hopefully the JIT will notice this and try to elide the allocations, but I don't know how much you can trust the JIT to figure that out. The best thing to do is to manually compose the operations yourself, but that isn't always possible or convenient.
- deleted 10y ago[deleted]
- hyperpape 10y agoWhen I look at the documentation, I find the section with forEach to be so much noise. Ditto for autocompletion in an IDE. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html https://docs.oracle.com/javase/8/docs/api/java/util/concurre.... When does manual composition not work well? The article's examples seem easy enough: map.forEach(1, (k, v) -> "There is " + v.size() + " articles about " + k, System.out::println); map.forEach(1, (k, v) -> System.out.println("There is " + v.size() + " articles about " + k));
- openasocket 10y agoTo be fair, there are only 3 signatures for the forEach() method, the reason that section is so noisy is because it's followed by forEachEntry(), forEachKey(), and forEachValue(), each of which has two signatures. You technically don't need those other methods, but they are convenient to have. I find "map.forEachValue(1, Foo::bar)" to be more clear than "map.forEach(1, (_, v) -> Foo.bar(v))". Manual composition can get ugly if the argument is used multiple times, especially if the function is non trivial. Say something like map.forEach(1, Expensive::func, x -> x * x) which would naively manually compose to map.forEach(1, (k, v) -> Expensive.func(k, v) * Expensive.func(k, v)) which is going to be less efficient, doubly bad if that function has side effects, and noisy on top of it. You could assign the value to a local variable inside the lambda, which gives you map.forEach(1, (k, v) -> { int x = Expensive.func(k, v); return x * x; }) which fixes the efficiency and correctness problems, but is a bit too verbose for me. There's probably other situations, that was just the first one I thought of. Honestly, though, it all comes down to taste and the situation. I personally would use manual composition excepting circumstances like my example above.