对List对象列表属性值的快速搜索

来源:互联网 发布:mac office 2011 汉化 编辑:程序博客网 时间:2024/05/20 17:39

对于数据的搜索已有很多成熟的方案,比如Apace Lucene框架,结合ikanalyer等分词器能实现很复杂和高效的搜索,或直接使用sql语言对数据库关键字进行搜索等。

但这些搜索都很重,对于已经加载完成的数据列表并不适用。

比如有这样一个需求:已经加载了一个班的学生在一个List列表中,要根据学生和姓名和住址做一个模糊搜索。因为数据已经加载到List中,存在于内存中,若再从数据库或网络上去使用关键字取值并不是很优的方案。而一般做法是在查找关键字里,遍历整个列表逐个匹配,但若是如手机上做逐个输入搜索的话会产生大量的循环操作,效率很低。

为此,今天分享一种比较对List列表比较高效的搜索方式。

思路:
1、传入数据源List,并指定要搜索的字段;将这些字段的值拼接成一个字符串,并保存每个对象的值的起始和结束位置:
2、搜索时,先使用正则表达式在保存的搜索字符串找到位置,再利用这些位置在索引数据数组中找到对应对象索引;

具体实现:

package com.fansion;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.List;import java.util.regex.Matcher;import java.util.regex.Pattern;public class FSearchTool {    private StringBuffer mKeyWordString = new StringBuffer();    private List<Object> mSearchObjs = new ArrayList<>();    private int[] mIndexes;    public FSearchTool(List<? extends Object> objects, String... fields) throws Exception {        super();        init(objects, fields);    }    private void init(List<? extends Object> objs, String... fields) throws Exception {        if (objs != null) {            mKeyWordString.setLength(0);            mSearchObjs.clear();            mSearchObjs = new ArrayList<>(objs);            mIndexes = new int[mSearchObjs.size() * 2];            int index = 0;            for (int i = 0; i < mSearchObjs.size(); i++) {                Object info = mSearchObjs.get(i);                // 指定要搜索的字段                String searchKey = getSearchKey(info, fields);                // 将该字符串在总字符串中的起终位置保存下来,位置是索引值而非长度                int length = mKeyWordString.length();                mIndexes[index] = length;                mKeyWordString.append(searchKey);                length = mKeyWordString.length();                index++;                // 保存新加搜索字段的索引值                mIndexes[index] = (length > 0) ? length - 1 : 0;                index++;            }        }    }    /**     * 通过反射从对象中取出指定字段的值     */    private String getSearchKey(Object obj, String... fields) throws Exception {        StringBuilder searchKeys = new StringBuilder();        Class<? extends Object> clazz = obj.getClass();        try {            for (String str : fields) {                // 搜索字段使用空格隔开                Field f = clazz.getDeclaredField(str);                f.setAccessible(true);                Object val = f.get(obj);                searchKeys.append(val).append(" ");                f.setAccessible(false);            }        } catch (Exception e) {            throw new Exception("取值异常:" + e.getMessage());        }        return searchKeys.toString();    }    /**     * 搜索结果     *     * @param keyWords     *            搜索的关键字,要去掉首尾的空格     * @return 返回搜索到的对象     */    public List<Object> searchTasks(String keyWords) {        List<Object> searchedTask = new ArrayList<>();        int[] searchIndex = getSearchIndex(keyWords);        for (int index : searchIndex) {            if (index != -1 && index < mSearchObjs.size() * 2) {                Object info = mSearchObjs.get(index / 2);                if (info != null && !searchedTask.contains(info)) {                    searchedTask.add(info);                }            }        }        return searchedTask;    }    /**     * 找到匹配的索引数据     *     * @param keyWords     *            搜索的关键字     * @return 在初始化的索引数组的下标数组     */    private int[] getSearchIndex(String keyWords) {        Pattern pattern = Pattern.compile(keyWords, Pattern.CASE_INSENSITIVE | Pattern.LITERAL);        Matcher matcher = pattern.matcher(mKeyWordString.toString());        ArrayList<Integer> searchResult = new ArrayList<>();        while (matcher.find()) {            // 不宜在此处再做循环,否则可能造成循环次数过多错误            searchResult.add(matcher.start());        }        int[] searchIndexes = new int[searchResult.size()];        for (int i = 0; i < searchIndexes.length; i++) {            int findIndex = findIndex(searchResult.get(i));            searchIndexes[i] = (findIndex / 2) * 2;        }        return searchIndexes;    }    /**     * 使用二分法找到指定字符位置在索引数组中的位置     *     * @param charAt     *            字符在整个字符串中的位置     * @return 在索引数组中的位置     */    private int findIndex(int charAt) {        int low = 0;        int high = mIndexes.length - 1;        int mid = -1;        while (low <= high) {            mid = (low + high) >>> 1;            int midVal = mIndexes[mid];            if (midVal < charAt) {                low = mid + 1;            } else if (midVal > charAt) {                high = mid - 1;            } else {                return mid;            }        }        return mid;    }}

简单的使用和测试:

    public static void main(String[] args) {        List<Student> students = new ArrayList<>();        students.add(new Student("001", "小王", "天府大道一街"));        students.add(new Student("002", "小王", "天府大道二街"));        students.add(new Student("003", "小红", "天府大道三街"));        students.add(new Student("004", "小明", "天府大道四街"));        students.add(new Student("005", "小王", "天府大道五街"));        students.add(new Student("006", "小王", "软件园南门"));        students.add(new Student("007", "小张", "吉泰路"));        try {            FSearchTool tool = new FSearchTool(students, "name", "address");            System.out.println(tool.searchTasks("明"));        } catch (Exception e) {            e.printStackTrace();        }    }

输出结果:

[Student [id=004, name=小明, address=天府大道四街]]

博客地址:对List对象列表属性值的快速搜索

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 职场老实背锅怎么办 三星a8充不了电怎么办 果6软件连不上网怎么办 孩子在幼儿园磕伤了怎么办 小孩在幼儿园摔骨折了怎么办 孩子在幼儿园摔骨折了怎么办 大腿被撞了很痛怎么办 马面褶子坏了怎么办 纱料衣服有褶怎么办 裙子如果后背那里小了怎么办 湖州耳朵鸣很严重怎么办 预授权撤销错了怎么办 打酒店里小卡片电话被骗怎么办 本科错过了选导师该怎么办 本科导师威胁学生不让毕业怎么办 意向导师名额满了怎么办? 扔的瓶子被限制怎么办 左钝缘支起始部重度狭窄怎么办 吃了抑郁症药困怎么办 如果股票退市持股人怎么办 电信身份信息审核失败怎么办 打完肌肉针肿了怎么办 refa掉了一个球怎么办 左腿比右腿粗怎么办 产妇上火怎么办吃什么下火呢 哺乳期上火怎么办吃什么下火 前扣内衣有点紧怎么办 橱柜做了小10厘米怎么办 一个月宝宝体检胸围35怎么办 月经量少脸上长斑怎么办 下压100上压160怎么办 怎么办去台湾新的驻签 学生去韩国旅游签证怎么办 猪的眼睛赛肿了怎么办 纹眉没有修复霜怎么办 衣服搞到走珠露香水洗不掉怎么办 涂牙膏把脸烧伤怎么办 月经血排不出来怎么办 猫眼角膜掉了一块怎么办 腰酸痛直不起腰怎么办 白血病移植后复发了怎么办