Do you really understand all about Iteration methods like forEach()
, map()
, filter()
, … in JavaScript? If you’ve never touched Java before, the answer will be no .
The article presents the reason why such a thing happened. Java has nothing to do with JavaScript. Ok, let’s start exploring the magic behind the above methods, from a Java perspective.
1. From the apparent simplicity of JavaScript
1.1. Iteration methods
Surely every JavaScript developer has used iteration methods like forEach()
, map()
, …. Here is a simple example.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> primes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">,</span> <span class="token number">11</span> <span class="token punctuation">,</span> <span class="token number">13</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token comment">// forEach() nè</span> primes <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token parameter">num</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// filter() nè</span> <span class="token keyword">const</span> primesLessThan10 <span class="token operator">=</span> primes <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> <span class="token parameter">num</span> <span class="token operator">=></span> num <span class="token operator"><</span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// reduce() nè</span> <span class="token keyword">const</span> sum <span class="token operator">=</span> primes <span class="token punctuation">.</span> <span class="token function">reduce</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">prev <span class="token punctuation">,</span> curr</span> <span class="token punctuation">)</span> <span class="token operator">=></span> prev <span class="token operator">+</span> curr <span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
To be honest, JavaScript is so simple that you might wonder how complex the code above is?
Just simply:
- Some of the commonly used methods of JavaScript
- The methods receive a callback function
- Callback function is written as an arrow function
All that is it, there’s no such thing as difficult, right?
1.2. Compare with pure code
Everyone knows that instead of using some method, forEach()
for example, you can use pure code to write.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> primes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token comment">// Dùng forEach()</span> primes <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token parameter">num</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Không dùng forEach()</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">const</span> num <span class="token keyword">of</span> primes <span class="token punctuation">)</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Or another method is filter()
.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> primes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">,</span> <span class="token number">11</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token comment">// Dùng filter()</span> <span class="token keyword">const</span> primesGreaterThan10 <span class="token operator">=</span> primes <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> <span class="token parameter">num</span> <span class="token operator">=></span> num <span class="token operator">></span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Không dùng filter()</span> <span class="token keyword">const</span> primesGreaterThan10_2 <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">const</span> num <span class="token keyword">of</span> primes <span class="token punctuation">)</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> num <span class="token operator">></span> <span class="token number">10</span> <span class="token punctuation">)</span> primesGreaterThan10_2 <span class="token punctuation">.</span> <span class="token function">push</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
1.3. So where is the complexity?
If you have JavaScript proficiently coded, you can code the above methods like flying without thinking much. However, that is simply because you have been familiar with and have coded many times .
When I first started learning the above methods, has anyone ever asked questions like this?
What is the function passed to
forEach()
?Is the callback function of
map()
different from theforEach()
?
That proves, forEach()
or something else is not as simple as you might think. Understanding how to run is one thing, code is one thing, but understanding why is like that is another …
2. To the magic complexity in Java
2.1. Does Java have forEach()
or something?
Java 8 added the Stream API feature, introducing the Stream concept. Basically every Stream can use the functions forEach()
, …
All Collection such as List, Set, .. of java are implements Stream interface. Hence, we can easily call forEach()
, … on it.
1 2 3 4 5 6 | <span class="token class-name">List</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Integer</span> <span class="token punctuation">></span></span> primes <span class="token operator">=</span> <span class="token class-name">Arrays</span> <span class="token punctuation">.</span> <span class="token function">asList</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">map</span> <span class="token punctuation">(</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">.</span> <span class="token punctuation">)</span> <span class="token comment">// Còn nữa</span> |
However, when using it, we cannot write like JavaScript.
1 2 3 4 5 6 7 8 9 10 | <span class="token class-name">List</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Integer</span> <span class="token punctuation">></span></span> primes <span class="token operator">=</span> <span class="token class-name">Arrays</span> <span class="token punctuation">.</span> <span class="token function">asList</span> <span class="token punctuation">(</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Không truyền trực tiếp được function</span> primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> function <span class="token punctuation">(</span> <span class="token keyword">int</span> num <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span> <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Mặc dù dùng lambda được, nhưng nó không giống arrow function</span> primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> num <span class="token operator">-></span> <span class="token class-name">System</span> <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> num <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
2.2. How did Java do that?
As above, we know that Java cannot pass a function to another function parameter. But the hard comes the wisdom , Java has designed a refreshing, super magic and suitable for strong typed languages like Java.
Specifically, java introduces the concept of Functional interface .
1 2 3 4 5 | <span class="token annotation punctuation">@FunctionalInterface</span> <span class="token keyword">interface</span> <span class="token class-name">Consumer</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">T</span> <span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">accept</span> <span class="token punctuation">(</span> <span class="token class-name">T</span> t <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
The above code is a built-in functional interface of java, I rewrote it as an example. Functional interface is just an interface, there is only one abstract method.
Err, so what does it have to do with JavaScript?
Since Java cannot pass a function to another function, instead of passing a callback function, java will pass an implements of Functional interface .
1 2 3 | primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token comment">/* Thứ gì đó implements Consumer interface */</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
This is the structure of the forEach()
method (a sample example).
1 2 3 4 5 | <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token class-name">Consumer</span> c <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token comment">/* Lặp hết từng elements */</span> <span class="token punctuation">)</span> c <span class="token punctuation">.</span> <span class="token function">accept</span> <span class="token punctuation">(</span> element <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Oh, have you seen anything related here. The code of forEach()
quite similar to the JavaScript side. The c.accept(element)
not the equivalent of the following JavaScript.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">function</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token parameter">callback</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token comment">/* Lặp hết từng elements */</span> <span class="token punctuation">)</span> <span class="token function">callback</span> <span class="token punctuation">(</span> element <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">...</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
See, instead of passing a function directly like JavaScript, java puts that function into a functional interface, and passes the implementation of the functional interface to the function.
2.3. What is Lambda?
Pay attention to the paragraph above, I have bolded the implements of the functional interface . Functional interface is just an interface, has the code to execute, so there must be something (temporarily called X), implements functional interface with code to execute, and we will pass X to the function forEach()
, …
So how to create X? Actually in java there are 4 ways:
- Use class implements, then create object from class, pass the object to the function
- Direct form implements always
- Use lambda
- Use the reference method
Method 2 is often used in the past, namely code like this.
1 2 3 4 5 6 7 8 | primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">Consumer</span> <span class="token generics"><span class="token punctuation"><</span> <span class="token class-name">Integer</span> <span class="token punctuation">></span></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">accept</span> <span class="token punctuation">(</span> <span class="token class-name">Integer</span> t <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Code ở đây</span> <span class="token class-name">System</span> <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> t <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Compared to JavaScript, the above method is too verbose.
1 2 3 4 | primes <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token function">functon</span> <span class="token punctuation">(</span> <span class="token parameter">t</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> t <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Therefore, people use lambda instead.
1 2 | primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> t <span class="token operator">-></span> <span class="token class-name">System</span> <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> t <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Although the lambda is quite similar to the arrow function of JavaScript, there are differences:
- Arrow function is short form of function
- Lambda actually returns a functional interface
Hence, the lambda t -> System.out.println(t)
is a functional interface.
And the lambda can automatically infer the data type. Since forEach()
accepts the Consumer
interface, and Consumer
has only one method whose parameter is Integer (do generics), t
is of type Integer
.
The last is a shorthand for the lambda, which is called the reference method. Use when lambda is short as above.
1 2 | primes <span class="token punctuation">.</span> <span class="token function">stream</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">forEach</span> <span class="token punctuation">(</span> <span class="token class-name">System</span> <span class="token punctuation">.</span> out <span class="token operator">::</span> <span class="token function">println</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
3. The functional interfaces are available in java
3.1. Built in functional interfaces
Java has built in many functional interfaces, each corresponding to a type of method within it (the method depends on the parameter and return type):
Consumer<T> { void accept(T t); }
: This guy only knows how to eat (has 1 parameter) but doesn’t return anything (no return)Supplier<T> { T get(); }
: Only return, do not take any parametersFunction<T, R> { R apply(T t); }
: Take 1 parameter, return a valuePredicate<T> { boolean test(T t); }
: The judge is here, takes a param and returns true and false
Above are 4 basic functional interfaces. There are also functional interfaces BiABC
format instead of taking 1 parameter, it will take 2 parameters.
So what to do with forEach()
, map()
, … this and that?
3.2. The main point of the article
At this point, you will understand the relationship between Java and JavaScript.
Each iteration method will receive a corresponding functional interface.
And this is the main point of this article.
In JavaScript does not show that clearly, due to the weakly typed nature of the language also applies to the function. However, when methods like forEach()
entered Java, it made a difference:
forEach()
used with theConsumer
interfacemap()
used with theFunction
interfacefilter()
used with thePredicate
interfacefind()
,findValue()
used with thePredicate
interfaceBiFunction
reduce()
use withBiFunction
interface (because there are 2 parameters, and return a value)
The reason is easy to infer from the context. This in Java can be extended to JavaScript as well.
For example, with JavaScript when using filter()
, the following code returns incorrect results.
1 2 3 4 | <span class="token keyword">const</span> primes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">,</span> <span class="token number">7</span> <span class="token punctuation">,</span> <span class="token number">11</span> <span class="token punctuation">,</span> <span class="token number">13</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> primesLessThan10 <span class="token operator">=</span> primes <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> primesLessThan10 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Kết quả sai</span> |
Since filter()
not for Consumer
, it has to be Predicate
, get e
and return a boolean
. Fix like this.
1 2 | primes <span class="token punctuation">.</span> <span class="token function">filter</span> <span class="token punctuation">(</span> <span class="token parameter">e</span> <span class="token operator">=></span> e <span class="token operator">></span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Okay the article is quite long, I just stopped here. Thank you for taking the time to read until now, I am grateful for that. Hopefully JavaScript developers will understand more about iteration methods, and use them more accurately.
Happy coding