预备资料
实体类
import lombok.Data;import java.util.ArrayList;import java.util.List;@Datapublic class Area {private Long id;private String name;private Long parentId;private List<Area> children = new ArrayList<>();}
区域数据
3660条
area.json
783562条
data.json
public List<Area> getAreaList() {String areaStr = FileUtil.readString("area.json", StandardCharsets.UTF_8);// String areaStr = FileUtil.readString("data.json", StandardCharsets.UTF_8);return JSONUtil.toList(areaStr, Area.class);}
递归方式
代码
public long recursionBuildAreaData(List<Area> areaList) {System.out.println("开始时间:" + new Date());TimeInterval timer = new TimeInterval();timer.start();// 找出所有一级区域 调用递归方法查找子区域List<Area> areas = areaList.stream().filter(area -> area.getParentId() == 0).peek(area -> area.setChildren(getChildrenArea(area, areaList))).collect(Collectors.toList());long result = timer.intervalMs();System.out.println("递归方式,耗时:" + result + "ms");System.out.println("结束时间:" + new Date());return result;}private List<Area> getChildrenArea(Area area, List<Area> areaList) {// 找出parentId为当前区域id的所有子区域 对子区域再进行递归return areaList.stream().filter(item -> item.getParentId().equals(area.getId())).peek(item -> item.setChildren(getChildrenArea(item, areaList))).collect(Collectors.toList());}
耗时
3660条 200毫秒左右
783562条 2小时9分钟
先转map再循环方式
代码
public long toMapThanFor(List<Area> areaList) {TimeInterval timer = new TimeInterval();timer.start();// 先转成Map key为id value为AreaMap<Long, Area> areaMap = areaList.stream().collect(Collectors.toMap(Area::getId, area -> area));List<Area> areas = new ArrayList<>();for (Area area : areaList) {// 不是根节点 则添加到其parentId的区域的children中if (area.getParentId() != 0) {areaMap.get(area.getParentId()).getChildren().add(area);} else {areas.add(area);}}long result = timer.intervalMs();System.out.println("先转为Map,再遍历的方式,耗时:" + result + "ms");return result;}@Testpublic void testToMapThanFor() {List<Area> areaList = getAreaList();toMapThanFor(areaList);}
耗时
边循环边填充map方式
代码
public long optimizationToMapThanFor(List<Area> areaList) {TimeInterval timer = new TimeInterval();timer.start();Map<Long, Area> areaMap = new HashMap<>();List<Area> areas = new ArrayList<>();for (Area area : areaList) {areaMap.putIfAbsent(area.getId(), area);// 不是根节点 则添加到其parentId的区域的children中if (area.getParentId() != 0) {areaMap.putIfAbsent(area.getParentId(), area);areaMap.get(area.getParentId()).getChildren().add(area);} else {areas.add(area);}}long result = timer.intervalMs();System.out.println("边循环,边填充map的方式,耗时:" + result + "ms");return result;}@Testpublic void testToMapThanFor2() {List<Area> areaList = getAreaList();optimizationToMapThanFor(areaList);}
耗时


速度比对
代码
@Testpublic void testMain() {TimeInterval timer = new TimeInterval();timer.start();List<Area> areaList = getAreaList();int l1Count = 0;int l2Count = 0;int l3Count = 0;for (int i = 0; i < 100; i++) {long l1 = toMapThanFor(areaList);long l2 = optimizationToMapThanFor(areaList);if (l1 > l2) {l2Count++;} else if (l1 < l2) {l1Count++;} else {l3Count++;}System.out.println();}System.out.println("区域数据:" + areaList.size());System.out.println("次数多,即耗时少");System.out.println("(普通)先转为Map,再遍历的方式:" + l1Count + "次");System.out.println("(优化)边循环,边填充map的方式:" + l2Count + "次");System.out.println("相同速度:" + l3Count + "次");}
耗时






总结
数据量少时
速度上体现不出差距,基本都是0ms,先转map的代码比较简洁些
数据量多时
边循环边填充map的方式会稍微快一些
总述
平时使用”先转map再循环方式”即可,代码量简洁清晰点,有庞大数据时才使用”边循环边填充map方式”
完整代码
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONUtil;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MyTest {
// 获取区域列表
public List<Area> getAreaList() {
TimeInterval timer = new TimeInterval();
timer.start();
String areaStr = FileUtil.readString("area.json", StandardCharsets.UTF_8);
// String areaStr = FileUtil.readString("data.json", StandardCharsets.UTF_8);
List<Area> areaList = JSONUtil.toList(areaStr, Area.class);
System.out.println("读取区域数据:" + areaList.size() + "条" + ",耗时:" + timer.intervalMs() + "ms\n");
return areaList;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 递归组装数据
public long recursionBuildAreaData(List<Area> areaList) {
TimeInterval timer = new TimeInterval();
timer.start();
// 找出所有一级区域 调用递归方法查找子区域
List<Area> areas = areaList.stream().filter(area -> area.getParentId() == 0)
.peek(area -> area.setChildren(getChildrenArea(area, areaList)))
.collect(Collectors.toList());
long result = timer.intervalMs();
System.out.println("递归方式,耗时:" + result + "ms");
return result;
}
private List<Area> getChildrenArea(Area area, List<Area> areaList) {
// 找出parentId为当前区域id的所有子区域 对子区域再进行递归
return areaList.stream().filter(item -> item.getParentId().equals(area.getId()))
.peek(item -> item.setChildren(getChildrenArea(item, areaList))
).collect(Collectors.toList());
}
@Test
public void testRecursionBuildAreaData() {
List<Area> areaList = getAreaList();
recursionBuildAreaData(areaList);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public long toMapThanFor(List<Area> areaList) {
TimeInterval timer = new TimeInterval();
timer.start();
// 先转成Map key为id value为Area
Map<Long, Area> areaMap = areaList.stream().collect(Collectors.toMap(Area::getId, area -> area));
List<Area> areas = new ArrayList<>();
for (Area area : areaList) {
// 不是根节点 则添加到其parentId的区域的children中
if (area.getParentId() != 0) {
areaMap.get(area.getParentId()).getChildren().add(area);
} else {
areas.add(area);
}
}
long result = timer.intervalMs();
System.out.println("先转为Map,再遍历的方式,耗时:" + result + "ms");
return result;
}
@Test
public void testToMapThanFor() {
List<Area> areaList = getAreaList();
toMapThanFor(areaList);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public long optimizationToMapThanFor(List<Area> areaList) {
TimeInterval timer = new TimeInterval();
timer.start();
Map<Long, Area> areaMap = new HashMap<>();
List<Area> areas = new ArrayList<>();
for (Area area : areaList) {
areaMap.putIfAbsent(area.getId(), area);
// 不是根节点 则添加到其parentId的区域的children中
if (area.getParentId() != 0) {
areaMap.putIfAbsent(area.getParentId(), area);
areaMap.get(area.getParentId()).getChildren().add(area);
} else {
areas.add(area);
}
}
long result = timer.intervalMs();
System.out.println("边循环,边填充map的方式,耗时:" + result + "ms");
return result;
}
@Test
public void testToMapThanFor2() {
List<Area> areaList = getAreaList();
optimizationToMapThanFor(areaList);
}
@Test
public void testMain() {
TimeInterval timer = new TimeInterval();
timer.start();
List<Area> areaList = getAreaList();
int l1Count = 0;
int l2Count = 0;
int l3Count = 0;
for (int i = 0; i < 100; i++) {
long l1 = toMapThanFor(areaList);
long l2 = optimizationToMapThanFor(areaList);
if (l1 > l2) {
l2Count++;
} else if (l1 < l2) {
l1Count++;
} else {
l3Count++;
}
System.out.println();
}
System.out.println("区域数据:" + areaList.size());
System.out.println("次数多,即耗时少");
System.out.println("(普通)先转为Map,再遍历的方式:" + l1Count + "次");
System.out.println("(优化)边循环,边填充map的方式:" + l2Count + "次");
System.out.println("相同速度:" + l3Count + "次");
}
}

