最近做項目,遇到了很多雙層for循環組裝數據的情況,有的甚至是三層循環,數據組裝比較麻煩,同事看到我有一個三層的for循環,說你這個可以優化成兩層,三層嵌套的太深了,數據量大的情況下,性能會比較差。是的,大家都知道for循環嵌套的越深,數據量大的情況下,循環遍歷的次數也是成指數級增長的,性能可想而知(雖然當時我做的這個項目的數據量只有幾百,沒有太大,這也是當時編碼的時候直接用了三層for循環的原因,後來還是聽從了同事的意見,優化了一波)。雖然當時我知道數據量不是很大,但是作爲一個技術人,有可以改進的地方,堅決不能放過任何可以優化的點,這是作爲技術人的底線,也是對高質量代碼的追求,盡力保證自己寫出來的代碼都是高效,安全,快速的。後來從這個優化中我體會到了List轉Map思想的妙用,性能成幾何倍數增加,後來自己又拓展到兩層for循環,做了一些測試,發現確實妙不可言,雖然只是一個小小的優化,我覺得有必要記錄下來,分享一下自己的心得,方便大家共同學習。廢話說了這麼多,現在我帶大家一步一步的演示,先看一下雙層for循環下的性能測試數據:
public static Map<String, AllUserInfoModel> getTeamBdmMap(List<AllUserInfoModel> allUserInfoModelList, List<Integer> allBdm) {
Map<String, AllUserInfoModel> teamBdmMap = new HashMap<>(128);
allBdm.forEach(item -> {
allUserInfoModelList.forEach(user -> {
if (item.equals(user.getSysUserId())) {
if (StringUtils.isNotBlank(user.getDepartmentName())) {
teamBdmMap.put(user.getDepartmentName(), user);
}
}
});
});
return teamBdmMap;
}
public static void main(String[] args) {
List<AllUserInfoModel> allUserInfoModelList = new ArrayList<>();
List<Integer> allBdm = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
AllUserInfoModel userInfoModel = new AllUserInfoModel();
userInfoModel.setSysUserId(i);
userInfoModel.setDepartmentName("測試戰隊-" + i);
allUserInfoModelList.add(userInfoModel);
if (i % 100 == 0) {
allBdm.add(i);
}
}
long ls = System.currentTimeMillis();
getTeamBdmMap(allUserInfoModelList, allBdm);
long le = System.currentTimeMillis();
System.out.println(le - ls);
}
測試十萬次的結果爲:
以上是我們按照一般的正常List循環兩層遍歷得到的結果,2543毫秒即2.5s。
下面讓我們看一下轉map後的測試結果:
public static Map<String, AllUserInfoModel> getTeamBdmMapTest(List<AllUserInfoModel> allUserInfoModelList, List<Integer> allBdm) {
Map<String, AllUserInfoModel> teamBdmMap = new HashMap<>(128);
Map<Integer, AllUserInfoModel> userMap = allUserInfoModelList.stream().collect(Collectors.toMap(AllUserInfoModel::getSysUserId, user -> user));
allBdm.forEach(item -> {
AllUserInfoModel user = userMap.get(item);
if (Objects.nonNull(user) && StringUtils.isNotBlank(user.getDepartmentName())) {
teamBdmMap.put(user.getDepartmentName(), user);
}
});
return teamBdmMap;
}
public static void main(String[] args) {
List<AllUserInfoModel> allUserInfoModelList = new ArrayList<>();
List<Integer> allBdm = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
AllUserInfoModel userInfoModel = new AllUserInfoModel();
userInfoModel.setSysUserId(i);
userInfoModel.setDepartmentName("測試戰隊-" + i);
allUserInfoModelList.add(userInfoModel);
if (i % 100 == 0) {
allBdm.add(i);
}
}
long ls2 = System.currentTimeMillis();
getTeamBdmMapTest(allUserInfoModelList, allBdm);
long le2 = System.currentTimeMillis();
System.out.println(le2 - ls2);
}
轉Map後測試十萬次的結果如下:
把allUserInfoModelList轉爲Map<Integer, AllUserInfoModel> userMap後時間爲137毫秒即爲0.1秒,兩次結果做個對比可以發現後者比前者提高了約250倍的性能,如果更大的數據量,性能更是指數級提升。正是利用了Map的快速查找(算法複雜度O(1)),減少了循環遍歷的次數(時間複雜度由O(n)轉變爲了O(1)),性能才得以百倍提升(不過增加了空間複雜度,此處是空間換時間,需要在內存不緊張的條件下進行,需要結合實際情況選取是否需要轉換)。我覺得這是一種思路的轉變,只是一個小小的轉換,性能卻是天差地別,當然,這個要看具體的業務場景,也不能一味的爲了用map而用map,根據具體的業務場景需求,選擇合適的方法纔是最好的。