說明:這是在面試中面試官出的題。雖然是常見的優化問題,但這種經驗的確很有用。感慨之余,分享出來,以此共勉。
場景:現有List<PersonA>,List<PersonB>,PersonA 的屬性是 String類型的身份證號,int型age;PersonB 的屬性是 String類型的身份證號,int型sex;兩個集合中的身份證號有相同的;
需求:查找身份證號相同的人的性別。
常見的思路是:
1 @Data 2 public class PersonA { 3 private String card; 4 private int age; 5 6 public PersonA(String card, int age) { 7 this.card = card; 8 this.age = age; 9 } 10 } 11 ---------------------------------------------- 12 @Data 13 public class PersonB { 14 private String card; 15 private int sex; 16 17 public PersonB(String card, int sex) { 18 this.card = card; 19 this.sex = sex; 20 } 21 }
public class TestForFor { private List<PersonA> pa; private List<PersonB> pb; @Before public void before(){ pa = new ArrayList<>(); for (int i = 0; i < 10000; i++) { pa.add(new PersonA(UUID.randomUUID().toString(),20)); } pa.add(new PersonA("abcd111",10)); pa.add(new PersonA("abcd112",10)); pa.add(new PersonA("abcd113",10)); pa.add(new PersonA("abcd114",10)); pa.add(new PersonA("abcd115",10)); pa.add(new PersonA("abcd116",10)); pb = new ArrayList<>(); for (int i = 0; i < 10000; i++) { pb.add(new PersonB(UUID.randomUUID().toString(),Math.random() >= 0.5 ? 1 : 0)); } pb.add(new PersonB("abcd111",1)); pb.add(new PersonB("abcd112",1)); pb.add(new PersonB("abcd113",1)); pb.add(new PersonB("abcd114",1)); pb.add(new PersonB("abcd115",1)); pb.add(new PersonB("abcd116",1)); } @Test public void testFor(){ out.println("start search"); for (PersonA a : pa) { for (PersonB b : pb) { if (a.getCard().equals(b.getCard())){ out.println(b.getSex()==1?"男":"女"); } } } } }
結果。。。花費三秒多的時間。這還只是一萬條數據
現在換一種思路,直接貼代碼
1 private List<PersonA> pa; 2 private List<PersonB> pb; 3 private Map<String,Object> map; 4 @Before 5 public void before(){ 6 out.println("start before"); 7 pa = new ArrayList<>(); 8 for (int i = 0; i < 10000; i++) { 9 pa.add(new PersonA(UUID.randomUUID().toString(),20)); 10 } 11 pa.add(new PersonA("abcd111",10)); 12 pa.add(new PersonA("abcd112",10)); 13 pa.add(new PersonA("abcd113",10)); 14 pa.add(new PersonA("abcd114",10)); 15 pa.add(new PersonA("abcd115",10)); 16 pa.add(new PersonA("abcd116",10)); 17 18 19 pb = new ArrayList<>(); 20 for (int i = 0; i < 10000; i++) { 21 pb.add(new PersonB(UUID.randomUUID().toString(),Math.random() >= 0.5 ? 1 : 0)); 22 } 23 pb.add(new PersonB("abcd111",1)); 24 pb.add(new PersonB("abcd112",1)); 25 pb.add(new PersonB("abcd113",1)); 26 pb.add(new PersonB("abcd114",1)); 27 pb.add(new PersonB("abcd115",1)); 28 pb.add(new PersonB("abcd116",1)); 29 map = new HashMap<>(); 30 for ( PersonB pbb : pb ) { 31 map.put(pbb.getCard(),pbb.getSex()); 32 } 33 } 34 @Test 35 public void testFor(){ 36 out.println("start search"); 37 for (PersonA a : pa) { 38 if (map.containsKey(a.getCard())){ 39 out.print(a.getAge()+" "); 40 out.println((int)map.get(a.getCard())==1?"男":"女"); 41 } 42 //out.println(map.get(a.getCard())==null?"空":map.get(a.getCard())); 43 //out.println((int)map.get(a.getCard())==1?"男":"女"); 44 } 45 }
可以看出,查找的效率明顯提升。
這里面的重點,第29行我用map重新填寫了pb的數據[我的本地的sql壞了,所以用偽數據庫的方式模仿,感興趣也可以從數據庫里試試],
為什么用map填完了后速度會這么快?
原因很簡單。因為ArrayList的底層是數組實現的,若要查找必定是從索引0開始一個個的進行比對;而HashMap則不同,
HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的,如果定位到的數組位置不含鏈表(當前entry的next指向null),那么對於查找,添加等操作很快,僅需一次尋址即可;如果定位到的數組包含鏈表,對於添加操作,其時間復雜度依然為O(1),因為最新的Entry會插入鏈表頭部,僅需要簡單改變引用鏈即可,而對於查找操作來講,此時就需要遍歷鏈表,然后通過key對象的equals方法逐一比對查找。所以,性能考慮,HashMap中的鏈表出現越少,性能才會越好。
關於以上加粗內容取自博客
我在面試時只想到了hash,面試官提醒我用hashmap,恍然大悟。
時隔數月,回來歸納下這個問題。2018/9/13
其實這個問題可以抽象為:兩個數組求交集,這里簡要說下思路。
使用 treeset裝載第一個數組,遍歷第二個數組,if(!contains數組二中的值),add到一個新list中,最后這個list存的就是交集
原創分享,轉載標注。