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));
}
Copy the code
for(i:Int in 0 until list.size) {
    println(list[i])
}
Copy the code

Advanced for loop

for(int element : list) {
    System.out.println(element);
}
Copy the code
for (element in list) {
    println(element)
}
Copy the code

forEach

list.forEach((element)->{
    System.out.println(element);
})
Copy the code
list.forEach{
    println(it)
}
Copy the code

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 the filter

//since Java 8
list.stream().filter(e -> e % 2= =0);
Copy the code
val list1 = list.filter { it % 2= =0 }
Copy the code
val list2 = list.asSequence().filter { it % 2= =0 }
Copy the code

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 the map

list.stream().map(e -> e * 2 +1)
Copy the code
val list1 = list.map { it * 2 + 1 }
Copy the code
val list2 = list.asSequence().map { it * 2 + 1 }
Copy the code

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.

  • 1) Java: First invokestream() and then invoke filter()
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); }); }}Copy the code

Console outputs:

  • (2) the Kotlin: First invokeasSequence() and then invoke filter()
fun main(a) {
    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")}}Copy the code

Console outputs:

  • (3) Kotlin: Directly invokefilter()
fun main(a) {
    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")}}Copy the code

Console outputs:

It goes without saying that ① and ② have the same output. 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(a) {
    val list = listOf<Int> (1.2.3.4)
    list.asSequence()
        .filter {
            println("filter : $it")
            it % 2= =0
        }
        .map {
            println("map : $it")
            it * 2 + 1}}Copy the code

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();
   });
}
Copy the code
fun main(a) {
   val list = listOf<Int> (1.2.3.4)
   list.flatMap { 0 until it }
}
Copy the code

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 the sum

sum() is declared as:

/** * Returns the sum of all elements in the collection. */
@kotlin.jvm.JvmName("sumOfInt")
public fun可迭代<Int>.sum(a): Int {
    var sum: Int = 0
    for (element in this) {
        sum += element
    }
    return sum
}
Copy the code

When we use it:

fun main(a) {
   val list = listOf<Int> (1.2.3.4)
   println(list.sum())
}
Copy the code

Console outputs:

2.2 the 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>可迭代<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
}
Copy the code

When we use it:

fun main(a) {
    val list = listOf<Int> (1.2.3.4)
    val i = list.reduce { s : Int, t : Int ->
        s - t
    }
    println(i)
}
Copy the code

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>可迭代<T>.fold(initial: R, operation: (acc: R, T) -> R): R {
    var accumulator = initial
    for (element in this) accumulator = operation(accumulator, element)
    return accumulator
}
Copy the code

When we use it:

fun main(a) {
    val list = listOf<Int> (1.2.3.4)
    val s = list.fold(StringBuilder()) {
        acc, i ->  acc.append(i)
    }
    println(s)
}
Copy the code

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 :