使用RecyclerView.Adapter过滤大量条目

时间:2016-09-06 13:49:49

标签: java android android-filterable

我有adapter扩展RecyclerView.Adapter<RecyclerView.ViewHolder>并实施Filterable

我的getFilter()实施是:

@Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                mPersonListFiltered = (List<Person>) results.values;
                notifyDataSetChanged();
            }

            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                List<Person> filtered = new ArrayList<>();

                if (constraint == null || constraint.length() == 0) {
                    results.count = mPersonList.size();
                    results.values = mPersonList;
                } else {
                    String name, email, constr = Utils.removeDiacriticalMarks(constraint.toString());
                    for (Person person : mPersonList) {
                        name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                        email = person.getEmail().toLowerCase();
                        if (name.contains(constr) || email.contains(constr)) {
                            filtered.add(person);
                        }
                    }
                    results.count = filtered.size();
                    results.values = filtered;
                }
                return results;
            }
        };
    }

我希望在输入时过滤我的人员列表。如果我的列表大小不超过1k,这可以正常工作,但如果我将它扩展到5k,10k等等,它就开始变得迟钝。而且我理解为什么,对于每个我必须检查是否包含对其姓名和电子邮件的约束的人来说,这太糟糕了。 但我想知道在这种情况下,是否有很多条目,什么是最佳实现或替代方案来实现相同的结果,换句话说,一个快速过滤方法用于本地巨大的列表和&#34; on-the-飞&#34;打字。

感谢。

1 个答案:

答案 0 :(得分:1)

您可以预过滤您的Person条目。

您可以为所有单字符约束创建Map个列表(实际上是Set s),也可能是一些多字符约束。

    Map<String, Set<Person>> mFilteredPersonMap  = new HashMap<>();

当你获得适配器的Person列表时,将它们添加到地图中的一个集合中:

                for (Person person : mPersonList) {
                    String name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                    for (char c : name.toCharArray()) {
                        if (Character.isWhitespace(c)) continue;
                        // you may want to skip other chars i.e. symbols
                        Set<Person> set = mFilteredPersonMap.get(Character.toString(c));
                        if (set == null) {
                            set = new HashSet<>();
                            mFilteredPersonMap.put(Character.toString(c), set);
                        }
                        set.add(person);
                    }
                    // do the same thing for email
                }

我使用了名称中的每个字母,因为您在原始代码中使用了contains(constr)。我的偏好是在单词边界(正则表达式"\\b(\\w)")之后搜索字符并将其用作地图的键。

然后将地图用作第一级过滤器:

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();
            List<Person> filtered = new ArrayList<>();

            if (constraint == null || constraint.length() == 0) {
                results.count = mPersonList.size();
                results.values = mPersonList;
            } else {
                String name, email, constr = Utils.removeDiacriticalMarks(constraint.toString());
                String key = constr.substr(0, 1);
                Set set = mFilteredPersonMap.get(key);
                if (set != null) {
                    // now you are looping through a smaller collection
                    for (Person person : set) {
                        name =  Utils.removeDiacriticalMarks(person.getName().toLowerCase());
                        email = person.getEmail().toLowerCase();
                        if (name.contains(constr) || email.contains(constr)) {
                            filtered.add(person);
                        }
                    }
                }
                results.count = filtered.size();
                results.values = filtered;
            }
            return results;
        }