1 Transformation operations
1.1 Traversal
There are at least 3 methods to traverse a list in either Java or Kotlin: Traditional for loop, advanced for loop and forEach.
Traditional for loop
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
for(i:Int in 0 until list.size) {
println(list[i])
}
Advanced for loop
for(int element : list) {
System.out.println(element);
}
for (element in list) {
println(element)
}
forEach
list.forEach((element)->{
System.out.println(element);
})
list.forEach{
println(it)
}
We have to keep in mind that when forEach is used for traversal, neither break nor continue is available in the program.
In Kotlin, local return, like return@forEach, and non-local return like return are available.
In Java, there is only non-local return.
What local return and non-local return can do for us is really limited. Some complex cases need complex process control.
So, here comes our main topic today: filter, map and flatMap.
| function | description |
|---|---|
| filter | to filter elements which do not meet specific conditions |
| map | to turn each element into a new element by mapping and output a new collection |
| flatMap | to turn each element into a new collection by mapping and output a new collection composed of these new collections |
Of course, all these functions above are based on traversal. In another word, they will traverse each element before prosessing it.
1.2 filter

//since Java 8
list.stream().filter(e -> e % 2 == 0);
val list1 = list.filter { it % 2 == 0 }
val list2 = list.asSequence().filter { it % 2 == 0 }
In Kotlin, we can directly invoke HOF(Higher Order Function, HOF) filter() or first invoke asSequence() and then invoke filter(). What's the difference? This question will be answered after map() is introduced.
1.3 map

list.stream().map(e -> e * 2 +1)
val list1 = list.map { it * 2 + 1 }
val list2 = list.asSequence().map { it * 2 + 1 }
Still, in Kotlin, we can directly invoke HOF(Higher Order Function, HOF) map() or first invoke asSequence() and then invoke map(). What's the difference?
Let's program in IDE.
- ① Java: First invoke
stream()and then invokefilter()
public class Jv {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
list.stream()
.filter(e -> {
System.out.println("filter : " + e);
return e % 2 == 0;
})
.map(e -> {
System.out.println("map : " + e);
return e * 2 + 1;
})
.forEach(e -> {
System.out.println("forEach : " + e);
});
}
}
Console outputs:

- ② Kotlin: First invoke
asSequence()and then invokefilter()
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
list.asSequence()
.filter {
println("filter : $it")
it % 2 == 0
}
.map {
println("map : $it")
it * 2 + 1
}
.forEach {
println("forEach : $it")
}
}
Console outputs:

- ③ Kotlin: Directly invoke
filter()
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
list.filter {
println("filter : $it")
it % 2 == 0
}
.map {
println("map : $it")
it * 2 + 1
}
.forEach {
println("forEach : $it")
}
}
Console outputs:

It is obvious that ① and ② have the same output. Before filter(), the collection has been transformed into a lazy sequence, in Chinese "懒序列". In this case, the whole program is like a water pipe, and forEach() is like a water tap, called "terminal operator".
The list [1, 2, 3, 4] "flows" along with the "water pipe":
1 ----> "filter : 1" ----> filtered;
2 ----> "filter : 2" ----> "map : 2" ----> "forEach : 5";
3 ----> "filter : 3" ----> filtered;
4 ----> "filter : 4" ----> "map : 4" ----> "forEach : 9"
On the other hand, ③ works like this:
[1, 2, 3, 4]
----> "filter : 1", "filter : 2", "filter : 3", "filter : 4"
----> [2, 4]
----> "map : 2", "map : 4"
----> [5, 9]
----> "forEach : 5", "forEach : 9"
In ② and ③, forEach() is called "terminal operator", even there is println() both in filter() and map(), but without forEach(), the water tap, "water won't be flowing", so console outputs nothing.
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
list.asSequence()
.filter {
println("filter : $it")
it % 2 == 0
}
.map {
println("map : $it")
it * 2 + 1
}
}
Console outputs nothing:

1.4 flatMap

public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
list.stream().flatMap(e -> {
ArrayList<Integer> al = new ArrayList<>(e);
for (int i = 0; i < e; i++) {
al.add(i);
}
return al.stream();
});
}
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
list.flatMap { 0 until it }
}
2 Aggregation operations
| function | description |
|---|---|
| sum | to add all elements numbers together and return a result value |
| reduce | to aggregate all elements according to specific rules and return a value whose type is as the same as elements |
| fold | giving a initial value, to aggregate all elements with the initial value according to specific rules and return a value whose type is as the same as initial value. |
2.1 sum
sum() is declared as:
/**
* Returns the sum of all elements in the collection.
*/
@kotlin.jvm.JvmName("sumOfInt")
public fun Iterable<Int>.sum(): Int {
var sum: Int = 0
for (element in this) {
sum += element
}
return sum
}
When we use it:
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
println(list.sum())
}
Console outputs:

2.2 reduce
reduce() is declared as:
/**
* Accumulates value starting with the first element and applying [operation] from left to right to current accumulator value and each element.
*
* @sample samples.collections.Collections.Aggregates.reduce
*/
public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S {
val iterator = this.iterator()
if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.")
var accumulator: S = iterator.next()
while (iterator.hasNext()) {
accumulator = operation(accumulator, iterator.next())
}
return accumulator
}
When we use it:
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
val i = list.reduce { s : Int, t : Int ->
s - t
}
println(i)
}
Console outputs:

2.3 fold

fold() is declared as:
/**
* Accumulates value starting with [initial] value and applying [operation] from left to right to current accumulator value and each element.
*/
public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
var accumulator = initial
for (element in this) accumulator = operation(accumulator, element)
return accumulator
}
When we use it:
fun main() {
val list = listOf<Int>(1, 2, 3, 4)
val s = list.fold(StringBuilder()) {
acc, i -> acc.append(i)
}
println(s)
}
Console outputs:

We should know that sum(), reduce() and fold() are all "terminal operators", they can work like forEach() as the "water tap".
Q&A
-
Q : What is the difference between Rxjava and these sequences mentioned above?
-
A :
-
Q : What are the application scenarios of collection transformation and aggregation?
-
A :