一、集合比较

Dart 中,DeepCollectionEquality 类是 collection 包中的一个类,用于比较两个集合(如 ListSetMap 等)是否相等,并且进行深度比较。这意味着它不会比较集合的引用,而是会递归地比较集合中的每个元素的内容,确保集合内部的内容也完全相同

通常,== 运算符只能比较对象的引用是否相等,而 DeepCollectionEquality 会比较集合中每个元素的值。

依赖包:

collection: ^1.18.0
import 'package:collection/collection.dart';

void main() {
  // 创建两个相同的嵌套列表
  var list1 = [
    {'name': 'Alice', 'age': 30},
    {'name': 'Bob', 'age': 25},
  ];
  var list2 = [
    {'name': 'Alice', 'age': 30},
    {'name': 'Bob', 'age': 25},
  ];

  // 使用 DeepCollectionEquality 进行深度比较
  var equality = DeepCollectionEquality();

  print(equality.equals(list1, list2)); // 输出: true
}

实际开发中的代码:

import 'package:collection/collection.dart';

bool _compare(dynamic a, dynamic b) {
  if (a is String || a is int || a is double || a is bool) {
    return a == b;
  } else if (a is Set || a is Map || a is List || a is Iterable) {
    return const DeepCollectionEquality().equals(a, b);
  }

  return a == b;
}

二、用==比较对象类型

上面提到了话题是比较两个集合是否完全一样,比较的是两个集合中的所有属性,包含元素的顺序;

但有时候我们也希望通过==符号就能简单的对边两个对象是否相等,就像比较两个数字或字符串一样;

假设我们有一个类:

class Person {
  final String id;
  final String name;

  Person({required this.id, required this.name});
}

我们需要简单的通过==来判断两个Person类对象是否相等,默认情况下,比较的是引用地址:

/// p1、p2引用地址不一样,所以不相同
Person p1 = Person(id: 1, name: '张三');
Person p2 = Person(id: 1, name: '张三');
Person p3 = p1;
print(p1 == p2); // 输出 false
print(p1 == p3); // 引用地址相同,输出 true

既然默认情况下,比较的是引用地址,而不是比较具体属性,那我们需要重写类的==符号,来改变这一规则,修改后的Person


class Person {
  final int id;
  final String name;

  Person({required this.id, required this.name});

  @override
  bool operator ==(covariant Object other) {
    /// identical(this, other) 用于判断两个对象的引用地址是否相同——默认情况
    /// runtimeType == other.runtimeType 用于判断两个对象的类型是否相同
    /// 最后,比较类的各个属性是否相等
    /// 所以:只要是引用相等,或者,属于同一种类,并且各个属性相等,那么两个都想就相等;
    return identical(this, other) ||
        other is Person &&
            runtimeType == other.runtimeType &&
            id == other.id &&
            name == other.name;
  }
}

Person改成如上结构后:
前面的p1和p2相等了。

既然p1和p2相等了,那么包含p1的集合是否contains了p2呢?答案是否定了,接着往下看。

var set = {p1};
print(set.contains(p2)); // 期望 true,但实际是 false

思考:是不是以为set.contains(p2)返回true呢?实际上返回的还是false,为什么呢?

实际原因是:

  1. 集合类型(如 Set、Map 的 key)在判断元素是否相等时,既会用 ==,也会用 hashCode。
  2. 只有当两个对象的 hashCode 相等时,才会进一步用 == 判断是否“真正相等”。
  3. 如果你只重写了 ==,但没有重写 hashCode,那么即使两个对象“看起来相等”,它们的 hashCode 可能不同,导致集合类无法正确识别它们为同一个元素。

所以需要重写hashCode:

@override
int get hashCode => id.hashCode;

最后的Person类:

class Person {
  final int id;
  final String name;

  Person({required this.id, required this.name});

  @override
  bool operator ==(covariant Object other) {
    /// identical(this, other) 用于判断两个对象的引用地址是否相同——默认情况
    /// runtimeType == other.runtimeType 用于判断两个对象的类型是否相同
    /// 最后,比较类的各个属性是否相等
    /// 所以:只要是引用相等,或者,属于同一种类,并且各个属性相等,那么两个都想就相等;
    return identical(this, other) ||
        other is Person &&
            runtimeType == other.runtimeType &&
            id == other.id &&
            name == other.name;
  }

  @override
  int get hashCode => id.hashCode;
}

qngyun1029
980 声望15 粉丝