我想问一下我应该如何在Flutter中处理大量清单。当我在要搜索的列表中确实很深的数据项上时,我的应用程序变得超级慢。我的清单是一个数据结构大的70,000+个对象。
以下是我“搜索”列表的方式。
Future<Iterable<SomeDataStruct>> _getAllData() async {
return allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim())));
}
使用FutureBuilder内部的ListView.builder构建列表。
当我搜索并填充列表深处的一个或多个结果时,该应用程序的运行速度非常慢,我单击列表项的时间很短,然后才执行onTap几秒钟。而且,如果我需要更改搜索查询,则在单击TextField之后,需要一些时间才能使软键盘重新显示。
我在哪里犯错或处理错了,我应该怎么做才能在不使应用无法忍受的情况下搜索庞大的列表。
编辑: 更改代码后,我如何不降低应用程序运行速度。这是正确的吗?
String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
if(querySearch!=tempQuery) {
tempQuery = querySearch;
searchResults = allData.where((a) => (a.dataTitle.toLowerCase().contains(querySearch.toLowerCase().trim()))).toList();
}
return searchResults;
}
答案 0 :(得分:1)
包含的物品很贵
包含查询非常昂贵,因为如果可以找到搜索词,则需要在每个位置(最多value.length
-searchTerm.length
)检查每个条目。
将搜索支持限制在字符串的开头将大大提高性能。另外,您可以创建辅助程序数据结构,在该结构中,值的整个列表以开头的相同字符分割为多个部分。如果块仍然太大,则可以为第二个字符添加另一个级别。由于字符数有限,因此查找会很快。
使用数据库可能会减轻一些编程工作(维护索引)。像SQLite这样的数据库可以与专门用于您的查询的索引一起使用。
分成较小的工作块,以允许框架执行其工作
如果您不能只限于“字符串开始”搜索,您仍然可以将数据结构分成较小的块,并为每个异步块调用搜索。这样,在搜索下一个块之前,UI会“呼吸一些”以重新呈现UI。搜索结果将逐步更新。
移出UI线程
另一种方法是启动另一个隔离并在那里进行搜索。另一个隔离可以在另一个CPU(核心)上运行,因此在搜索时不会阻塞UI线程。这样,就不必拆分成大块了。尽管逐步更新UI而不是让用户等到整个搜索结果可用之前,仍然可能是有利的。
另请参见
缓存
将搜索结果保存在内存中也可能有助于提高性能。例如,如果用户输入foo
,然后按退格键,则可以对先前已经计算过的fo
重新使用搜索结果,但这仅在某些情况下有用。
测量
当然,另一个重要的点是进行基准测试。无论您尝试如何提高性能,都应创建基准以了解哪些措施具有什么效果,以及是否值得。您将学到很多有关您的方案,数据,Dart等的信息,这将使您能够做出明智的决定。
答案 1 :(得分:0)
如果列表足够大,搜索列表会使您的应用崩溃。我测试了搜索 150,000 个项目的列表,但我的应用程序崩溃了。如果您搜索的内容靠近列表的开头,它似乎可以工作,但如果您搜索的内容可能位于列表的深处,例如索引 100,000,那么事情会显着减慢,应用程序将冻结。
为了解决这个问题,我使用基于 sqlite 构建的 moor
库转向了数据库解决方案。您只需按照文档设置样板内容,不需要很长时间。
您可以创建一个简单的数据库表,其中只有 2 个字段,id
和另一个用于保存列表值。在本例中,我将另一个字段称为 itemName
在你的数据库类中,你可以有一个函数来进行过滤,例如
Future<List<Item>> getFilteredItems(search) => (select(Items)..where((t) => t.itemName.like(search))).get();
然后您的代码将如下所示:
String tempQuery;
List<SomeDataStruct> searchResults = [];
Future<List<SomeDataStruct>> _getAllData() async {
if(querySearch!=tempQuery) {
tempQuery = querySearch;
query = await MyDatabaseClass().getFilteredItems("%$querySearch%");
for (int i = 0; i<query.length; i++) {
searchResults.add(query[i].itemName); //itemName
}
}
return searchResults;
}
此后不再崩溃或死机。
答案 2 :(得分:-1)
也许是这样:
ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text(data[index]);
},
)