Flutter高效数据比较插件fast_immutable_equatable的使用
Flutter高效数据比较插件fast_immutable_equatable的使用
本Dart包由fast_equatable
(添加了缓存值)和equatable
(添加了不可变性)衍生而来。
迁移
如何从equatable
迁移
- 将
class MyClass with EquatableMixin
重命名为class MyClass with IFastEquatable
。 - 将
class MyClass extends Equatable
重命名为class MyClass extends IFastEquatable
。 - 将
List<Object> get props
重命名为List<Object?> get hashParameters
。 - 添加:
[@override](/user/override) bool get cacheHash => true;
如何从fast_equatable
迁移
- 将
class MyClass with FastEquatable
重命名为class MyClass with IFastEquatable
。 - 通过添加
final
使字段变为不可变(由于类自动标记为不可变,dart linter会提示你)。
概览
哈希值通过双查找技巧在静态映射中进行缓存,以保持不变性。
需要注意的是,在速度方面,此库由于双查找而略慢于fast_equatable
包,但仍然比equatable
包快,特别是在进行大量比较时。
你可以像使用混入一样使用with IFastEquatable
(这是首选选项,因为它更容易集成到现有代码中),或者作为类继承extends IFastEquatable
。
该库提供了哈希缓存功能,以提高List
、Map
、Set
和一般Iterable
集合的速度。
默认情况下,使用广泛传播的詹金斯哈希函数。但你可以自由地实现并提供自己的哈希引擎。如果想创建一个包含新哈希引擎实现的PR。
hashParameters
中的任何类型对象都允许(这与equatable
包中的props
工作方式相同)。默认支持简单类型和标准集合如List
、Iterable
、Map
和Set
。
为了使用来自fast_immutable_collections
的不可变集合如IList
、ISet
和IMap
,只需启用深度相等和哈希:
defaultConfig = ConfigList(isDeepEquals: true, cacheHashCode: true);
然而,由于IFastEquatable
被标记为不可变,你可以使用常规的List
、Map
和Set
而不必担心它们会被修改。
如何在新项目中使用IFastEquatable
推荐使用接口方法:
class A with IFastEquatable {
final String arg1;
final Map<String> arg2;
const A(this.arg1, this.arg2);
[@override](/user/override)
bool get cacheHash => true;
[@override](/user/override)
List<Object?> get hashParameters => [arg1, arg2];
}
如何子类化A
库在处理子类和hashParameters
时强制你遵循一定的风格。重要的是hashParameters
必须用展开操作符调用,否则编译器会发出警告。
class AB extends A {
final IList<int> arg3;
AB(super.arg1, super.arg2, this.arg3);
[@override](/user/override)
bool get cacheHash => true;
[@override](/user/override)
List<Object?> get hashParameters => [...super.hashParameters, arg3];
}
性能测试
在example
目录下,你会发现性能测试代码,展示了由此带来的速度提升。然而,这并不完全可比,因为测试并没有尝试多次使用同一对象进行比较。如果是这种情况,fast_immutable_equatable
会更快。
equatable for 1000000 elements(RunTime): 4789464.0 us.
fast_immutable_equatable (uncached) for 1000000 elements(RunTime): 4467237.0 us.
fast_immutable_equatable (cached) for 1000000 elements(RunTime): 3723849.5 us.
完整示例
以下是完整的示例代码:
import 'dart:math';
import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:equatable/equatable.dart';
import 'package:fast_immutable_equatable/fast_immutable_equatable.dart';
class FastEquatableCached with IFastEquatable {
final String value1;
final List<String>? value2;
const FastEquatableCached(this.value1, this.value2);
[@override](/user/override)
bool get cacheHash => true;
[@override](/user/override)
List<Object?> get hashParameters => [value1, value2];
}
class FastEquatableUncached with IFastEquatable {
final String value1;
final List<String>? value2;
const FastEquatableUncached(this.value1, this.value2);
[@override](/user/override)
bool get cacheHash => false;
[@override](/user/override)
List<Object?> get hashParameters => [value1, value2];
}
// Quick example of how to subclass A.
class A with IFastEquatable {
final String arg1;
final int arg2;
const A(this.arg1, this.arg2);
[@override](/user/override)
bool get cacheHash => true;
[@override](/user/override)
List<Object?> get hashParameters => [arg1, arg2];
}
class AB extends A {
final int arg3;
AB(super.arg1, super.arg2, this.arg3);
[@override](/user/override)
bool get cacheHash => true;
[@override](/user/override)
List<Object?> get hashParameters => [...super.hashParameters, arg2];
}
class TestClassEquatable extends Equatable {
final String value1;
final List<String>? value2;
const TestClassEquatable(this.value1, this.value2);
[@override](/user/override)
List<Object?> get props => [value1, value2];
}
class EquatableBenchmark extends BenchmarkBase {
final List<String> _randsValA;
final List<String> _randsValB;
late final List<TestClassEquatable> objects;
EquatableBenchmark(this._randsValA, this._randsValB)
: assert(_randsValA.length == _randsValB.length),
super('equatable for ${_randsValA.length} elements');
[@override](/user/override)
void setup() {
objects = List.generate(_randsValA.length,
(i) => TestClassEquatable(_randsValA[i], [_randsValB[i]]));
}
[@override](/user/override)
void run() {
final set = <TestClassEquatable>{};
for (final obj in objects) {
set.add(obj);
}
}
}
class FastEquatableUncachedBenchmark extends BenchmarkBase {
final List<String> _randsValA;
final List<String> _randsValB;
late final List<FastEquatableUncached> objects;
FastEquatableUncachedBenchmark(this._randsValA, this._randsValB)
: assert(_randsValA.length == _randsValB.length),
super(
'fast_immutable_equatable (uncached) for ${_randsValA.length} elements');
[@override](/user/override)
void setup() {
objects = List.generate(_randsValA.length,
(i) => FastEquatableUncached(_randsValA[i], [_randsValB[i]]));
}
[@override](/user/override)
void run() {
final set = <FastEquatableUncached>{};
for (final obj in objects) {
set.add(obj);
}
}
}
class FastEquatableCachedBenchmark extends BenchmarkBase {
final List<String> _randsValA;
final List<String> _randsValB;
late final List<FastEquatableCached> objects;
FastEquatableCachedBenchmark(this._randsValA, this._randsValB)
: assert(_randsValA.length == _randsValB.length),
super(
'fast_immutable_equatable (cached) for ${_randsValA.length} elements');
[@override](/user/override)
void setup() {
objects = List.generate(_randsValA.length,
(i) => FastEquatableCached(_randsValA[i], [_randsValB[i]]));
}
[@override](/user/override)
void run() {
final set = <FastEquatableCached>{};
for (final obj in objects) {
set.add(obj);
}
}
}
void main(List<String> args) {
const nAcc = 1000000;
final rand = Random();
final randsVal1 = List.generate(nAcc, (_) => rand.nextInt(nAcc).toString());
final randsVal2 = List.generate(nAcc, (_) => rand.nextInt(nAcc).toString());
EquatableBenchmark(randsVal1, randsVal2).report();
FastEquatableUncachedBenchmark(randsVal1, randsVal2).report();
FastEquatableCachedBenchmark(randsVal1, randsVal2).report();
}
更多关于Flutter高效数据比较插件fast_immutable_equatable的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter高效数据比较插件fast_immutable_equatable的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter开发中,高效地进行数据比较是优化性能和减少不必要渲染的关键步骤之一。fast_immutable_equatable
插件就是为了解决这一问题而设计的,它提供了一个快速且简便的方法来比较不可变对象的相等性。下面是如何在Flutter项目中使用 fast_immutable_equatable
的代码示例。
首先,确保你已经在 pubspec.yaml
文件中添加了 fast_immutable_equatable
依赖:
dependencies:
flutter:
sdk: flutter
fast_immutable_equatable: ^x.y.z # 请替换为最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来,让我们创建一个简单的示例,展示如何使用 fast_immutable_equatable
。
1. 定义一个数据模型
假设我们有一个表示用户信息的模型 User
:
import 'package:fast_immutable_equatable/fast_immutable_equatable.dart';
class User extends EquatableMixin {
final String id;
final String name;
final int age;
User({required this.id, required this.name, required this.age});
@override
List<Object?> get props => [id, name, age];
}
这里,我们使用了 EquatableMixin
,并实现了 props
getter,它返回一个包含所有用于比较相等性的属性的列表。
2. 使用数据模型进行比较
现在,我们可以创建 User
对象并使用 ==
运算符或 ===
运算符(对于值类型比较)来检查它们的相等性:
void main() {
User user1 = User(id: '1', name: 'Alice', age: 30);
User user2 = User(id: '1', name: 'Alice', age: 30);
User user3 = User(id: '2', name: 'Bob', age: 25);
print(user1 == user2); // 输出: true
print(user1 == user3); // 输出: false
}
在这个例子中,user1
和 user2
被认为是相等的,因为它们的所有属性都相同。而 user3
的属性与 user1
不同,所以它们不相等。
3. 在Flutter应用中使用
在Flutter应用中,你可能会在状态管理中使用这些数据模型。例如,在使用 Provider
状态管理库时,可以这样使用:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'user_model.dart'; // 假设User类定义在这个文件中
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => UserProvider(),
),
],
child: MyApp(),
),
);
}
class UserProvider with ChangeNotifier {
User? _user;
User? get user => _user;
void setUser(User user) {
if (_user != user) {
_user = user;
notifyListeners();
}
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('User Info')),
body: Center(
child: Consumer<UserProvider>(
builder: (_, userProvider, __) {
final User? user = userProvider.user;
return user != null
? Text('Name: ${user.name}, Age: ${user.age}')
: Text('No user data');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
final UserProvider userProvider =
Provider.of<UserProvider>(context, listen: false);
userProvider.setUser(User(id: '1', name: 'Alice', age: 30));
},
tooltip: 'Set User',
child: Icon(Icons.add),
),
),
);
}
}
在这个例子中,UserProvider
是一个 ChangeNotifier
,它持有一个 User
对象。当 setUser
方法被调用时,如果新的 User
对象与当前对象不相等(根据 EquatableMixin
的逻辑),它会通知监听器更新UI。
通过这种方式,fast_immutable_equatable
插件帮助你在Flutter应用中高效地管理不可变对象的状态比较,从而优化性能和用户体验。