【算法導論 in lambda】用lambda來重寫插入排序算法
插入排序原本的實現方式之一:
public int[] sort_ori(int[] ins) { for (int i = 1; i < ins.length; i++) { int currentIndex = i; int currentValue = ins[i]; int switchIndex = -1; for (int j = 0; j < currentIndex; j++) { if (ins[j] > currentValue) { switchIndex = j; break; } } System.out.println(switchIndex); if (switchIndex >= 0) { if (currentIndex - switchIndex >= 0) System.arraycopy(ins, switchIndex, ins, switchIndex + 1, currentIndex - switchIndex); ins[switchIndex] = currentValue; } } return ins; }
插入排序算法對應的比喻例子是打牌時抽牌的過程,如果目標的數據類型為鏈表的話,用插入排序會特別適合。
這邊的目標數據類型是數組,並且是原地算法,更貼切的比喻是按照顏色深淺整理蠟筆盒裏的蠟筆。
步驟的話,就是取出某根蠟筆,然後找到它對應的位置,然後將這個位置之後的蠟筆都滾動到下一列。
因此原來的算法中會有3個循環
loop1:從頭到尾選擇被排列的對象
loop2:在這個位置之前尋找對象應該放置的位置
loop3:將該位置之後原對象位置之前的所有蠟筆滾到右邊
嵌套其實是挺反人類的,它通常以一個for循環或者while循環作為標誌,但除非將其上下的代碼都看一遍,或者看註釋,不然很難第一眼就看出這個循環到底要做什麽。
通過lambda表達式對插入排序進行重寫:
public int[] sort_lambda(int[] ins) { IntStream.range(1, ins.length).forEach(currentIndex -> { int currentValue = ins[currentIndex]; IntStream.range(0, currentIndex).filter(j -> ins[j] > currentValue).findFirst().ifPresent(switchIndex -> { IntStream.iterate(currentIndex, a2 -> a2 - 1).limit(currentIndex - switchIndex).forEach(a2 -> { ins[a2] = ins[a2 - 1]; }); ins[switchIndex] = currentValue; }); }); return ins; }
這裏用3個IntStream的操作替代了之前3個loop
其中loop2的代碼變成了“IntStream.range(0, currentIndex).filter(j -> ins[j] > currentValue).findFirst().ifPresent(xxx)”
之前一直以為流操作都會從頭執行到尾,但今天測試過才發現並不是這樣,可以簡單測試一下。
@Test public void run() { int[] a = new int[]{5, 4, 3, 2, 1}; Arrays.stream(a).filter(a1->{ System.out.println("comparing "+a1); return a1>3; }).findFirst(); }
這個方法會去判斷數組a中的值,找到第一個大於3的值。原本以為它的執行方式會像它的方法鏈一樣,將[5,4,3,2,1]轉換為[5,4]之後再根據findFirst()給出5。
但控制臺的輸出為:
也就是只執行了第一個比較。。。
然後忽然想起來,之前常的limit。。也是處於方法鏈的後端(而不是作為參數放到stream的處理中),可能stream底層通過反射的機制,像sql一樣有個優化器的實現吧。(媽蛋以前一直以為流會從頭執行到尾,很多不需要遍歷到底的業務不敢用stream,白擔心了)
既然這樣,實現loop2就可以放心大膽的使用stream了。
loop2:在這個位置之前尋找對象應該放置的位置
IntStream.range(0,currentIndex) ---- 對於loop1中指定對象之前的位置(左閉右開)
.filter(a1-> ins[a1]>currentValue) ---- 判斷其是否大於loop1指定的值
.findFirst() ---- 找到第一個就行了
.ifPresent(loop3) ---- 如果存在的話,執行loop3
能像寫sql一樣寫java,同時性能也不會有損失,挺開心的
【算法導論 in lambda】用lambda來重寫插入排序算法