Tương đương Stream.collect Java 8 nào có sẵn trong thư viện Kotlin tiêu chuẩn?


181

Trong Java 8, có Stream.collectcho phép tổng hợp trên các bộ sưu tập. Trong Kotlin, điều này không tồn tại theo cùng một cách, ngoài việc có thể là một tập hợp các hàm mở rộng trong stdlib. Nhưng không rõ những gì tương đương cho các trường hợp sử dụng khác nhau.

Ví dụ, ở đầu JavaDocCollectors là các ví dụ được viết cho Java 8 và khi chuyển chúng sang Kolin, bạn không thể sử dụng các lớp Java 8 khi trên một phiên bản JDK khác, vì vậy có khả năng chúng nên được viết khác nhau.

Về mặt tài nguyên trực tuyến hiển thị các ví dụ về các bộ sưu tập của Kotlin, chúng thường tầm thường và không thực sự so sánh với các trường hợp sử dụng tương tự. Các ví dụ tốt thực sự phù hợp với các trường hợp như được ghi lại cho Java 8 là Stream.collectgì? Danh sách có:

  • Tích lũy tên vào danh sách
  • Tích lũy tên vào một TreeSet
  • Chuyển đổi các phần tử thành chuỗi và nối chúng, phân tách bằng dấu phẩy
  • Tính tổng tiền lương của nhân viên
  • Nhóm nhân viên theo phòng ban
  • Tính tổng tiền lương theo bộ phận
  • Học sinh phân vùng đi qua và thất bại

Với các chi tiết trong JavaDoc được liên kết ở trên.

Lưu ý: câu hỏi này được cố ý viết và trả lời bởi tác giả ( Câu hỏi tự trả lời ), để các câu trả lời thành ngữ cho các chủ đề thường gặp của Kotlin có trong SO. Ngoài ra để làm rõ một số câu trả lời thực sự cũ được viết cho bảng chữ cái của Kotlin không chính xác cho Kotlin ngày nay.


Trong trường hợp bạn không có lựa chọn nào khác ngoài việc sử dụng 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)
Jayson Minard

Câu trả lời:


257

Có các hàm trong Kotlin stdlib cho trung bình, đếm, phân biệt, lọc, tìm, nhóm, nối, ánh xạ, min, max, phân vùng, cắt, sắp xếp, tổng hợp, đến / từ mảng, đến / từ danh sách, đến / từ bản đồ , liên minh, đồng lặp, tất cả các mô hình chức năng, và nhiều hơn nữa. Vì vậy, bạn có thể sử dụng những cái đó để tạo ra 1 lớp lót nhỏ và không cần sử dụng cú pháp phức tạp hơn của Java 8.

Tôi nghĩ rằng điều duy nhất còn thiếu từ Collectorslớp Java 8 tích hợp là tóm tắt (nhưng trong một câu trả lời khác cho câu hỏi này là một giải pháp đơn giản) .

Một điều còn thiếu từ cả hai là gộp theo số đếm, được thấy trong một câu trả lời Stack Overflow khác và cũng có một câu trả lời đơn giản. Một trường hợp thú vị khác là trường hợp này cũng từ Stack Overflow: Cách thành ngữ để đổ chuỗi vào ba danh sách bằng cách sử dụng Kotlin . Và nếu bạn muốn tạo một cái gì đó giống như Stream.collectcho mục đích khác, hãy xem Custom Stream.collect trong Kotlin

EDIT 11.08.2017: Các hoạt động thu thập chunked / windowed đã được thêm vào trong kotlin 1.2 M2, xem https://blog.jetbrains.com/kotlin/2017/08/kotlin-1-2-m2-is-out/


Việc khám phá toàn bộ Tham chiếu API cho kotlin.collections luôn là điều tốt trước khi tạo các hàm mới có thể tồn tại ở đó.

Dưới đây là một số chuyển đổi từ các Stream.collectví dụ Java 8 sang tương đương trong Kotlin:

Tích lũy tên vào danh sách

// Java:  
List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());
// Kotlin:
val list = people.map { it.name }  // toList() not needed

Chuyển đổi các phần tử thành chuỗi và nối chúng, phân tách bằng dấu phẩy

// Java:
String joined = things.stream()
                       .map(Object::toString)
                       .collect(Collectors.joining(", "));
// Kotlin:
val joined = things.joinToString(", ")

Tính tổng tiền lương của nhân viên

// Java:
int total = employees.stream()
                      .collect(Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val total = employees.sumBy { it.salary }

Nhóm nhân viên theo phòng ban

// Java:
Map<Department, List<Employee>> byDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));
// Kotlin:
val byDept = employees.groupBy { it.department }

Tính tổng tiền lương theo bộ phận

// Java:
Map<Department, Integer> totalByDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                     Collectors.summingInt(Employee::getSalary)));
// Kotlin:
val totalByDept = employees.groupBy { it.dept }.mapValues { it.value.sumBy { it.salary }}

Học sinh phân vùng đi qua và thất bại

// Java:
Map<Boolean, List<Student>> passingFailing =
     students.stream()
             .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
// Kotlin:
val passingFailing = students.partition { it.grade >= PASS_THRESHOLD }

Tên của các thành viên nam

// Java:
List<String> namesOfMaleMembers = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .map(p -> p.getName())
    .collect(Collectors.toList());
// Kotlin:
val namesOfMaleMembers = roster.filter { it.gender == Person.Sex.MALE }.map { it.name }

Tên nhóm của các thành viên trong danh sách theo giới tính

// Java:
Map<Person.Sex, List<String>> namesByGender =
      roster.stream().collect(
        Collectors.groupingBy(
            Person::getGender,                      
            Collectors.mapping(
                Person::getName,
                Collectors.toList())));
// Kotlin:
val namesByGender = roster.groupBy { it.gender }.mapValues { it.value.map { it.name } }   

Lọc một danh sách sang danh sách khác

// Java:
List<String> filtered = items.stream()
    .filter( item -> item.startsWith("o") )
    .collect(Collectors.toList());
// Kotlin:
val filtered = items.filter { it.startsWith('o') } 

Tìm chuỗi ngắn nhất một danh sách

// Java:
String shortest = items.stream()
    .min(Comparator.comparing(item -> item.length()))
    .get();
// Kotlin:
val shortest = items.minBy { it.length }

Đếm các mục trong danh sách sau khi bộ lọc được áp dụng

// Java:
long count = items.stream().filter( item -> item.startsWith("t")).count();
// Kotlin:
val count = items.filter { it.startsWith('t') }.size
// but better to not filter, but count with a predicate
val count = items.count { it.startsWith('t') }

và trên đó đi ... Trong mọi trường hợp, không yêu cầu gấp, giảm hoặc chức năng khác đặc biệt để bắt chước Stream.collect. Nếu bạn có thêm trường hợp sử dụng, hãy thêm chúng vào bình luận và chúng ta có thể thấy!

Về sự lười biếng

Nếu bạn muốn lười biếng xử lý một chuỗi, bạn có thể chuyển đổi sang Sequencesử dụng asSequence()trước chuỗi. Vào cuối chuỗi chức năng, bạn thường kết thúc với một Sequence. Sau đó, bạn có thể sử dụng toList(), toSet(), toMap()hoặc một số chức năng khác thực hiện chủ Sequenceở cuối.

// switch to and from lazy
val someList = items.asSequence().filter { ... }.take(10).map { ... }.toList()

// switch to lazy, but sorted() brings us out again at the end
val someList = items.asSequence().filter { ... }.take(10).map { ... }.sorted()

Tại sao không có loại?!?

Bạn sẽ nhận thấy các ví dụ về Kotlin không chỉ định các loại. Điều này là do Kotlin có kiểu suy luận đầy đủ và hoàn toàn loại an toàn tại thời điểm biên dịch. Hơn cả Java vì nó cũng có các loại không thể và có thể giúp ngăn chặn NPE đáng sợ. Vì vậy, điều này trong Kotlin:

val someList = people.filter { it.age <= 30 }.map { it.name }

giống như:

val someList: List<String> = people.filter { it.age <= 30 }.map { it.name }

Bởi vì Kotlin biết những gì peoplelà, và đó people.ageIntdo đó biểu thức lọc chỉ cho phép so với một Int, và đó people.namelà một Stringdo đó mapbước tạo ra một List<String>(readonly Listcủa String).

Bây giờ, nếu peoplecó thể null, như List<People>?sau đó:

val someList = people?.filter { it.age <= 30 }?.map { it.name }

Trả về một List<String>?giá trị cần phải được kiểm tra null ( hoặc sử dụng một trong các toán tử Kotlin khác cho các giá trị null , xem cách thành ngữ này của Kotlin để xử lý các giá trị nullable và cả cách xử lý danh sách rỗng hoặc rỗng trong Kotlin )

Xem thêm:


Có tương đương vớiallelStream () của Java8 trong Kotlin không?
arnab

Câu trả lời về các bộ sưu tập bất biến và Kotlin là câu trả lời tương tự cho @arnab ở đây song song, các thư viện khác tồn tại, sử dụng chúng: stackoverflow.com/a
Jayson Minard

2
@arnab Bạn có thể muốn xem hỗ trợ của Kotlin cho các tính năng Java 7/8 (cụ thể là kotlinx-support-jdk8) đã được cung cấp vào đầu năm nay: thảo luận.kotlinlang.org / t / jdk7-8-quick-in -kotlin-1-0 / 1625
cướp

Có thực sự thành ngữ khi sử dụng 3 tham chiếu "nó" khác nhau trong một tuyên bố không?
herman

2
Đó là một ưu tiên, trong các mẫu ở trên tôi đã giữ chúng ngắn gọn và chỉ cung cấp một tên địa phương cho một tham số nếu cần thiết.
Jayson Minard

47

Để 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 Sequencetạ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ế Sequencelặp lại một lần, bạn có thể sử dụng constrainOnce()hàm Sequencenhư 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 Maptừ 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ừ Personthể 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.summarizingIntkhô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 summarizingIntphươ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 summarizingIntchứ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 Sequencevà 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.


Trong luồng 5, không có bất kỳ điểm cộng nào để sử dụng hai bản đồ thay vì một bản đồ .map { it.substring(1).toInt() }: như bạn biết loại suy ra rõ ràng là một trong những sức mạnh của kotlin.
Michele Keyboardmico

đúng, nhưng cũng không có nhược điểm nào (để so sánh tôi giữ chúng tách biệt)
Jayson Minard

Nhưng mã Java có thể dễ dàng được tạo ra song song, vì vậy trong nhiều trường hợp, bạn nên gọi mã luồng Java từ Kotlin.
Howard Lovatt

@HowardLovatt có nhiều trường hợp song song không phải là cách để đi, đặc biệt là trong các môi trường đồng thời nặng, nơi bạn đã ở trong một nhóm luồng. Tôi cá là trường hợp sử dụng trung bình KHÔNG song song và đó là trường hợp hiếm. Nhưng tất nhiên, bạn luôn có tùy chọn sử dụng các lớp Java khi bạn thấy phù hợp và không ai trong số này thực sự là mục đích của câu hỏi và câu trả lời này.
Jayson Minard

3

Có một số trường hợp khó tránh khỏi việc gọi collect(Collectors.toList())hoặc tương tự. Trong những trường hợp đó, bạn có thể nhanh chóng thay đổi thành tương đương với Kotlin bằng các hàm mở rộng, chẳng hạn như:

fun <T: Any> Stream<T>.toList(): List<T> = this.collect(Collectors.toList<T>())
fun <T: Any> Stream<T>.asSequence(): Sequence<T> = this.iterator().asSequence()

Sau đó, bạn có thể chỉ cần stream.toList()hoặc stream.asSequence()quay trở lại vào API Kotlin. Một trường hợp như Files.list(path)buộc bạn vào một Streamthời điểm bạn có thể không muốn và các tiện ích mở rộng này có thể giúp bạn chuyển trở lại các bộ sưu tập tiêu chuẩn và API Kotlin.


2

Thêm về sự lười biếng

Hãy lấy giải pháp ví dụ cho "Tính tổng tiền lương theo bộ phận" do Jayson đưa ra:

val totalByDept = employees.groupBy { it.dept }.mapValues { it.value.sumBy { it.salary }}

Để làm cho sự lười biếng này (tức là tránh tạo bản đồ trung gian trong groupBybước), không thể sử dụng asSequence(). Thay vào đó, chúng ta phải sử dụng groupingByfoldvận hành:

val totalByDept = employees.groupingBy { it.dept }.fold(0) { acc, e -> acc + e.salary }

Đối với một số người, điều này thậm chí có thể dễ đọc hơn, vì ban đầu bạn không xử lý các mục bản đồ: it.valuephần trong giải pháp gây khó hiểu cho tôi.

Vì đây là trường hợp phổ biến và chúng tôi không muốn viết ra foldmỗi lần, nên tốt hơn là chỉ cung cấp một sumByhàm chung trên Grouping:

public inline fun <T, K> Grouping<T, K>.sumBy(
        selector: (T) -> Int
): Map<K, Int> = 
        fold(0) { acc, element -> acc + selector(element) }

để chúng ta có thể viết một cách đơn giản:

val totalByDept = employees.groupingBy { it.dept }.sumBy { it.salary }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.