Dart 中的 Comparator
最后修改于 2025 年 5 月 25 日
本教程将探讨 Dart 的 Comparator<T>
类型,它支持对集合进行自定义排序。您将学习如何为内置类型和自定义对象创建可重用的比较函数,并通过实际示例演示各种排序场景。
Comparator 概述
Comparator<T>
是一种函数类型,它定义了如何对类型为 T
的两个对象进行排序。它遵循许多编程语言中使用的标准比较约定,返回一个整数,指示两个项目的相对顺序。
比较器函数的签名是
typedef Comparator<T> = int Function(T a, T b);
它必须返回
返回值 | 含义 |
---|---|
负整数 | a 排在 b 前面 |
零 | a 和 b 相等 |
正整数 | a 排在 b 后面 |
Dart 的 Comparable
接口提供了一个 compareTo
方法,该方法遵循相同的约定。许多内置类型都实现了 Comparable
,使其实例自然可排序。
基本 Comparator 用法
本示例演示了使用内联和外部比较函数的Sprites的基本 Comparator 用法。我们将使用不同的方法对数字和字符串进行排序。
// External comparator function int compareNumbers(int a, int b) { return a.compareTo(b); } void main() { // Sorting numbers with external comparator List<int> numbers = [5, 2, 9, 1, 7]; numbers.sort(compareNumbers); print('Sorted numbers: $numbers'); // Sorting strings with inline comparator List<String> words = ['apple', 'banana', 'cherry']; words.sort((a, b) => a.compareTo(b)); print('Sorted words: $words'); // Reverse sorting with external comparator int reverseStringComparator(String a, String b) => b.compareTo(a); words.sort(reverseStringComparator); print('Reverse sorted words: $words'); }
该示例显示了三种排序场景:使用外部比较器对数字进行排序,使用内联比较器对字符串进行排序,以及使用另一个外部比较器对字符串进行反向排序。每种都演示了标准的比较模式。
像 compareNumbers
这样的外部比较器可以在您的代码库中重用,而内联比较器则适用于一次性排序需求。compareTo
方法为许多类型提供了自然排序。
$ dart run basic_comparator.dart Sorted numbers: [1, 2, 5, 7, 9] Sorted words: [apple, banana, cherry] Reverse sorted words: [cherry, banana, apple]
排序自定义对象
本示例展示了如何使用 Comparator 对自定义对象进行排序。我们将定义一个 Person
类并创建用于不同属性的 Comparator。
class Person { final String name; final int age; final double height; Person(this.name, this.age, this.height); @override String toString() => '$name ($age years, ${height}m)'; } // External comparators int compareByAge(Person a, Person b) => a.age.compareTo(b.age); int compareByName(Person a, Person b) => a.name.compareTo(b.name); int compareByHeight(Person a, Person b) => a.height.compareTo(b.height); void main() { List<Person> people = [ Person('Alice', 30, 1.65), Person('Bob', 25, 1.80), Person('Charlie', 35, 1.75) ]; // Sort by age people.sort(compareByAge); print('By age:'); people.forEach(print); // Sort by name people.sort(compareByName); print('\nBy name:'); people.forEach(print); // Sort by height (inline comparator) people.sort((a, b) => compareByHeight(a, b)); print('\nBy height:'); people.forEach(print); }
我们为 Person
类定义了三个外部比较器,每个比较器按不同的属性排序。该示例演示了如何通过传递不同的比较函数轻松更改排序标准。
toString
的重写提供了可读的输出,我们使用 forEach(print)
来显示排序列表。请注意,我们也可以内联使用外部 compareByHeight
函数。
$ dart run object_comparator.dart By age: Bob (25 years, 1.8m) Alice (30 years, 1.65m) Charlie (35 years, 1.75m) By name: Alice (30 years, 1.65m) Bob (25 years, 1.8m) Charlie (35 years, 1.75m) By height: Alice (30 years, 1.65m) Charlie (35 years, 1.75m) Bob (25 years, 1.8m)
多字段排序
对于更复杂的排序需求,您可以比较多个字段。本示例展示了如何创建按主键和次键排序的比较器。
class Product { final String category; final String name; final double price; Product(this.category, this.name, this.price); @override String toString() => '$category / $name: \$$price'; } // Compare by category, then by price int compareProducts(Product a, Product b) { var categoryCompare = a.category.compareTo(b.category); if (categoryCompare != 0) return categoryCompare; return a.price.compareTo(b.price); } // Compare by price descending, then by name int compareByPriceDescThenName(Product a, Product b) { var priceCompare = b.price.compareTo(a.price); // Descending if (priceCompare != 0) return priceCompare; return a.name.compareTo(b.name); } void main() { List<Product> products = [ Product('Electronics', 'Laptop', 999.99), Product('Food', 'Apple', 0.99), Product('Electronics', 'Phone', 699.99), Product('Food', 'Banana', 0.59), Product('Electronics', 'Tablet', 499.99), ]; // Sort by category then price products.sort(compareProducts); print('By category then price:'); products.forEach(print); // Sort by price (descending) then name products.sort(compareByPriceDescThenName); print('\nBy price (desc) then name:'); products.forEach(print); }
该示例演示了两种多字段排序策略。第一个按类别(主键)然后按价格(次键)对产品进行排序。第二个首先按降序价格排序,然后按相同价格的商品名称排序。
每个比较器首先检查主字段 - 如果这些值不同,它将立即返回该比较结果。只有当主字段相同时,它才会比较次字段。此模式可以扩展到任意数量的字段。
$ dart run multi_field_comparator.dart By category then price: Electronics / Tablet: $499.99 Electronics / Phone: $699.99 Electronics / Laptop: $999.99 Food / Banana: $0.59 Food / Apple: $0.99 By price (desc) then name: Electronics / Laptop: $999.99 Electronics / Phone: $699.99 Electronics / Tablet: $499.99 Food / Apple: $0.99 Food / Banana: $0.59
Comparator 工具
Dart 在 package:collection
库中提供了有用的 Comparator 工具。本示例演示了 compareNatural
、compareAsciiUpperCase
以及组合 Comparator。
import 'package:collection/collection.dart'; class Student { final String name; final int grade; final DateTime birthDate; Student(this.name, this.grade, this.birthDate); @override String toString() => '$name (Grade $grade, DOB: ${birthDate.year})'; } void main() { // Natural string comparison (case sensitive) List<String> names = ['alice', 'Bob', 'Charlie', 'dave']; names.sort(compareNatural); print('Natural sort: $names'); // Case-insensitive comparison names.sort(compareAsciiUpperCase); print('Case-insensitive sort: $names'); // Composing comparators List<Student> students = [ Student('Alice', 10, DateTime(2007, 5, 15)), Student('Bob', 10, DateTime(2007, 3, 20)), Student('Charlie', 11, DateTime(2006, 8, 10)), Student('Alice', 9, DateTime(2008, 2, 5)), ]; // Sort by grade, then name, then birthdate var studentComparator = Comparator<Student>((a, b) { var gradeCompare = a.grade.compareTo(b.grade); if (gradeCompare != 0) return gradeCompare; var nameCompare = compareNatural(a.name, b.name); if (nameCompare != 0) return nameCompare; return a.birthDate.compareTo(b.birthDate); }); students.sort(studentComparator); print('\nSorted students:'); students.forEach(print); }
package:collection
库提供了几个内置的 Comparator。compareNatural
执行区分大小写的字符串比较,而 compareAsciiUpperCase
通过在比较之前将字符串转换为大写来提供不区分大小写的排序。
对于 Student
类,我们组合了一个自定义比较器,该比较器首先按年级排序,然后按姓名,然后按出生日期排序。这展示了如何在保持代码可读性的同时构建复杂的排序逻辑。
$ dart run comparator_utilities.dart Natural sort: [Bob, Charlie, alice, dave] Case-insensitive sort: [alice, Bob, Charlie, dave] Sorted students: Alice (Grade 9, DOB: 2008) Bob (Grade 10, DOB: 2007) Alice (Grade 10, DOB: 2007) Charlie (Grade 11, DOB: 2006)
Comparator 工厂
为了获得最大的灵活性,您可以创建 Comparator 工厂,根据参数生成比较函数。本示例演示了一个为任何可比较属性创建比较器的工厂。
class Book { final String title; final String author; final int year; final double rating; Book(this.title, this.author, this.year, this.rating); @override String toString() => '$title by $author ($year) ★$rating'; } // Comparator factory for any Comparable property Comparator<T> compareBy<T, V extends Comparable<V>>(V Function(T) selector) { return (a, b) => selector(a).compareTo(selector(b)); } // Comparator factory for descending order Comparator<T> compareByDescending<T, V extends Comparable<V>>(V Function(T) selector) { return (a, b) => selector(b).compareTo(selector(a)); } void main() { List<Book> books = [ Book('Dart in Action', 'Manning', 2023, 4.5), Book('Flutter Basics', 'OReilly', 2022, 4.2), Book('Dart in Action', 'Manning', 2021, 4.7), Book('Advanced Flutter', 'Apress', 2023, 4.8), ]; // Sort by title books.sort(compareBy<Book, String>((b) => b.title)); print('By title:'); books.forEach(print); // Sort by year descending then rating descending books.sort((a, b) { var yearCompare = compareByDescending<Book, num>((b) => b.year)(a, b); if (yearCompare != 0) return yearCompare; return compareByDescending<Book, num>((b) => b.rating)(a, b); }); print('\nBy year (desc) then rating (desc):'); books.forEach(print); // Sort by author then title books.sort(compareBy<Book, String>((b) => b.author) .thenCompare(compareBy<Book, String>((b) => b.title))); print('\nBy author then title:'); books.forEach(print); } extension ComparatorExtensions<T> on Comparator<T> { Comparator<T> thenCompare(Comparator<T> other) { return (a, b) { var result = this(a, b); if (result != 0) return result; return other(a, b); }; } }
compareBy
工厂创建的比较器可以从对象中提取可比较的属性。thenCompare
扩展方法将比较器链接在一起,类似于 Java 中的 thenComparing。
这种方法提供了最大的灵活性 - 您可以轻松地为任何属性创建比较器,而无需编写重复的比较逻辑。该示例展示了如何用最少的代码按不同属性组合对书籍进行排序。
$ dart run comparator_factories.dart By title: Advanced Flutter by Apress (2023) ★4.8 Dart in Action by Manning (2023) ★4.5 Dart in Action by Manning (2021) ★4.7 Flutter Basics by OReilly (2022) ★4.2 By year (desc) then rating (desc): Advanced Flutter by Apress (2023) ★4.8 Dart in Action by Manning (2023) ★4.5 Flutter Basics by OReilly (2022) ★4.2 Dart in Action by Manning (2021) ★4.7 By author then title: Advanced Flutter by Apress (2023) ★4.8 Dart in Action by Manning (2023) ★4.5 Dart in Action by Manning (2021) ★4.7 Flutter Basics by OReilly (2022) ★4.2
来源
Dart Comparator API
Dart Comparable API
Dart Collection 包
Dart 的 Comparator<T>
提供了强大而灵活的排序功能。通过创建可重用的比较函数并利用 Dart 的函数式特性,您可以实现复杂的排序逻辑,同时保持代码的整洁和可维护性。所示技术从简单的属性比较到复杂的 Faça排序场景都适用。
作者
列出 所有 Dart 教程。