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

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

抛出问题


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

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标签的系统已经不多了。


相关文章
|
Java API Spring
Java SpringBoot 公众号集成模板推送消息
Java SpringBoot 公众号集成模板推送消息
|
存储 人工智能 架构师
ChatGPT 与软件架构 (4) - 架构师提示工程指南
ChatGPT 与软件架构 (4) - 架构师提示工程指南
453 0
|
监控 供应链 数据可视化
运营数据分析在企业管理中的重要性
本文详述了构建高效营销策略体系的方法,涵盖市场调研、产品定位、差异化策略、品牌建设及数据分析等环节,强调了数据可视化工具在策略执行中的重要作用,旨在帮助企业提升市场竞争力和盈利能力。
|
存储 分布式计算 Cloud Native
湖仓一体概念快问快答
湖仓一体概念快问快答
984 0
湖仓一体概念快问快答
|
存储 Java 数据挖掘
构建基于Spring Boot的数据分析平台
构建基于Spring Boot的数据分析平台
|
Java 测试技术 API
SpringBoot单元测试快速写法问题之复杂的业务逻辑设计有效的单元测试如何解决
SpringBoot单元测试快速写法问题之复杂的业务逻辑设计有效的单元测试如何解决
|
存储 监控 固态存储
【性能突破】揭秘!如何让您的数据库在高并发风暴中稳如磐石——一场关于WAL写入性能优化的实战之旅,不容错过的技术盛宴!
【8月更文挑战第21天】在高并发环境下,数据库面临极大挑战,特别是采用Write-Ahead Logging (WAL)的日志机制。本文通过一个在线交易系统的案例,分析了WAL写入性能瓶颈,并提出优化方案:理解WAL流程;分析磁盘I/O瓶颈、缓冲区设置与同步策略;通过增大WAL缓冲区、使用SSD及调整同步策略来优化;最后通过测试验证改进效果,总结出一套综合优化方法。
332 0
|
XML 开发框架 前端开发
在WPF应用中,结合阿里矢量图标库使用Geometry图标
在WPF应用中,结合阿里矢量图标库使用Geometry图标
|
存储 设计模式 安全
【Swift开发专栏】Swift的扩展与分类
【4月更文挑战第30天】Swift的扩展和枚举分类提供类型添加新功能的灵活性。文章分为三部分:扩展用于为已有类型添加属性、方法等,适用于功能扩展、代码组织;枚举分类定义相关值,支持原始值和关联值,用于状态表示和类型安全选项。扩展和分类在实际开发中应用于类型增强、状态管理及组合使用。了解并掌握这些特性,能提升代码的灵活性和可维护性。更多高级特性和应用值得进一步探索。
272 2
|
设计模式 Java 开发者
springboot-策略和模板模式的思考与实践
前言 在日常的开发过程中,经常会遇到一些复杂的业务场景,那么如何优雅的实现复杂的业务功能,而且使得系统的性能、可靠性、可读性达到最好呢?这里不仅考验着开发者的编程功底,实践能力,还有对局部和全局的把握能力。我们都知道,java 是面向对象编程的语言而非面向过程编程的语言,但是在实际操作中,常常会为了走捷径实现功能,忽略了代码的结构性建设,久而久之项目就是堆砌成屎山,系统性能降低,可读性差,系统的可维护性降低,扩展难度极大。为了解决这样的问题,就需要开发者修炼自己的设计功底,学好并用好设计模式。在本文要谈的就是对策略和模板设计模式使用心得与体会。