在個人上一篇文章中,講到了我本身初步認識 HashMap 的一個經驗分享:HashMap 淺析 —— LeetCode Two Sum 刷題總結。做爲一個 CRUD 工程師,平時不多接觸到基礎組件的涉及,那麼是否是很難有機會用到 HashMap 呢?java
今天,就舉一個常見的查詢例子,來看看咱們如何使用 HashMap 來提升代碼的效率。算法
已知一個 Student 類:編程
public class Student { private Long id; private String name; public Student(Long id, String name) { this.id = id; this.name = name; } // ---Getters And Setters--- }
和一個 Score 類:segmentfault
public class Score { private Long studentId; private String mathScore; private String englishScore; public Score(Long studentId, String mathScore, String englishScore) { this.studentId = studentId; this.mathScore = mathScore; this.englishScore = englishScore; } // ---Getters And Setters--- }
咱們須要把 Student 和 Score 合併到一塊兒,即類 Report:微服務
public class Report { private Long studentId; private String studentName; private String mathScore; private String englishScore; public Report(Long studentId, String studentName, String mathScore, String englishScore) { this.studentId = studentId; this.studentName = studentName; this.mathScore = mathScore; this.englishScore = englishScore; } }
看類的屬性咱們就明白了,這裏其實至關於在 Student 和 Score 之間作一個 Join,獲得 Report。這是咱們在編程中常見的場景(例如查詢了訂單中心,用戶中心,支付中心,合併各個中心返回的結果造成一個表單,由於各個中心是獨立的微服務,沒法使用 SQL JOIN)。測試
現有兩個 List:List<Student>
和 List<Score>
:this
List<Student> students = Arrays.asList( new Student(1L, "Angle"), new Student(2L, "Baby") ); List<Score> scores = Arrays.asList( new Score(1L, "90", "87"), new Score(2L, "92", "78") );
在學會使用 HashMap 以前,我可能會作一次雙重循環:spa
List<Report> reports = new ArrayList<>(); for (Student student : students) { for (Score score : scores) { if (!student.getId().equals(score.getStudentId())) { continue; } reports.add( new Report(student.getId(), student.getName(), score.getMathScore(), score.getEnglishScore()) ); break; } }
時間複雜度最差的狀況下是O(n * m)
。code
可是使用 HashMap 來改善程序,就能獲得不錯的效果:blog
Map<Long, Student> map = new HashMap<>(); for (Student student : students) { map.put(student.getId(), student); } List<Report> reports = new ArrayList<>(); for (Score score : scores) { Student student = map.get(score.getStudentId()); if(student == null){ // 避免 NPE continue; } reports.add( new Report(student.getId(), student.getName(), score.getMathScore(), score.getEnglishScore()) ); }
雙重循環,變成了兩次循環,時間複雜度是O(n + m)
。
顯然要比前面的方法效果要好一些。筆者寫了測試代碼分別測試兩個方法的效率,在 10w 數據下,執行時間以下:
差距好像挺大。想了解爲何 HashMap 可以獲得如此好的效果,能夠看個人這篇文章:HashMap 淺析 —— LeetCode Two Sum 刷題總結。若是讀者有更好的解法歡迎留言交流,筆者水平有限,在算法上研究很少。
10w 數據的測試源碼見下方,各位讀者能夠自行試驗下效果:
package com.xiangyu.demo.hashmap; import com.xiangyu.java.hashmap.Report; import com.xiangyu.java.hashmap.Score; import com.xiangyu.java.hashmap.Student; import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class HashMapTest { private List<Student> students = new ArrayList<>(); private List<Score> scores = new ArrayList<>(); @Before public void before() { // 每一個list 裏放 10w 數據 for (long i = 0; i < 100000; i++) { students.add(new Student(i, "test")); scores.add(new Score(i, "95", "95")); } } @Test public void TestHashMap() { Map<Long, Student> map = new HashMap<>(); for (Student student : students) { map.put(student.getId(), student); } List<Report> reports = new ArrayList<>(); for (Score score : scores) { Student student = map.get(score.getStudentId()); if (student == null) { continue; } reports.add( new Report(student.getId(), student.getName(), score.getMathScore(), score.getEnglishScore()) ); } System.out.println(reports.size()); } @Test public void testFor2() { List<Report> reports = new ArrayList<>(); for (Student student : students) { for (Score score : scores) { if (!student.getId().equals(score.getStudentId())) { continue; } reports.add( new Report(student.getId(), student.getName(), score.getMathScore(), score.getEnglishScore()) ); break; } } System.out.println(reports.size()); } }