Java转Kotlin:集合变换、聚合与懒序列

·  阅读 320

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 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);
                });
    }
}
复制代码

Console outputs:

  • ② Kotlin: First invoke asSequence() and then invoke filter()
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 :

分类:
代码人生
标签:
分类:
代码人生
标签:
收藏成功!
已添加到「」, 点击更改