前語: 不要為了讀文章而讀文章,一定要帶著問題來讀文章,勤思考。
這是一位同學面試的過程中遇到的面試題,我覺得挺有意思,去研究了一下,發現竟然有這麼多方法,在此,與大家分享一下。
關於List這種數據結構,在這裡我就不多說了,很重要,不知道的同學可以翻閱以前的文章。
對於這類問題,你首先要給面試官一個定心丸: List是java.util.Collection 接口的一個子接口,並且是一個有序列表 。 這一步很關鍵,不能錯,一定要記清楚,如果這一步錯,那麼後面說再多,也等於零,請記住下面這張圖。
我們再來抓這個問題的關鍵點: 如何用程序證明List是有序的?
方法即是迭代List並比較相鄰元素。 如果兩個相鄰元素中的任何一個未排序,我們可以說List未排序。
下面我就來列舉幾種方法。
1、迭代器
其實Java的遍歷有三種方式: for、foreach、Iterator; 關於這三種的區別,你也可以在面試中延伸一下(比如說 為什麼在Iterator能夠修改集合內部的元素,而foreach不行? 會拋什麼異常? )。
既然提到遍歷,那麼這裡有有兩個東東需要提一下Comparator與Comparable,它們的區別是什麼?
Comparable : 如果一個類實現了該接口,那麼該類默認支持排序,相當於“ 內部比較器 ”。
Comparator : 如果一個類型實現該接口,那麼該類自身不支持排序,它只是充當一個“ 外部比較器 ”的角色。
以String為例,它自身實現了 Comparable接口,那麼我可以用下面這種方式來驗證:
public static boolean isSorted(List<string> listOfStrings) {/<string>
if (isEmpty(listOfStrings) || listOfStrings.size() == 1) {
return true;
}
Iterator<string> iter = listOfStrings.iterator();/<string>
String current, previous = iter.next();
while (iter.hasNext()) {
current = iter.next();
if (previous.compareTo(current) > 0) {
return false;
}
previous = current;
}
return true;
}
我們自己建的類一般不會自動實現 Comparable接口,那麼我們可以嘗試用 Comparator來進行比較,如:
public static boolean isSorted(List<employee> employees, Comparator<employee> employeeComparator) {/<employee>/<employee>
if (isEmpty(employees) || employees.size() == 1) {
return true;
}
Iterator<employee> iter = employees.iterator();/<employee>
Employee current, previous = iter.next();
while (iter.hasNext()) {
current = iter.next();
if (employeeComparator.compare(previous, current) > 0) {
return false;
}
previous = current;
}
return true;
}
通過上面的例子,我們可以發現不同之處: 在於前後兩個元素的比較方式上。 總得來說,如果你建的類默認有排序功能,那麼建議用Comparable; 但是,我們可以使用Comparator進行精確控制排序。
到這裡,看出來沒,一個迭代器咱們就可以吹這麼多牛皮出來^_^
2、遞歸
遞歸這種方式只是一種思路,我覺得用的比較巧妙,啥都不說了,直接上代碼。
public static boolean isSorted(List<string> listOfStrings) {/<string>
return isSorted(listOfStrings, listOfStrings.size());
}
public static boolean isSorted(List<string> listOfStrings, int index) {/<string>
if (index < 2) {
return true;
} else if (listOfStrings.get(index - 2).compareTo(listOfStrings.get(index - 1)) > 0) {
return false;
} else {
return isSorted(listOfStrings, index - 1);
}
}
說實話,這種證明操作我剛開始是沒有想到的,很巧妙的玩法。
3、第三方組件
這裡就不得不提谷歌開源的 Guava ,這個組件基本上包含了我們常用的所有工具類,也包括檢驗排序。
比如,利用Guava的Ordering類檢查List是否有序,同樣它也提供了兩種方式,一種是針對於實現了Comparable接口的類型,比如:
public static boolean isSorted(List<string> listOfStrings) {/<string>
return Ordering.<string> natural().isOrdered(listOfStrings);/<string>
}
另一種是使用 Comparator進行排序的類,比如:
public static boolean isSorted(List<employee> employees, Comparator<employee> employeeComparator) {/<employee>/<employee>
return Ordering.from(employeeComparator).isOrdered(employees);
}
它內部還有很多方法,比如natural().reverseOrder():來檢查列表是否按相反的順序排序; 再比如我們可以使用natural().nullFirst()和natural().nullLast()來檢查null是否出現在排序列表的第一個或最後一個。
如果我們使用Java 8及以上的版本,Guava在Comparators類方面提供了更好的選擇,比如以下示例:
public static boolean isSorted(List<string> listOfStrings) {/<string>
return Comparators.isInOrder(listOfStrings, Comparator.<string> naturalOrder());/<string>
}
從上面的示例中,使用默認順序來檢查排序列表,但我們也可以使用Comparator來自定義排序檢查。
小結
從本文,我們可以看出,面試的過程中,首先要抓住問題的關鍵點,然後把自己知道的知識點一點點延展開來,讓面試官知道你的深度與廣度,就比如我昨天發的這篇關於浮點數科學計數法的文章《 Stackoverflow: 為什麼將0.1f改為0會使性能降低10倍? 》,浮點數這個知識點基礎嗎? 確實基礎,基礎到大多數程序員而不屑一看! 重要嗎? 真重要,稍不注意就會出錯,比如下面這種寫法:
if(0.9f-0.8f == 0.1f){
System.err.println("equal");
}else {
System.err.println("no equal");
}
特別像我們做金融的,線上出現這種問題就有可能是致命的; 比如我們公司金額全部都用整數,不做浮點數計算,容易損失精度,要麼用BigDecimal。 所以,我也一直強調基礎很重要,比如算法,雖然你工作中用到的很少,但你得知道那幾種基本算法,什麼樣的數據量該選誰最優,正如孤盡大佬所說的“ 一個方法看起來數據量沒多少,沒必要進行算法優化,但是乘以一個訪問量級,那麼它對服務器的性能損耗將是巨大的! ”等等。 。 。
還有我每次分享給大家的文章,真值得你仔細去研究,每篇文章的內容不能保證百分之百的不出錯,但每篇文章引申出來的知識點,就值得你去延伸,自己不動手,又想成為大牛,別做夢了!
真的,編程這條道路上,只能靠自己,誰也幫不了你!
閱讀更多 rabbit兔兔 的文章