从非诚勿扰看数据索引,优化代码小妙招

简介: 从非诚勿扰看数据索引,优化代码小妙招

抛出问题


假如有两组数据,这六个人去参加非诚勿扰,需要做一个配对。数据如下:

List<User> usersA = new ArrayList<>();
usersA.add(new User("1", "吕布", "10"));
usersA.add(new User("2", "刘备", "11"));
usersA.add(new User("3", "孙悟空", "12"));
List<User> usersB = new ArrayList<>();
usersB.add(new User("10", "貂蝉", "1"));
usersB.add(new User("11", "孙尚香", "2"));
usersB.add(new User("12", "紫霞", "3"));


比如 usersA.add(new User("1", "吕布", "10"));

1是吕布自己的ID,10是配对方的ID。假设现在有个需求,需要返回一个结果集,返回“吕布-貂蝉”这样的数据。首先想到的肯定就是双层for,这简直就是理所当然的事情。

int loopCount = 0;
for(User userA : usersA) {
  for(User userB : usersB) {
        loopCount++;          
    if(userB.getUid().equals(userA.getCoupleId())) {
      System.out.println( String.format("%s-%s",userA.getUname(),userB.getUname()));
    }
  }
}


结果:

吕布-貂蝉
刘备-孙尚香
孙悟空-紫霞
一共循环9次

逻辑清晰,简单易懂,完全没有问题。


优化1


一个最简单的优化思路就是,比如吕布已经找到了貂蝉,那么应该立刻跳出循环才对。因为吕布已经找到貂蝉了,就没必要再去找孙尚香。int loopCount = 0;

int loopCount = 0;
for(User userA : usersA) {
  for(User userB : usersB) {
        loopCount++;          
    if(userB.getUid().equals(userA.getCoupleId())) {
      System.out.println( String.format("%s-%s",userA.getUname(),userB.getUname()));
      break;
    }
  }
}


加一个break就完事儿了。

吕布-貂蝉
刘备-孙尚香
孙悟空-紫霞
一共循环6次

从9次减少到6次,非常大的提升。


优化2


纳尼,还能优化?肯定的,比如吕布匹配到貂蝉后,下一个循环,刘备还是会匹配到貂蝉,这就不合理,所以不妨把匹配到的对象直接从List删去,就不会出现重复匹配的情况了。

非诚勿扰的规则也是,牵手成功后就直接下场了,不会继续留在舞台上。所以,加一个remove操作就很有必要。

for(User userA : usersA) {
    for(User userB : usersB) {
        loopCount++;
        if(userB.getUid().equals(userA.getCoupleId())) {
            System.out.println( String.format("%s-%s",userA.getUname(),userB.getUname()));
            usersB.remove(userB);
            break;
        }
    }
}


我靠,直接到3次了,这肯定是最优解了吧?然而,并不是。因为List里面user的顺序是正好的,吕布对貂蝉,刘备对孙尚香,猴子对露娜。

可实际情况,哪有这么巧合的事情??

比如我随便打乱一下顺序,就不同了。

List<User> usersB = new ArrayList<>();
usersB.add(new User("12", "紫霞", "3"));
usersB.add(new User("10", "貂蝉", "1"));
usersB.add(new User("11", "孙尚香", "2"));


这样的结果就是循环5次,那如果数据更多呢?比如第一个数据要匹配到最后一个才对得上,那效率也就太低了。


优化3


虽然用for循环给人感觉是天经地义,但是不妨换个思路,男嘉宾为什么非要一个个去找呢,比如吕布,一眼扫过去看到貂蝉,直接牵走不就完事了吗?

所以,我们可以给女嘉宾建立索引,最简单的办法就是用Map。

int loopCount = 0;
Map<String, String> usersMap = new LinkedHashMap<String, String> (); 
for (User userB : usersB) {
    loopCount++;
    usersMap.put(userB.getUid(), userB.getUname());
}
for (User userA : usersA) {
    loopCount++;
    System.out.println(String.format("%s-%s", userA.getUname(), usersMap.get(userA.getCoupleId())));
}


结果:


吕布-貂蝉

刘备-孙尚香

孙悟空-紫霞

一共循环6次


假如两个列表的长度分别是m和n,那么不管其中的顺序如何,循环的深度永远是固定的m+n。虽然还是比不上上面的最佳情况n次,但是当m和n特别大的时候,效率就立刻突显出来了。


当然了,因为用了Map,牺牲了一点点内存,算是空间换时间吧。


实际应用场景


项目中的实际应用有很多,比如数据字典,很多项目会在SpringContext启动的时候,就把字典表加载到内存。当出现表字段需要做转换的时候,直接查表,效率非常可观。(和left join比起来)

比如,我有一个xml:

<grid id="grid0" class="com.service.UserService" method="getList" parameters="" resulttype="com.answer.entity.User"> 
   <column id="uid">
      用户编号
   </column> 
   <column id="uname">
      用户名称
   </column> 
   <column id="role"  datadic="role">
      角色
   </column> 
  </grid> 


这是自定义的标签,可以通过反射,xml解析的方式做一个小型的解析框架。用到了jsoup,做一个测试类:

public class JspTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
        Map<String, Map<String, Object>> datadics = new HashedMap();
        Map<String, Object> roleDict = new HashedMap();
        roleDict.put("1", "射手");
        roleDict.put("2", "法师");
        datadics.put("role", roleDict);
        File file = new File("D:\\eclipse-mars\\workspace2\\answer\\WebContent\\table.xml");
        Document doc = Jsoup.parse(file, "UTF-8");
        Elements grids = doc.getElementsByTag("grid");
        List<String> colums = new ArrayList<String>();
        List<List<Object>> rows = new ArrayList<List<Object>>();
        for (Iterator<Element> iterator = grids.iterator(); iterator.hasNext(); ) {
            Element grid = (Element) iterator.next();
            String className = grid.attr("class");
            String method = grid.attr("method");
            String resultType = grid.attr("resultType");
            Class<?> clazz = Class.forName(className);
            Class resultClass = Class.forName(resultType);
            Method method2 = clazz.getMethod(method);
            List result = (List) method2.invoke(clazz.newInstance());
            Elements columns = grid.getElementsByTag("column");
            int index = 0;
            for (int i = 0; i < result.size(); i++) {
                Element e = (Element) columns.get(i);
                String colName = e.text();
                colums.add(colName);
                List row = new ArrayList<>();
                rows.add(row);
                Object object = result.get(index++);
                for (int j = 0; j < columns.size(); j++) {
                    String id = columns.get(j).attr("id");
                    String DATADIC = columns.get(j).attr("DATADIC");
                    Map<String, Object> map = datadics.get(DATADIC);
                    Object fieldValue = null;
                    fieldValue = ReflectUtil.getFieldValue(object, id);
                    if (map != null) {
                        fieldValue = map.get(String.valueOf(fieldValue));
                    }
                    rows.get(i).add(fieldValue);
                }
            }
        }
        StringBuffer sb = new StringBuffer("\n");
        for (Iterator iterator = colums.iterator(); iterator.hasNext(); ) {
            String column = (String) iterator.next();
            sb.append("");
        }
        sb.append("\n");
        for (Iterator iterator = rows.iterator(); iterator.hasNext(); ) {
            List<Object> row = (List<Object>) iterator.next();
            sb.append("");
            for (Iterator iterator2 = row.iterator(); iterator2.hasNext(); ) {
                Object string = iterator2.next();
                sb.append("");
            }
            sb.append("");
            sb.append("\n");
        }
        sb.append("");
        sb.append(column);
        sb.append("");
        sb.append(string);
        sb.append("");
        grids.get(0).replaceWith(Jsoup.parse(sb.toString()).body());
        System.out.println(doc);
        FileUtil.writeString(doc.toString(), new File("D:\\eclipse - mars\\workspace2\\answer\\WebContent\\table.html "), " GBK ");
    }
}


最终生成一个html,效果如下:


微信截图_20230505014124.png


其中,角色已经默认用字典转化了,很多框架内部应该是做了类似的事情。当然,主要是一些老的框架,毕竟现在还在用jsp标签的系统已经不多了。


相关文章
|
存储 编译器
IAT表入门简析【滴水逆向三期52笔记】
IAT表入门简析【滴水逆向三期52笔记】
新增节添加代码【滴水逆向三期46笔记】
新增节添加代码【滴水逆向三期46笔记】
|
小程序 数据安全/隐私保护 计算机视觉
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
177 0
切勿外传,我要把我的写作“小心思”放出来了!| 年终总结之学习篇🚩
|
存储 SQL 消息中间件
你管这破玩意儿叫索引?
你管这破玩意儿叫索引?
|
Java C语言
计算机教育中缺失的一课,劝学弟学妹们一句,一定要趁早补上,工作后会事半功倍!
各位学弟学妹们好,作为稍微年长的我(岁月是把杀猪刀啊),今天就给大家补补课。 在大学里的,我们上的计算机专业课程一般都是像操作系统、编译原理、计算机组成原理、计算机网络这些理论课程,还有一些像C语言、Java、.Net这些可以实践的课程,甚至还有可能让你焊一个收音机,但是对于一些基本习惯却很容易被忽略,需要学弟学妹们自行摸索。
216 0
计算机教育中缺失的一课,劝学弟学妹们一句,一定要趁早补上,工作后会事半功倍!
|
SQL 存储 缓存
面试的时候,如果你没掌握索引,绝对没戏!
之前朋友在面试的时候被问到了许多关于索引的问题,而索引这个词一直也是我们在开发中最最最常见的,也是很多在进行性能优化的时候会去做的一件事情,所以今天我们就来说说面试中关于索引的那点事。
面试的时候,如果你没掌握索引,绝对没戏!
下一篇
无影云桌面