抛出问题
假如有两组数据,这六个人去参加非诚勿扰,需要做一个配对。数据如下:
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,效果如下:
用户编号 | 用户名称 | 角色 |
001 | 鲁班七号 | 射手 |
002 | 后裔 | 射手 |
001 | 张良 | 法师 |
其中,角色已经默认用字典转化了,很多框架内部应该是做了类似的事情。当然,主要是一些老的框架,毕竟现在还在用jsp标签的系统已经不多了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)