Để biết thêm ví dụ, đây là tất cả các mẫu từ Hướng dẫn luồng Java 8 được chuyển đổi thành Kotlin. Tiêu đề của mỗi ví dụ, được lấy từ bài viết nguồn:
Luồng hoạt động như thế nào
// Java:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
// C1
// C2
// Kotlin:
val list = listOf("a1", "a2", "b1", "c2", "c1")
list.filter { it.startsWith('c') }.map (String::toUpperCase).sorted()
.forEach (::println)
Các loại luồng khác nhau # 1
// Java:
Arrays.asList("a1", "a2", "a3")
.stream()
.findFirst()
.ifPresent(System.out::println);
// Kotlin:
listOf("a1", "a2", "a3").firstOrNull()?.apply(::println)
hoặc, tạo một hàm mở rộng trên Chuỗi có tên ifPftime:
// Kotlin:
inline fun String?.ifPresent(thenDo: (String)->Unit) = this?.apply { thenDo(this) }
// now use the new extension function:
listOf("a1", "a2", "a3").firstOrNull().ifPresent(::println)
Xem thêm: apply()
chức năng
Xem thêm: Chức năng mở rộng
Xem thêm: ?.
Toán tử cuộc gọi an toàn và nói chung là nullable : Trong Kotlin, cách thức thành ngữ để xử lý các giá trị nullable, tham chiếu hoặc chuyển đổi chúng là gì
Các loại luồng khác nhau # 2
// Java:
Stream.of("a1", "a2", "a3")
.findFirst()
.ifPresent(System.out::println);
// Kotlin:
sequenceOf("a1", "a2", "a3").firstOrNull()?.apply(::println)
Các loại luồng khác nhau # 3
// Java:
IntStream.range(1, 4).forEach(System.out::println);
// Kotlin: (inclusive range)
(1..3).forEach(::println)
Các loại luồng khác nhau # 4
// Java:
Arrays.stream(new int[] {1, 2, 3})
.map(n -> 2 * n + 1)
.average()
.ifPresent(System.out::println); // 5.0
// Kotlin:
arrayOf(1,2,3).map { 2 * it + 1}.average().apply(::println)
Các loại luồng khác nhau # 5
// Java:
Stream.of("a1", "a2", "a3")
.map(s -> s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println); // 3
// Kotlin:
sequenceOf("a1", "a2", "a3")
.map { it.substring(1) }
.map(String::toInt)
.max().apply(::println)
Các loại luồng khác nhau # 6
// Java:
IntStream.range(1, 4)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
// a1
// a2
// a3
// Kotlin: (inclusive range)
(1..3).map { "a$it" }.forEach(::println)
Các loại luồng khác nhau # 7
// Java:
Stream.of(1.0, 2.0, 3.0)
.mapToInt(Double::intValue)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
// a1
// a2
// a3
// Kotlin:
sequenceOf(1.0, 2.0, 3.0).map(Double::toInt).map { "a$it" }.forEach(::println)
Tại sao đặt hàng vấn đề
Phần này của Hướng dẫn luồng Java 8 giống với Kotlin và Java.
Sử dụng lại luồng
Trong Kotlin, nó phụ thuộc vào loại bộ sưu tập cho dù nó có thể được tiêu thụ nhiều lần. A Sequence
tạo ra một trình vòng lặp mới mỗi lần và trừ khi nó xác nhận "chỉ sử dụng một lần", nó có thể đặt lại về điểm bắt đầu mỗi khi nó được thực hiện. Do đó, trong khi điều sau không thành công trong luồng Java 8, nhưng hoạt động trong Kotlin:
// Java:
Stream<String> stream =
Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("b"));
stream.anyMatch(s -> true); // ok
stream.noneMatch(s -> true); // exception
// Kotlin:
val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }
stream.forEach(::println) // b1, b2
println("Any B ${stream.any { it.startsWith('b') }}") // Any B true
println("Any C ${stream.any { it.startsWith('c') }}") // Any C false
stream.forEach(::println) // b1, b2
Và trong Java để có được hành vi tương tự:
// Java:
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
Do đó, trong Kotlin, nhà cung cấp dữ liệu quyết định liệu nó có thể thiết lập lại và cung cấp trình lặp mới hay không. Nhưng nếu bạn muốn cố ý hạn chế Sequence
lặp lại một lần, bạn có thể sử dụng constrainOnce()
hàm Sequence
như sau:
val stream = listOf("d2", "a2", "b1", "b3", "c").asSequence().filter { it.startsWith('b' ) }
.constrainOnce()
stream.forEach(::println) // b1, b2
stream.forEach(::println) // Error:java.lang.IllegalStateException: This sequence can be consumed only once.
Hoạt động nâng cao
Thu thập ví dụ # 5 (vâng, tôi đã bỏ qua những câu đã có trong câu trả lời khác)
// Java:
String phrase = persons
.stream()
.filter(p -> p.age >= 18)
.map(p -> p.name)
.collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));
System.out.println(phrase);
// In Germany Max and Peter and Pamela are of legal age.
// Kotlin:
val phrase = persons.filter { it.age >= 18 }.map { it.name }
.joinToString(" and ", "In Germany ", " are of legal age.")
println(phrase)
// In Germany Max and Peter and Pamela are of legal age.
Và như một lưu ý phụ, trong Kotlin, chúng ta có thể tạo các lớp dữ liệu đơn giản và khởi tạo dữ liệu thử nghiệm như sau:
// Kotlin:
// data class has equals, hashcode, toString, and copy methods automagically
data class Person(val name: String, val age: Int)
val persons = listOf(Person("Tod", 5), Person("Max", 33),
Person("Frank", 13), Person("Peter", 80),
Person("Pamela", 18))
Thu thập ví dụ # 6
// Java:
Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" + name2));
System.out.println(map);
// {18=Max, 23=Peter;Pamela, 12=David}
Ok, một trường hợp quan tâm hơn ở đây cho Kotlin. Trước tiên, các câu trả lời sai để khám phá các biến thể của việc tạo một Map
từ bộ sưu tập / chuỗi:
// Kotlin:
val map1 = persons.map { it.age to it.name }.toMap()
println(map1)
// output: {18=Max, 23=Pamela, 12=David}
// Result: duplicates overridden, no exception similar to Java 8
val map2 = persons.toMap({ it.age }, { it.name })
println(map2)
// output: {18=Max, 23=Pamela, 12=David}
// Result: same as above, more verbose, duplicates overridden
val map3 = persons.toMapBy { it.age }
println(map3)
// output: {18=Person(name=Max, age=18), 23=Person(name=Pamela, age=23), 12=Person(name=David, age=12)}
// Result: duplicates overridden again
val map4 = persons.groupBy { it.age }
println(map4)
// output: {18=[Person(name=Max, age=18)], 23=[Person(name=Peter, age=23), Person(name=Pamela, age=23)], 12=[Person(name=David, age=12)]}
// Result: closer, but now have a Map<Int, List<Person>> instead of Map<Int, String>
val map5 = persons.groupBy { it.age }.mapValues { it.value.map { it.name } }
println(map5)
// output: {18=[Max], 23=[Peter, Pamela], 12=[David]}
// Result: closer, but now have a Map<Int, List<String>> instead of Map<Int, String>
Và bây giờ cho câu trả lời đúng:
// Kotlin:
val map6 = persons.groupBy { it.age }.mapValues { it.value.joinToString(";") { it.name } }
println(map6)
// output: {18=Max, 23=Peter;Pamela, 12=David}
// Result: YAY!!
Chúng tôi chỉ cần tham gia các giá trị khớp nhau để thu gọn danh sách và cung cấp một biến áp jointToString
để chuyển từ Person
thể hiện sang Person.name
.
Thu thập ví dụ # 7
Ok, điều này có thể dễ dàng thực hiện mà không cần tùy chỉnh Collector
, vì vậy hãy giải quyết theo cách của Kotlin, sau đó tạo ra một ví dụ mới cho thấy cách thực hiện một quy trình tương tự Collector.summarizingInt
không tồn tại trong Kotlin.
// Java:
Collector<Person, StringJoiner, String> personNameCollector =
Collector.of(
() -> new StringJoiner(" | "), // supplier
(j, p) -> j.add(p.name.toUpperCase()), // accumulator
(j1, j2) -> j1.merge(j2), // combiner
StringJoiner::toString); // finisher
String names = persons
.stream()
.collect(personNameCollector);
System.out.println(names); // MAX | PETER | PAMELA | DAVID
// Kotlin:
val names = persons.map { it.name.toUpperCase() }.joinToString(" | ")
Đó không phải là lỗi của tôi, họ đã chọn một ví dụ tầm thường !!! Ok, đây là một summarizingInt
phương pháp mới cho Kotlin và một mẫu phù hợp:
Tóm tắt Ví dụ
// Java:
IntSummaryStatistics ageSummary =
persons.stream()
.collect(Collectors.summarizingInt(p -> p.age));
System.out.println(ageSummary);
// IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23}
// Kotlin:
// something to hold the stats...
data class SummaryStatisticsInt(var count: Int = 0,
var sum: Int = 0,
var min: Int = Int.MAX_VALUE,
var max: Int = Int.MIN_VALUE,
var avg: Double = 0.0) {
fun accumulate(newInt: Int): SummaryStatisticsInt {
count++
sum += newInt
min = min.coerceAtMost(newInt)
max = max.coerceAtLeast(newInt)
avg = sum.toDouble() / count
return this
}
}
// Now manually doing a fold, since Stream.collect is really just a fold
val stats = persons.fold(SummaryStatisticsInt()) { stats, person -> stats.accumulate(person.age) }
println(stats)
// output: SummaryStatisticsInt(count=4, sum=76, min=12, max=23, avg=19.0)
Nhưng tốt hơn là tạo một chức năng mở rộng, 2 thực sự phù hợp với các kiểu trong Kotlin stdlib:
// Kotlin:
inline fun Collection<Int>.summarizingInt(): SummaryStatisticsInt
= this.fold(SummaryStatisticsInt()) { stats, num -> stats.accumulate(num) }
inline fun <T: Any> Collection<T>.summarizingInt(transform: (T)->Int): SummaryStatisticsInt =
this.fold(SummaryStatisticsInt()) { stats, item -> stats.accumulate(transform(item)) }
Bây giờ bạn có hai cách để sử dụng các summarizingInt
chức năng mới :
val stats2 = persons.map { it.age }.summarizingInt()
// or
val stats3 = persons.summarizingInt { it.age }
Và tất cả những điều này tạo ra kết quả tương tự. Chúng tôi cũng có thể tạo tiện ích mở rộng này để hoạt động Sequence
và cho các kiểu nguyên thủy thích hợp.
Để giải trí, hãy so sánh mã JDK Java với mã tùy chỉnh Kotlin cần có để thực hiện tóm tắt này.
collect(Collectors.toList())
hoặc tương tự, bạn có thể gặp phải vấn đề này: stackoverflow.com/a/35722167/3679676 (vấn đề, có cách giải quyết)