編寫高質量代碼:改善Java程序的151個建議(第5章:數組和集合___建議67~82)

有些東西,當你即將失去了的時候,你才懂得它的彌足珍貴,也許亡羊補牢,爲時未晚!html

建議67:不一樣的列表選擇不一樣的遍歷算法算法

建議68:頻繁插入和刪除時使用LinkedList數組

建議69:列表相等只關心元素數據緩存

建議70:子列表只是原列表的一個視圖安全

建議71:推薦使用subList處理局部列表多線程

建議72:生成子列表後不要再操做原列表性能

建議73:使用Comparator進行排序測試

建議74:不推薦使用binarySearch對列表進行檢索大數據

建議75:集合中的元素必須作到compareTo和equals同步加密

建議76:集合運算的並集、交集、差集

建議77:使用shuffle打亂列表

建議78:減小hashmap中元素的數量

建議79:集合中哈希碼不要重複

建議80:多線程使用Vector或HashTable

建議81:非穩定排序推薦使用List

建議82:由點及面,集合你們族總結

建議67:不一樣的列表選擇不一樣的遍歷算法

測試來看簡單for循環比foreach能快那麼一丟丟。

建議68:頻繁插入和刪除時使用LinkedList

ArrayList在進行插入元素時:

public void add(int index, E element) {
    //檢查下標是否越界
    rangeCheckForAdd(index);
    //若須要擴容,則增大底層數組的長度
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //給index下標以後的元素(包括當前元素)的下標加1,空出index位置
    System.arraycopy(elementData, index, elementData, index + 1,size - index);
    //賦值index位置元素
    elementData[index] = element;
    //列表長度加1
    size++;
}

注意看arrayCopy方法,只要插入一個元素,其後的元素就會向後移動一位,雖然arrayCopy是一個本地方法,效率很是高,但頻繁的插入,每次後面的元素都要拷貝一遍,效率變的就低了。而使用LinkedList就顯得更好了,LinkedList是一個雙向列表,它的插入只是修改了相鄰元素的next和previous引用。

原理不說了。

修改元素:LinkedList不如ArrayList,由於LinkedList是按順序存儲的,所以定位元素必然是一個遍歷過程,效率大打折扣。而ArrayList的修改動做是數組元素的直接替換,簡單高效。

LinkedList:刪除和插入效率高;ArrayList:修改元素效率高。

建議69:列表相等只關心元素數據

判斷集合是否相等只須關注元素是否相等便可,不用看容器

建議70:子列表只是原列表的一個視圖

List接口提供了subList方法,其做用是返回一個列表的子列表,這與String類subSting有點相似。

注意:subList產生的列表只是一個視圖,全部的修改動做直接做用於原列表。

建議71:推薦使用subList處理局部列表

咱們來看這樣一個簡單的需求:一個列表有100個元素,如今要刪除索引位置爲20~30的元素。這很簡單,一個遍歷很快就能夠完成,代碼以下:

public class Client71 {
    public static void main(String[] args) {
        // 初始化一個固定長度,不可變列表
        List<Integer> initData = Collections.nCopies(100, 0);
        // 轉換爲可變列表
        List<Integer> list = new ArrayList<Integer>(initData);
        // 遍歷,刪除符合條件的元素
        for (int i = 0; i < list.size(); i++) {
            if (i >= 20 && i < 30) {
                list.remove(i);
            }
        }
    }
}

這段代碼很符合個人風格!

下面用subList解決這個問題:

public static void main(String[] args) {
        // 初始化一個固定長度,不可變列表
        List<Integer> initData = Collections.nCopies(100, 0);
        // 轉換爲可變列表
        List<Integer> list = new ArrayList<Integer>(initData);
        //刪除指定範圍內的元素
        list.subList(20, 30).clear();
    }

建議72:生成子列表後不要再操做原列表

注意:subList生成子列表後,保持原列表的只讀狀態。

建議73:使用Comparator進行排序

一、默認排序

Collections.sort(list)

二、按某字段排序

Collections.sort(list,new PositionComparator())

知道這些就好了,原文寫的太晦澀了,我感受沒啥用!

想了解更多的使用Comparator進行排序

建議74:不推薦使用binarySearch對列表進行檢索

不推薦,乾脆就不要寫了,好嗎?

對列表進行檢索就使用indexOf就挺好的!

public class Client74 {
    public static void main(String[] args) {
        List<String> cities = new ArrayList<String> ();
        cities.add("上海");
        cities.add("廣州");
        cities.add("廣州");
        cities.add("北京");
        cities.add("天津");
        //indexOf取得索引值
        int index1= cities.indexOf("廣州");
        //binarySearch找到索引值
        int index2= Collections.binarySearch(cities, "廣州");
        System.out.println("索引值(indexOf):"+index1);
        System.out.println("索引值(binarySearch):"+index2);
    }
}

binarySearch採用的是二分法搜索的Java版實現。從中間開始搜索,結果確定是2了。

使用binarySearch的二分法查找比indexOf的遍歷算法性能上高不少,特別是在大數據集且目標值又接近尾部時,binarySearch方法與indexOf方法相比,性能上會提高几十倍,所以從性能的角度考慮時能夠選擇binarySearch。

建議75:集合中的元素必須作到compareTo和equals同步

一看到標題,短期有些發懵,簡單來講,

indexOf依賴equals方法查找,binarySearch則依賴compareTo方法查找;

equals是判斷元素是否相等,compareTo是判斷元素在排序中的位置是否相同。

注意:實現了compareTo方法就應該覆寫equals方法,確保二者同步。

建議76:集合運算的並集、交集、差集

一、並集 addAll

二、交集 retainAll

三、差集 removeAll

四、無重複的並集

並集是集合A加集合B,那若是集合A和集合B有交集,就須要確保並集的結果中只有一份交集,此爲無重複的並集,此操做也比較簡單,代碼以下:

//刪除在list1中出現的元素
list2.removeAll(list1);
//把剩餘的list2元素加到list1中
list1.addAll(list2);

之因此介紹並集、交集、差集,那是由於在實際開發中,不多有使用JDK提供的方法實現集合這些操做,基本上都是採用了標準的嵌套for循環:要並集就是加法,要交集就是contains判斷是否存在,要差集就使用了!contains(不包含)。

之因此會寫這麼low的東西是想告誡本身多用JDK提供的方法,不要總想着本身去實現,不少東西Java老賊都有現成的!

建議77:使用shuffle打亂列表

簡而言之,言而總之,shuffle就是用來打亂list列表順序的,應用的場景好比

一、在抽獎程序中:好比年會的抽獎程序,先使用shuffle把員工順序打亂,每一個員工的中獎概率相等,而後就能夠抽出第一名、第二名。

二、用於安全傳輸方法:好比發送端發送一組數據,先隨機打亂順序,而後加密發送,接收端解密,而後進行排序!

建議78:減小hashmap中元素的數量

一、hashmap和ArrayList的長度都是動態增長的,兩者機制有些不一樣

二、先說hashmap,它在底層是以數組的方式保存元素的,其中每個鍵值對就是一個元素,也就是說hashmap把鍵值對封裝成一個entry對象,而後再將entry對象放到數組中,也就是說hashmap比ArrayList多一層封裝,多出一倍的對象。

在插入鍵值對時會作長度校驗,若是大於或者等於閾值,則數組長度會增大一倍。

hashMap的size大於數組的0.75倍時,就開始擴容,一次擴容兩倍。

三、ArrayList的擴容策略,它是在小於數組長度的時候纔會擴容1.5倍。

綜合來講,HashMap比ArrayList多了一層Entry的底層封裝對象,多佔用了內存,而且它的擴容策略是2倍長度的遞增,同時還會根據閾值判斷規則進行判斷,所以相對於ArrayList來講,一樣的數據,它就會優先內存溢出。

注:Entry是Map中用來保存一個鍵值對的,而Map實際上就是多個Entry的集合。

建議79:集合中哈希碼不要重複

一、Java中hashcode的理解

JVM每new一個Object,都會在Hash哈希表中產生一個hashcode。假設不一樣的對象產生了相同的hashcode,hash key相同致使衝突,那麼就在這個hash key的地方產生了一個鏈表,將一樣hashcode的對象放到單鏈表中,串到一塊兒。

重寫equals時必定要重寫hashcode方法

兩個相等對象的equals方法必定爲true, 但兩個hashcode相等的對象不必定是相等的對象。

hashcode相等僅僅保證兩個對象在hash表裏的同一個hash鏈上,繼而經過equals方法才能肯定是否是同一個對象。

總而言之,hashmap中hashcode應避免衝突。

建議80:多線程使用Vector或HashTable

Vector是ArrayList的多線程版本,HashTable是HashMap的多線程版本。

建議81:非穩定排序推薦使用List

什麼叫非穩定排序?

public static void main(String[] args) {
        SortedSet<Person> set = new TreeSet<Person>();
        // 身高180CM
        set.add(new Person(180));
        // 身高175CM
        set.add(new Person(175));
        set.first().setHeight(185);
        for (Person p : set) {
            System.out.println("身高:" + p.getHeight());
        }
    }

奇了怪了,爲何呢?這個就是非穩定排序。

SortedSet接口(TreeSet實現了此接口)只是定義了在給集合加入元素時將其進行排序,並不能保證元素修改後的排序結果,所以TreeSet適用於不變量的集合數據排序。

解決方法:

一、Set集合重排序:從新生成一個Set對象,再排序。

set.first().setHeight(185);
//set重排序
set=new TreeSet<Person>(new ArrayList<Person>(set));

感受若是這麼寫就失去了代碼了樂趣了,太愚蠢了,pass。

二、使用List解決

使用Collections.sort()方法對List排序。

list中容許有重複的元素,避免重複能夠先轉成HashSet,去重後再轉回來。

建議82:由點及面,集合你們族總結

Java中的集合類實在是太豐富了,有經常使用的ArrayListHashMap,也有不經常使用的StackQueue,有線程安全的VectorHashTable,也有線程不安全的LinkedListTreeMap,有阻塞式的ArrayBlockingQueue,也有非阻塞式的PriorityQueue等,整個集合你們族很是龐大,能夠劃分如下幾類:

一、List:

實現List接口的集合主要有:ArrayList、LinkedList、Vector、Stack,其中ArrayList是一個動態數組,LinkedList是一個雙向鏈表,Vector是一個線程安全的動態數組,Stack是一個對象棧,遵循先進後出的原則。  

stack簡介:

Stack來自於Vector,那麼顯然stack的底層實現是數組。

stack的方法:

① push(xx); //入棧

② pop() ; //棧頂元素出棧

③ empty() ; //斷定棧是否爲空

④ peek(); //獲取棧頂元素

⑤ search(xx); //判端元素num是否在棧中,若是在返回1,不在返回-1。

二、Set:

Set是不包含重複元素的集合,其主要實現類有:EnumSet、HashSet、TreeSet,其中EnumSet是枚舉類型專用Set,全部元素都是枚舉類型;HashSet是以哈希嗎決定其元素位置的Set,其原理和hashmap類似,它提供快速的插入和查找方法;TreeSet是一個自動排序的Set,它實現了SortedSet接口。

三、Map:

HashMap、HashTable、Properties、EnumMap、TreeMap等。

其中properties是hashtable的子類,它的主要用途是從property文件中加載數據,並提供方便的操做,EnumMap則要求其key必須是一個枚舉類型。

map中還有一個WeakHashMap類須要簡單說明一下,它是一個採用弱鍵方式的map類,它的特色是:WeakHashMap對象的存在並不會阻止垃圾回收器對鍵值對的回收,也就是說使用WeakHashMap不用擔憂內存溢出的問題,GC會自動刪除不用的鍵值對,但存在一個嚴重的問題:GC是靜悄悄的回收的(什麼時候回收,God,Knows!)咱們的程序沒法知曉該動做,存在着重大的隱患。

WeakHashMap存在重大隱患,那這個東西何時使用呢?

在《Effective Java》一書中第六條,消除陳舊對象時,提到了weakHashMap,使用短期內就過時的緩存時最好使用weakHashMap。

四、Queue:

淺談Java隊列Queue

五、數組:

數組與集合的最大區別是數組可以容納基本類型,而集合只能容納引用類型,全部的集合底層存儲的都是數組。

 

編寫高質量代碼:改善Java程序的151個建議@目錄

相關文章
相關標籤/搜索