我們常說電腦基礎很重要。今天也來談談關於java的一些基礎知識。其實java基礎知識,thinking java無疑是一本非常值得讀的書。java core設計到的範圍也很廣,下面討論一些常用的java core的一些知識。由於java core知識太多,無法枚舉。
下面是將要出現的一些知識:
1,String,StringBuilder,StringBuffer
2,ArrayList,LinkedList,Vector,LinkedBlockingQueue
3,HashMap,HashTable,TreeMap,ConcurrentHashMap
4,int,Integer等
第一個知識:
關於String,StringBuffer,StringBuilder這三個類是在java code中出現頻率很高的。但是作者發現,即使是很多進階程式員都在亂用這些類。或許是早期的習慣所致,特別是StringBuffer和StringBuilder這兩個類。可能是因為早期版本只有StringBuffer的原因,至今很多程式員都對他不離不棄。先說說這三個類的一些區別。
String,是一個final類,不可變對象。一旦聲明了一個String對象,意味著其值不會改變。通常String有三種做法,一.String str = "abc" 。二,String str = new String("adc")。三,加號字串拼接。這兩種用法有非常大的區別。對於第一種用法,jvm會現在常量池中去找"abc"這個值,如果沒有找到,會在堆中建立“adc”對象,然後把值返回常量池。所以這種申明會更節約記憶體,但是它也有限制,不能超過65534。估計在編碼的時候沒有人會寫這麼長的字串定義。對於第二種用法,其實是申請了兩個對象。括弧裡面的"abc"和之前的一樣,然後new
String會在堆裡建一個對象。所以它是會每次都申請一塊記憶體的。對於第三種情況,如果是兩個常量字串相加,如 String str = "ab"+"c",那麼編譯器會最佳化為String str = "abc".如果是變數相加,如String str="ab";String c = "c",str = str+c,(如果c是final修飾的會等同於之前的編譯器最佳化情況),那麼其本質是,+是一個StringBuilder的重載,也就相當於StringBuilder append 然後再toString()。呵呵,加號和StringBuilder一樣,是否就一直應該用加號呢
,方便?對於一個加號或連加的情況是可以的。對於在迴圈中用加號這就是一個悲劇,特別是迴圈的次數越多越悲劇。在迴圈中會在每次迴圈都new StringBuilder.這得產生多少中間變數呀,gc就頻繁了。效能急劇下降。
StringBuilder和StringBuffer都是可變的字串。不過StringBuffer是安全執行緒的。所以在棧封閉的情況下,不需要用它。直接用StringBuilder這個速度要快些。不會有鎖的開銷。這兩者內部都是使用char數組,所以要動態擴容。兩者的預設初始化容量都是char[16]。當容量不夠的時候會擴到原來容量的二倍。所以能夠預估容量的時候就給一個預估值,避免動態擴容。
第二個知識:
ArrayList的實現是動態數組,也就是容器是數組擴容的,這個和StringBuilder一樣,所以能給預估值最好也給預估值。需要強調的是它不是安全執行緒的,不能在多線程環境種使用。數組的最大優勢,他是連續儲存的,查詢快,在末尾插入和刪除都快。但是如果要在中間或者數組首位插入和刪除,需要移動這個元素後面的所有元素,所以效能不言自明。
Vector的實現也是動態數組,不過它每次擴容是原來容量的一半,它是安全執行緒的可以在多線程環境中使用。
LinkedList的實現是鏈表,就是指標和結構體。它是雙向鏈表。也就是在每一個元素(除了首尾元素)都有前驅和後繼。它是在儲存上可以離散的,所以在插入的時候需要重新分配空間,然後調整指標指向,需要更多的記憶體來存指標。它的優勢在於插入和刪除對任何節點都是一樣快。但是查詢速度就要比動態數組慢。
LinkedBlockingQueue,這是一個安全執行緒的有邊界隊列(可以指定)。主要是要介紹一下它的一些阻塞用法,put是阻塞的一直要到隊列到了邊界以內才能插入。offer是立即返回的(也有等代多少時間直到可用的),如果不能插入了就返回false。同樣對於的還有取資料,即刪除資料,分別是take和poll與之對應。
對於第三個知識:
首先需要明白的是,無論什麼hash,hash就是為了速度而生的。hash是無序的。
HashMap是採用hash演算法,用LinkedList解決hash碰撞,到了預設擴容因子0.75會rehash。這是一個耗時的操作,所以在HashMap時,能預估容量的盡量預估,記住擴容因子預設是0.75這意味著你至少要把預估容量設定成實際容量的4/3倍,通常用1.6或3/2。理論上講容量越大hash碰撞的機會就越小,那麼線性尋找的耗時就越少,hash的效能也就越高。需要強調的是HashMap是非安全執行緒的,再多線程環境用HashMap會產生嚴重的後果,如,cpu飆到100%。具體可以見 http://code.alibabatech.com/blog/dev_related_969/hashmap-result-in-improper-use-cpu-100-of-the-problem-investigated.html
非常詳細。
HashTable是HashMap的一種安全實現,但是它並不高效,因為他鎖住寫請求(put remove)時會把所有的Entry都鎖上,並且對於讀也是加鎖的。它也有容量和rehash問題。同上
ConcurretHashMap是HashTable的高效實現,同樣不能有null的key出現,它之所以高效是因為它只對寫請求加鎖,並且只鎖被寫入的這個Entry的對應桶位,所以兩個不相干的寫入(寫的不同位置)是可以並發寫的。所以它是一種高效的實現。同樣有容量的和rehash問題
TreeMap是紅/黑樹狀結構的實現,與hash無關了呵。紅/黑樹狀結構是局部平衡的二叉樹,是在插入和查詢之間做的效能平衡。TreeMap是對key排序,所以key如果沒有實現Comparator介面,就必須指定用於排序的Comparator實現。
對於第四個知識:
主要是想說明的是Integer i = [-128,127]是在常量池中命中對象。所以再對Integer上鎖的時候要注意,有可能是一個全域鎖。