java arraylist排序_Java 开发者最容易犯的10个错误【译】
我最近在學(xué)習(xí) Java,覺得這篇舊文不錯,就翻譯了一下,感覺對新手有些幫助。
原文:https://www.programcreek.com/2014/05/top-10-mistakes-java-developers-make/
譯者: @方應(yīng)杭
顧問:張博
這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關(guān)鍵詞的趨勢而分析得出的。它們可能并不是真正的 10 個最多的錯誤,但還是挺普遍的。如果你有異議,可以給我留言。如果你能告訴我其他常見的錯誤,我會非常感謝你。
1 將 Array 轉(zhuǎn)換成 ArrayList 時出錯
一些開發(fā)者經(jīng)常用這樣的代碼將 Array 轉(zhuǎn)換成 ArrayList
List<String> list = Arrays.asList(arr);Arrays.asList() 的返回值是一個 ArrayList 類的對象,這個 ArrayList 類是 Arrays 類里的一個私有靜態(tài)類(java.util.Arrays.ArrayList),并不是 java.util.ArrayList 類。
java.util.Arrays.ArrayList 有 set() / get() / contains() 方法,但是并不提供任何添加元素的方法,因此它的長度是固定的。如果你希望得到一個 java.util.ArrayList 類的實例,你應(yīng)該這么做:
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));ArrayList 的構(gòu)造函數(shù)可以接受一個 Collection 實例,而 Collection 是 java.util.Arrays.ArrayList 的超類。
2 檢查 array 里是否含有某個值時出錯
一些開發(fā)者會這么寫:
Set<String> set = new HashSet<String>(Arrays.asList(arr)); return set.contains(targetValue);上面的代碼可以工作,但是其實沒必要把 list 轉(zhuǎn)為 set,這有些浪費(fèi)時間,簡單的寫法是這樣的:
Arrays.asList(arr).contains(targetValue);或者這樣的
for(String s: arr){if(s.equals(targetValue))return true; } return false;這兩種寫法中,前者可讀性更好。
3 遍歷 list 移除元素時出錯
下面的代碼在迭代時移除了元素:
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d")); for (int i = 0; i < list.size(); i++) {list.remove(i); } System.out.println(list);得到的結(jié)果是
[b, d]這種代碼的問題在于,當(dāng)元素被移除時,list 的長度也隨之變小了,index 也同時發(fā)生了變化。所以,如果你想要在循環(huán)中使用 index 移除多個元素,它可能不能正常工作。
你可能認(rèn)為在循環(huán)中刪除元素的正確方法是迭代器,比如 foreach 循環(huán)看起來就是一個迭代器,其實這樣還是有問題。
考慮以下代碼(代碼 1):
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));for (String s : list) {if (s.equals("a"))list.remove(s); }會拋出 ConcurrentModificationException 異常。
要正確地在遍歷時刪除元素,應(yīng)該這么寫(代碼 2):
ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d")); Iterator<String> iter = list.iterator(); while (iter.hasNext()) {String s = iter.next();if (s.equals("a")) {iter.remove();} }你必須在每次循環(huán)里先調(diào)用 .next() 再調(diào)用 .remove()。
在代碼 1 中的 foreach 循環(huán)中,編譯器會在元素的刪除操作之后調(diào)用 .next(),導(dǎo)致 ConcurrentModificationException 異常,如果你想深入了解,可以看看 ArrayList.iterator() 的源碼。
4 用 Hashtable 還是用 HashMap
一般來說,算法中的 Hashtable 是一種常見的數(shù)據(jù)結(jié)構(gòu)的名字。但是在 Java 中,這種數(shù)據(jù)結(jié)構(gòu)的名字卻是 HashMap,不是 Hashtable。Java 中 Hashtable 和 HashMap 的最重要的區(qū)別之一是 Hashtable 是同步的(synchronized)。因此大部分時候你不需要用 Hashtable,應(yīng)該用 HashMap。
5 直接使用 Collection 的原始類型時出錯
在 Java 中,「原始類型」和「無限制通配符類型」很容易被搞混。舉例來說,Set 是一個原始類型,而 Set<?> 是一個無限制通配符類型。
下面的代碼中的 add 接受原始類型 List 作為參數(shù):
public static void add(List list, Object o){list.add(o); } public static void main(String[] args){List<String> list = new ArrayList<String>();add(list, 10);String s = list.get(0); }這個代碼會在運(yùn)行時才拋出異常:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat ...使用原始類型的 collection 是很危險的,因為原始類型沒有泛型檢查。Set / Set<?> / Set<Object> 之間有非常大的差異,詳情可以看看《Set vs. Set<?>》和《Java Type Erasure Mechanism》。
6 訪問級別設(shè)置過高
很多開發(fā)者為了省事,把類字段標(biāo)記為 public,這不是個好習(xí)慣。好習(xí)慣應(yīng)該是將訪問級別設(shè)置得越低越好。
詳見《public, default, protected, and private》。
7 ArrayList 和 LinkedList 選用錯誤
如果不了解 ArrayList 和 LinkedList 的區(qū)別,你很容易會傾向于使用 ArrayList,因為它看起來更常見。
但是,ArrayList 和 LinkedList 有巨大的性能差異。簡單來說,如果 add/remove 操作較多,則應(yīng)該使用 LinkedList;如果隨機(jī)訪問操作較多,則應(yīng)該使用 ArrayList。
如果你想深入了解這些性能差異,可以看看《ArrayList vs. LinkedList vs. Vector》。
8 可變還是不可變?
不可變對象有很多好處,比如簡單、安全等。但是不可變對了要求每次改動都生成新的對象,對象一多就容易對垃圾回收造成壓力。我們應(yīng)該在可變對象和不可變對象上找到一個平衡點。
一般來說,可變對象可以避免產(chǎn)生太多中間對象。一個經(jīng)典的例子就是連接大量字符串。如果你使用不可變字符串,你就會造出許多用完即棄的中間對象。這既浪費(fèi)時間又消耗 CPU,所以這種情況下你應(yīng)該使用可變對象,如 StringBuilder:
String result=""; for(String s: arr){result = result + s; }還有一些情況值得使用可變對象。比如你可以通過將可變對象傳入方法來收集多個結(jié)果,從而繞開語法的限制。再比如排序和過濾操作,雖然你可以返回新的被排序之后的對象,但是如果元素數(shù)量眾多,這就會浪費(fèi)不少內(nèi)存。
擴(kuò)展閱讀《為什么字符串是不可變的》。
9 超類和子類的構(gòu)造函數(shù)
class Super {String s;public Super(String s){this.s = s;} }public class Sub extend Super{int x = 200;public Sub(String s){ // 編譯錯誤}public Sub(){ // 編譯錯誤System.out.println("Sub");}public static void main(String[] args){Sub s = new Sub();} }上述代碼會有編譯錯誤,因為沒有實現(xiàn) Super() 構(gòu)造函數(shù)。Java 中,如果一個類沒有定義構(gòu)造函數(shù),編譯器將會插入一個默認(rèn)的沒有參數(shù)的構(gòu)造函數(shù)。但是如果 Super 類已經(jīng)有了一個構(gòu)造函數(shù) Super(String s),那么編譯器就不會插入這個默認(rèn)的無參數(shù)的構(gòu)造函數(shù)。這就是上述代碼的遇到的情況。
Sub 類的兩個構(gòu)造函數(shù),一個有參數(shù)一個沒有參數(shù),都會調(diào)用 Super 類的無參數(shù)構(gòu)造函數(shù)。因為編譯器會嘗試在 Sub 類的兩個構(gòu)造函數(shù)里插入 super(),由于 Super 類沒有無參數(shù)構(gòu)造函數(shù),所以編譯器就報錯了。
解決這個問題,有三種方法:
想了解更多詳情,可以看《Constructors of Sub and Super Classes in Java?》。
10 用 "" 還是用構(gòu)造函數(shù)
字符串可以通過兩種途徑來構(gòu)造:
// 1. 使用雙引號 String x = "abd"; // 2. 使用構(gòu)造函數(shù) String y = new String("abc");有什么區(qū)別呢?
下面的代碼可以很快地告訴你區(qū)別:
String a = "abcd"; String b = "abcd"; System.out.println(a == b); // True System.out.println(a.equals(b)); // TrueString c = new String("abcd"); String d = new String("abcd"); System.out.println(c == d); // False System.out.println(c.equals(d)); // True想了解這兩種方式生成的字符串在內(nèi)存中是如何存在的,可以看看《Create Java String Using ” ” or Constructor?》
總結(jié)
這 10 個錯誤是我綜合 GitHub 上的項目、StackOverflow 上的問答和 Google 搜索關(guān)鍵詞的趨勢而分析得出的。它們可能并不是真正的 10 個最多的錯誤,但還是挺普遍的。如果你有異議,可以給我留言。如果你能告訴我其他常見的錯誤,我會非常感謝你。
總結(jié)
以上是生活随笔為你收集整理的java arraylist排序_Java 开发者最容易犯的10个错误【译】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: snipaste滚动截图方法_电脑必备!
- 下一篇: ubuntu新建python代码文件_[