java中的Arrays這個工具類你真的會用嗎

2.構造方法

? 因為是一個工具類唤蔗,所以它的構造方法定義為私有的义锥,且所有的實現(xiàn)方法都是靜態(tài)方法柳沙。也就是說這個類不能被實例化,通俗的講拌倍,就是不能new赂鲤。只能通過類名來直接調用方法(反射除外)廷粒。這樣做的目的是強化該類不可實列化的能力陶耍,突出該類作為工具類的根本職能镰绎。源碼如下:

// Suppresses default constructor, ensuring non-instantiability.

? ? private Arrays() {}

3.常用方法的解析

3.1快速插入集合元素的方法asList(T... a):

基本使用:

? /**

? ? * 數(shù)組轉化為集合

? ? */

? ? @Test

? ? public void toArrayTest(){

? ? ? ? List<Integer> list = Arrays.asList(2,4,5,6,6);

? ? ? ? for (Integer integer : list) {

? ? ? ? ? ? System.out.print(integer+" ");

? ? ? ? }

? ? }

輸出結果:

2 4 5 6 6

看一下源碼:

@SafeVarargs

@SuppressWarnings("varargs")

public static <T> List<T> asList(T... a) {

? ? ? ? return new ArrayList<>(a);

? ? }

// ArrayList的構造方法和屬性

? ? ? private final E[] a;

? ? ? ? ArrayList(E[] array) {

? ? ? ? ? ? a = Objects.requireNonNull(array);

? ? ? ? }

? 這個方法的實現(xiàn)比較簡單韩容,就是調用ArrayList的構造方法恨课,并且參數(shù)是一個數(shù)組宵荒,也就是將我們要構造的數(shù)傳入到ArrayList的構造方法中去递瑰,進行實例化粟誓。

3.2.二分查找的方法

Arrays類中的二分查找八種基本類型都有涉及寺谤,但都是方法的重載仑鸥。其實現(xiàn)原理都是一樣吮播,這里以int類型為例,進行說明眼俊。

基本使用:

? ? @Test

? ? public void binarySearchTest(){

? ? ? ? int[] arrays = {1,4,6,7,9,3};

? ? ? ? // 查找元素為7的下標值

? ? ? ? int result = Arrays.binarySearch(arrays,7);

? ? ? ? System.out.println(result);

? ? }

結果:

3

這個方法主要涉及的一下三個方法:

// 我們常用的方法

public static int binarySearch(int[] a, int key) {

? ? ? ? return binarySearch0(a, 0, a.length, key);

? ? }

/*

? 參數(shù)說明如下: a? 待查找的數(shù)組

? ? fromIndex? ? 查找的開始位置

? ? toIndex? ? ? 查找的結束位置

? ? key? ? ? ? ? 查找的目標值

*/

public static int binarySearch(int[] a, int fromIndex, int toIndex,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int key) {

? ? ? ? // 進行異常檢查

? ? ? ? rangeCheck(a.length, fromIndex, toIndex);

? ? ? ? return binarySearch0(a, fromIndex, toIndex, key);

? ? }

? ? // Like public version, but without range checks.

? ? private static int binarySearch0(int[] a, int fromIndex, int toIndex,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int key) {

? ? ? ? int low = fromIndex;

? ? ? ? int high = toIndex - 1;

? ? ? ? while (low <= high) {


? ? ? ? ? ? // 找出查找范圍的中間值

? ? ? ? ? ? int mid = (low + high) >>> 1;

? ? ? ? ? ? int midVal = a[mid];


? ? ? ? ? ? // 進行比較

? ? ? ? ? ? if (midVal < key)

? ? ? ? ? ? ? ? low = mid + 1;

? ? ? ? ? ? else if (midVal > key)

? ? ? ? ? ? ? ? high = mid - 1;

? ? ? ? ? ? else

? ? ? ? ? ? ? ? return mid; // key found

? ? ? ? }

? ? ? ? return -(low + 1);? // key not found.

? ? }

當然實現(xiàn)的核心方法還是上述私有方法binarySearch0()這個方法意狠,實現(xiàn)的邏輯也不復雜。

第一步就是聲明兩個變量存儲查找區(qū)域的開始和結束疮胖。

第二步 循環(huán)环戈,比較,不斷的縮小比較的范圍获列,直到找到數(shù)組中的值和目標值相同谷市,返回下標,如果沒有找到就返回一個負數(shù)也就是下面的這l兩行代碼:

return mid; // key found

return -(low + 1);? // key not found.

我認為:這個二分法實現(xiàn)的亮點就在于求中間值的移位運算:

int mid = (low + high) >>> 1;

有人就納悶了击孩,為什么還要使用移位運算迫悠,除法不行嗎?主要還是為了性能考量巩梢。因為移位運算占兩個機器周期创泄,而乘除法占四個運算周期,所以移位運算的速度肯定比乘除法的運算速度快很多括蝠,計算量小了可能區(qū)別不大鞠抑,但是計算量很大,就區(qū)別很明顯了忌警。

3.3 數(shù)組的拷貝

? ? @Test

? ? public void testCopyArrange(){

? ? ? ? ? // 原數(shù)組

? ? ? ? int [] srcArray = {11,2,244,5,6,54};

? ? ? ? // 拷貝原數(shù)組長度為3的部分

? ? ? ? int[] descArray = Arrays.copyOf(srcArray,3);


? ? ? ? System.out.println(Arrays.toString(descArray));

? ? }

輸出結果:

[11, 2, 244]

源碼分析:

/* 參數(shù)說明:

? original? 原數(shù)組

? newLength? 拷貝的數(shù)組長度

*/

public static int[] copyOf(int[] original, int newLength) {

? ? ? ? // 聲明一個新數(shù)組的長度搁拙,存儲拷貝后的數(shù)組

? ? ? ? int[] copy = new int[newLength];

? ? ? ? System.arraycopy(original, 0, copy, 0,

? ? ? ? ? ? ? ? ? ? ? ? Math.min(original.length, newLength));

? ? ? ? return copy;

? ? }

public static native void arraycopy(Object src,? int? srcPos,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Object dest, int destPos,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int length);

分析: 主要還是調用了本地的方法arraycopy完成數(shù)組的指定長度拷貝,可以看到源碼并沒有對數(shù)組的長度進行檢查法绵,主要是arraycopy()這個方法時使了Math.min()方法箕速,保證了你聲明的長度在一個安全的范圍之內,如果你拷貝的長度超出了數(shù)組的長度朋譬,就默認拷貝整個數(shù)組盐茎。至于native修飾的方法的使用,可以看看這里徙赢。

System.arraycopy(original, 0, copy, 0,

? ? ? ? ? ? ? ? ? ? ? ? Math.min(original.length, newLength));

當然如果需要拷貝數(shù)組指定的區(qū)間 字柠,可以使用Arrays的copyOfRange(int[] original, int from, int to) 實現(xiàn)原理和arraycopy()方法的原理類似:

? ? @Test

? ? public void testCopy(){

? ? ? ? int [] srcArray = {11,2,244,5,6,54};

? ? ? ? // 拷貝指定范圍的數(shù)組

? ? ? ? int[] descArray = Arrays.copyOfRange(srcArray,0,3);

? ? ? ? System.out.println(Arrays.toString(descArray));

? ? }

輸出結果:

[11, 2, 244]

注: copyOfRange(int[] original, int from, int to)中的參數(shù)to是不包含在拷貝的結果中的,上述的例子狡赐,就只能拷貝到索引為2的元素窑业,不包含索引為3的元素,這點需要注意阴汇。

3.4 equals方法

主要重寫了Object類的equals方法数冬,用來比較兩個數(shù)組內容是否相等,也就是他們中的元素是否相等。

基本用法:

? @Test

? ? public void equalTest(){

? ? ? ? int[] array ={1,2,3,4};

? ? ? ? int[] result ={1,2,3,4};

? ? ? ? System.out.println(Arrays.equals(array,result));

? ? ? ? System.out.println(array == result);

? ? }

結果:

true

false

看源碼之前拐纱,有必要講一下重寫了equals方法之后铜异,兩個對象比較的是值,也就是他們的內容秸架,這點非常的重要揍庄。重寫equals方法的注意事項可以移步這里。

源碼如下:

public static boolean equals(int[] a, int[] a2) {

? ? ? // 基于地址的比較

? ? if (a==a2)

? ? ? ? ? ? return true;

? ? ? ? if (a==null || a2==null)

? ? ? ? ? ? return false;

? ? ? ? int length = a.length;

? ? ? ? //? 基于長度的比較

? ? ? ? if (a2.length != length)

? ? ? ? ? ? return false;


? ? ? ? // 比較每個元素是否相等

? ? ? ? for (int i=0; i<length; i++)

? ? ? ? ? ? if (a[i] != a2[i])

? ? ? ? ? ? ? ? return false;

? ? ? ? return true;

? ? }

源碼說明如下:

源碼判斷了四次东抹,分別是首地址比較蚂子,是否為空,以及長度的比較缭黔,最后對于數(shù)組的各個元素進行比較食茎。

有必要說明下第一個判斷,也就是首地址的比較馏谨。當我們聲明一個數(shù)組變量時别渔,這個變量就代表數(shù)組的首地址,看下面這個代碼:

? ? @Test

? ? public void equalTest(){

? ? ? ? int[] array ={1,2,3,4};

? ? ? ? System.out.println(array);


? ? }

結果:

[I@4f2410ac? ? // [代表數(shù)組 I代表整數(shù) @分隔符 后邊內存地址十六進制

? 這表示的是一個地址惧互。還是因為在聲明一個數(shù)組時哎媚,會在堆里面創(chuàng)建一塊內存區(qū)域,但是這塊內存區(qū)域相對于堆來說可能很小喊儡,不好找拨与。為了方便查找,所以將數(shù)組內存中的首地址表示出來艾猜。虛擬機將地址傳給變量名array买喧。這也是引用類型,傳的是地址匆赃,也就是理解成array指向內存地址(類似于家庭的地址)岗喉,每次運行可能地址都不一樣,因為虛擬機開辟的內存空間可能不一樣炸庞。

理解了這個,那么a==a2就好理解了荚斯,如果兩個數(shù)組內存地址都相同埠居,那么兩個數(shù)組的肯定是相等的。

還有我認為程序寫的比較好的地方就是源碼中對數(shù)組每個元素的比較事期,也就是下面這段代碼滥壕;

? for (int i=0; i<length; i++)

? ? ? ? ? ? if (a[i] != a2[i])

? ? ? ? ? ? ? ? return false;

? ? ? ? return true;

使用a[i] != a2[i] 作為判斷條件,就可以減少比較次數(shù)兽泣,提高了性能绎橘。試想一下如果這里是相等的比較,那每次都要遍歷整個數(shù)組,如果數(shù)據(jù)量大了称鳞,無疑在性能上會慢很多涮较。又一次感嘆到源碼的魅力。

3.5 排序相關的方法sort()和parallelSort()

Arrays 這個類中主要涉及了兩種類型的排序方法串行 sort()和并行parallelSort()這兩個方法冈止,當然對象的排序和基本類型的排序也不太一樣狂票。這里還是以int[]類型的為例。進行說明熙暴。

首先比較兩個方法的性能:


? ? public final int UPPER_LIMIT = 0xffffff;

? ? final int ROUNDS = 10;

? ? final int INCREMENT = 5;

? ? final int INIT_SIZE = 1000;

? ? @Test

? ? public void sortAndParallelSortTest(){


? ? ? ? // 構造不同容量的集合

? ? ? ? for (int capacity = INIT_SIZE; capacity < UPPER_LIMIT ; capacity*= INCREMENT) {

? ? ? ? ? ? ArrayList<Integer> list = new ArrayList<>(capacity);

? ? ? ? ? ? for (int j = 0; j < capacity; j++) {

? ? ? ? ? ? ? ? list.add((int) (Math.random()*capacity));

? ? ? ? ? ? }


? ? ? ? ? ? double avgTimeOfParallelSort = 0;

? ? ? ? ? ? double avgTimeOfSort = 0;

? ? ? ? ? ? for (int j = 0; j <= ROUNDS ; j++) {

? ? ? ? ? ? ? ? // 每次排序都打亂順序

? ? ? ? ? ? ? ? Collections.shuffle(list);

? ? ? ? ? ? ? ? Integer[] arr1 = list.toArray(new Integer[capacity]);

? ? ? ? ? ? ? ? Integer[] arr2 = arr1.clone();


? ? ? ? ? ? ? ? avgTimeOfParallelSort += counter(arr1,true);

? ? ? ? ? ? ? ? avgTimeOfSort += counter(arr2, false);

? ? ? ? ? ? }

? ? ? ? ? ? // 輸出結果

? ? ? ? ? ? output(capacity,avgTimeOfParallelSort/ROUNDS,avgTimeOfSort/ROUNDS);

? ? ? ? }

? ? }

? ? private void output(int capacity, double v, double v1) {

? ? ? ? System.out.println("=======================測試排序的時間=========");

? ? ? ? System.out.println("Capacity"+capacity);

? ? ? ? System.out.println("ParallelSort"+v);

? ? ? ? System.out.println("Sort"+v1);

? ? ? ? System.out.println("比較快的排序是:"+(v < v1 ? "ParallelSort":"Sort"));

? ? }

? ? // 計算消耗的時間

? ? private double counter(Integer[] arr1, boolean b) {

? ? ? ? long begin,end;

? ? ? ? begin = System.nanoTime();

? ? ? ? if(b){

? ? ? ? ? ? Arrays.parallelSort(arr1);

? ? ? ? }else{

? ? ? ? ? ? Arrays.parallelSort(arr1);

? ? ? ? }

? ? ? ? end = System.nanoTime();

? ? ? ? return BigDecimal.valueOf(end-begin,9).doubleValue();

? ? }

部分的測試的結果:

=======================測試排序的時間=========

Capacity1000

ParallelSort6.284099999999999E-4

Sort5.599599999999999E-4

比較快的排序是:Sort

=======================測試排序的時間=========

Capacity5000

ParallelSort0.00163599

Sort0.0018313699999999995

比較快的排序是:ParallelSort

可以看到在數(shù)據(jù)量比較小的情況下闺属,使用sort()方法更快,一旦過了一個閾值周霉,就是ParallelSort()這個方法性能好掂器。這個閾值是多少呢。

我們先看一下parallelSort的源碼:

public static void parallelSort(int[] a) {

? ? ? ? int n = a.length, p, g;

? ? ? ? if (n <= MIN_ARRAY_SORT_GRAN ||

? ? ? ? ? ? (p = ForkJoinPool.getCommonPoolParallelism()) == 1)

? ? ? ? ? ? DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);

? ? ? ? else

? ? ? ? ? ? new ArraysParallelSortHelpers.FJInt.Sorter

? ? ? ? ? ? ? ? (null, a, new int[n], 0, n, 0,

? ? ? ? ? ? ? ? ((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?

? ? ? ? ? ? ? ? MIN_ARRAY_SORT_GRAN : g).invoke();

? ? }

可以看到當數(shù)組的長度小于MIN_ARRAY_SORT_GRAN或者p = ForkJoinPool.getCommonPoolParallelism()) == 1 (在單線程下)的時候俱箱,調用sort()排序的底層實現(xiàn)的DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);Arrays的開頭定義的常量如下:

private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;? ? // 這個值是8192

對比兩者国瓮,也就是在數(shù)組的長度比較大或者是多線程的情況下,優(yōu)先考慮并行排序匠楚,否則使用串行排序巍膘。

兩個排序的核心思想:

sort()方法的核心還是快排和優(yōu)化后的歸并排序, 快速排序主要是對哪些基本類型數(shù)據(jù)(int,short,long等)排序, 而合并排序用于對對象類型進行排序芋簿。

parallelSort()它使用并行排序-合并排序算法峡懈。它將數(shù)組分成子數(shù)組,這些子數(shù)組本身先進行排序然后合并与斤。

由于并行排序和串行排序的底層比較復雜肪康,且篇幅有限,想要詳細了解底層實現(xiàn)的話撩穿,可以移步到串行排序和并行排序

3.6 toString方法

基本用法:

@Test

? ? public void toStringTest(){

? ? ? ? int[] array = {1,3,2,5};

? ? ? ? System.out.println(Arrays.toString(array));

? ? }

結果:

[1, 3, 2, 5]

源碼分析如下:

public static String toString(int[] a) {

? ? ? ? // 1.判斷數(shù)組的大小

? ? ? ? if (a == null)

? ? ? ? ? ? return "null";

? ? ? ? int iMax = a.length - 1;

? ? ? ? if (iMax == -1)

? ? ? ? ? ? return "[]";


? ? ? // 2.使用StringBuilder進行追加

? ? ? ? StringBuilder b = new StringBuilder();

? ? ? ? b.append('[');

? ? ? ? for (int i = 0; ; i++) {

? ? ? ? ? ? b.append(a[i]);

? ? ? ? ? ? if (i == iMax)

? ? ? ? ? ? ? ? return b.append(']').toString();

? ? ? ? ? ? b.append(", ");

? ? ? ? }

? ? }

深圳網(wǎng)站建設www.sz886.com

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末磷支,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子食寡,更是在濱河造成了極大的恐慌雾狈,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抵皱,死亡現(xiàn)場離奇詭異善榛,居然都是意外死亡,警方通過查閱死者的電腦和手機呻畸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門移盆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伤为,你說我怎么就攤上這事咒循。” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵叙甸,是天一觀的道長颖医。 經(jīng)常有香客問我,道長蚁署,這世上最難降的妖魔是什么便脊? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮光戈,結果婚禮上哪痰,老公的妹妹穿的比我還像新娘。我一直安慰自己久妆,他們只是感情好晌杰,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筷弦,像睡著了一般肋演。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烂琴,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天爹殊,我揣著相機與錄音,去河邊找鬼奸绷。 笑死梗夸,一個胖子當著我的面吹牛,可吹牛的內容都是我干的号醉。 我是一名探鬼主播反症,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼畔派!你這毒婦竟也來了铅碍?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤线椰,失蹤者是張志新(化名)和其女友劉穎胞谈,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憨愉,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡呜魄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了莱衩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡娇澎,死狀恐怖笨蚁,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤括细,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布伪很,位于F島的核電站,受9級特大地震影響奋单,放射性物質發(fā)生泄漏锉试。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一览濒、第九天 我趴在偏房一處隱蔽的房頂上張望呆盖。 院中可真熱鬧,春花似錦贷笛、人聲如沸应又。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽株扛。三九已至,卻和暖如春汇荐,著一層夾襖步出監(jiān)牢的瞬間洞就,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工掀淘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留旬蟋,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓繁疤,卻偏偏與公主長得像咖为,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稠腊,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344