Java工具類Arrays中不得不知的常用方法

Arrays

位于java.util包內(nèi)的Arrays類是Java提供的一個操作數(shù)組的工具類彤叉,其內(nèi)部定義了一些常見的用于操作數(shù)組的靜態(tài)方法,下面就按照以下幾個常用類型村怪,梳理一下秽浇。

  • 數(shù)組轉(zhuǎn)List
  • 排序
  • 查找
  • 元素填充
  • 數(shù)組復制
  • toString
  • 相等性判斷

Arrays 數(shù)組操作集

數(shù)組轉(zhuǎn)List ---asList

    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

這個被“普遍”稱為數(shù)組轉(zhuǎn)List的方法,可能是Arrays內(nèi)大家使用頻率最高的一個靜態(tài)方法了甚负。使用起來也很簡單,下面就很容易的實現(xiàn)了將數(shù)組轉(zhuǎn)為List柬焕。

        String[] b = new String[]{"5", "6", "7", "8"};
        List<String> datas = Arrays.asList(b);

當然還有另一種使用方法:

List<String> datas = Arrays.asList("5", "6", "7", "8");

其實,個人感覺“數(shù)組轉(zhuǎn)List”這種說法是沒有意義的梭域;我們都知道在List是一個接口斑举,而真正實現(xiàn)了這個接口的類只有ArrayList,LinkedList,Vector。其中ArrayList和Vector內(nèi)部都是使用“動態(tài)數(shù)組”實現(xiàn)病涨,LinkedList采用鏈表結構實現(xiàn)富玷。根據(jù)上一篇數(shù)據(jù)接口-線性表我們知道,數(shù)據(jù)的物理結構只用順序存儲接口和鏈式存儲結構没宾,List也不出其右凌彬。因此說這個方法實現(xiàn)了數(shù)組拷貝更確切一點沸柔。

下面循衰,根據(jù)他的具體實現(xiàn),我們更能體會到拷貝的意義褐澎。

/**
     * Returns a fixed-size list backed by the specified array.  (Changes to
     * the returned list "write through" to the array.)  This method acts
     * as bridge between array-based and collection-based APIs, in
     * combination with {@link Collection#toArray}.  The returned list is
     * serializable and implements {@link RandomAccess}.
     *
     * <p>This method also provides a convenient way to create a fixed-size
     * list initialized to contain several elements:
     * <pre>
     *     List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
     * </pre>
     *
     * @param a the array by which the list will be backed
     * @return a list view of the specified array
     */
    @SafeVarargs
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }


    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }
        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }
        ……
    }

這里需要注意的是会钝,asList內(nèi)部的ArrayList并不是我們常用的那個ArrayList,而是在Arrays類內(nèi)部的一個私有靜態(tài)類舟扎。從代碼可以看出侦铜,這個內(nèi)部的ArrayList券膀,和常規(guī)的ArrayList相比,并沒有實現(xiàn)List接口奸鬓,而是直接繼承了AbstractList。

以下所說的ArrayList 統(tǒng)一指此處的靜態(tài)類

asList的實現(xiàn)很簡單掸读,返回了一個ArrayList的實例串远,參數(shù)為所要拷貝的數(shù)組名。

可以看到asList() 是接受一個泛型的變長參數(shù)的儿惫,而基本數(shù)據(jù)類型是無法被泛型化的澡罚。而對于泛型而言,基本數(shù)據(jù)類型肾请,實際上會被人為是一個 [x 的類型留搔。 [ 表示這是一個數(shù)組,x 為當前數(shù)組的類型铛铁。

因此隔显,這個方法不能直接“轉(zhuǎn)換”基礎數(shù)據(jù)類型的數(shù)組却妨。

        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
        public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

可以看到,正常情況下荣月,ArrayList的構造函數(shù)完成的工作就是一個賦值操作管呵,把我們傳遞進來的數(shù)組賦給a,而a就是一個數(shù)組哺窄。說白了捐下,這就是一個數(shù)組拷貝的過程。再看ArrayList內(nèi)部實現(xiàn)萌业,get,set 都是根據(jù)數(shù)組下標實現(xiàn)簡單的數(shù)組賦值操作坷襟。這里省略了ArrayList內(nèi)部幾個方法,總之都是對數(shù)組的操作生年,有興趣的同學婴程,可以自行查看源碼。注意抱婉,這個類內(nèi)部档叔,并沒有add方法的具體實現(xiàn),也就是說AbstractList內(nèi)部的add方法并沒有被覆蓋

AbstractList#add

    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

因此蒸绩,通過asList返回的List衙四,一定不能進行add 操作,否則會拋出異常患亿。

通過以上分析传蹈,我們可以得出以下結論:

  • asList 不接受基本數(shù)據(jù)類型的數(shù)組名,作為參數(shù)直接傳遞步藕。
  • asList 通過backed(拷貝)的方式惦界,返回的是一個固定長度的List,這點從方法注釋也可以看到
  • 鑒于第二條咙冗,不能對這個返回的List執(zhí)行add 方法沾歪,可以調(diào)用set方法。
  • 同理不能調(diào)用remove方法雾消,但可以調(diào)用get方法獲取元素灾搏。

這里關于第一個結論,還需要解釋一下仪或,不接受基本數(shù)據(jù)類型的數(shù)組名作為參數(shù)确镊,但是以下實現(xiàn)是可以的。

List ds = Arrays.asList(1, 2, 4, 4, 5);

好了范删,Arrays.asList的用法就說到這里了蕾域。既然都到這里了,順便多說一句,List轉(zhuǎn)數(shù)組的實現(xiàn)旨巷,Collection接口定義了統(tǒng)一的方法toArray巨缘。對于不同的List實現(xiàn),統(tǒng)一調(diào)用即可采呐。

List<String> datas;
.....
String[] result= (String[]) datas.toArray();

排序

sort

sort() 方法顧名思義若锁,主要是實現(xiàn)數(shù)組的排序,默認按升序進行排列斧吐。

Arrays 內(nèi)部關于sort的實現(xiàn)又固,可以大體分為兩類,一類是基本數(shù)據(jù)類型的排序煤率,一類是Object類型的排序仰冠。

  • 基本數(shù)據(jù)類型的排序
    //對數(shù)組排序
    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
    //對數(shù)組從指定位置排序 
    public static void sort(int[] a, int fromIndex, int toIndex) {
        //檢測參數(shù)是否越界
        rangeCheck(a.length, fromIndex, toIndex);
        DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
    }

sort靜態(tài)方法的實現(xiàn),按參數(shù)主要有兩種實現(xiàn)方法蝶糯,一種是數(shù)組整體進行排序洋只;一種是在數(shù)組內(nèi)指定一段起始位置進行排序,之后的對象數(shù)組排序也只是按指定起始位置排序昼捍,不再重復描述识虚。其內(nèi)部具體實現(xiàn)是DualPivotQuicksort(雙軸快速排序)。

這里可以接收的參數(shù)類型除了int數(shù)組妒茬,還可以是long担锤,short,char郊闯,byte妻献,float蛛株,double類型數(shù)組团赁。

  • Object 類型數(shù)組排序

實現(xiàn)Comparable接口的對象數(shù)組排序

    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }

注意,使用這個方法時谨履,提供的“數(shù)組中的對象”必須是實現(xiàn)了Comparable接口的欢摄,也就是說必須告知明確告知,對數(shù)組中的對象是按什么規(guī)則排序笋粟。

實現(xiàn)Comparator接口的排序

    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }

使用這個方法是泛型為T的數(shù)組怀挠,需要提供一個實現(xiàn)了Comparator接口的實例,同理也是必須明確告知排序規(guī)則害捕,如果同時實現(xiàn)了Comparable接口和Comparator接口绿淋,Comparator接口的實現(xiàn)將覆蓋Comparable接口的排序規(guī)則。

對象數(shù)組排序內(nèi)部實現(xiàn)采用了LegacyMergeSort(歸并排序)和TimSort排序尝盼。

parallelSort

parallelSort 是Java8新增的排序方式吞滞,和sort方法不同的是,他采用多線程并行的方式進行排序,當數(shù)據(jù)規(guī)模較大時和sort相比有明顯優(yōu)勢;具體可見arrays-sort-versus-arrays-parallelsort.

public static final int MIN_ARRAY_SORT_GRAN = 1 << 13;

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();
    }

parallelSort 使用方式及可接受參數(shù)類型和sort方法基礎數(shù)據(jù)類型時的參數(shù)完全一致裁赠。

查找

Arrays內(nèi)部的查找殿漠,主要是binarySearch(二分查找法)∨謇蹋可以說绞幌,關于查找到實現(xiàn)分類和排序完全一樣。首先從數(shù)據(jù)類型上也是分為基礎數(shù)據(jù)類型構成的數(shù)組和對象數(shù)組一忱。都支持按特定范圍進行排序莲蜘;對于對象數(shù)組的排序,對象數(shù)組需要實現(xiàn)Comparable接口或者是提供Comparator接口的實例帘营。

    public static int binarySearch(int[] a, int key) {
        return binarySearch0(a, 0, a.length, key);
    }
    //可以在指定范圍排序
    public static int binarySearch(int[] a, int fromIndex, int toIndex,
                                   int key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    //Object[]數(shù)組中的Object必須實現(xiàn)了Comparable接口
    public static int binarySearch(Object[] a, int fromIndex, int toIndex,
                                   Object key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    //提供Comparator實例
    public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) {
        return binarySearch0(a, 0, a.length, key, c);
    }

元素填充

    public static void fill(Object[] a, int fromIndex, int toIndex, Object val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++)
            a[i] = val;
    }

fill()方法,使用很簡單仪吧,也很好理解庄新,將數(shù)組用特定的元素val 填滿即可,也可以是特定位置薯鼠。

復制

這個方法择诈,就是實現(xiàn)兩個將原數(shù)組按指定長度復制到目標數(shù)組內(nèi)返回。

    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

其內(nèi)部使用System.arraycopy方法出皇,這是一個Java提供的native方法羞芍,因此效率會高一些。Java 內(nèi)部關于數(shù)組復制的實現(xiàn)郊艘,都用到了這個方法荷科。

同樣,也包括一個copyOfRange的方法纱注,這個按名字就可以理解畏浆,就是按照范圍進行復制。

toString

這個toString的靜態(tài)方法狞贱,其實也很實用刻获;從下面的代碼的實現(xiàn),可以看出瞎嬉,他的作用就是將我們定義的數(shù)組蝎毡,按照 "[a0,a1,....]"的格式轉(zhuǎn)成字符串,方便我們直接打印整個數(shù)組氧枣,打印出來的日志也會看起來更直觀沐兵,更方便。

public static String toString(double[] a) {
        if (a == null)
            return "null";
        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(a[i]);
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    }

它除了支持8種基礎數(shù)據(jù)類型的數(shù)組外便监,還支持Object類型的數(shù)組扎谎。

相等性

equals

Arrays內(nèi)部關于兩個數(shù)組相等的判斷可以首先看下下面的代碼:

基礎數(shù)據(jù)類型數(shù)組,以long類型為例

    public static boolean equals(float[] a, float[] 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 (Float.floatToIntBits(a[i])!=Float.floatToIntBits(a2[i]))
                return false;

        return true;
    }

Object 類型數(shù)組

public static boolean equals(Object[] a, Object[] 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++) {
            Object o1 = a[i];
            Object o2 = a2[i];
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }

        return true;
    }

我們知道,數(shù)組名代表數(shù)組首地址簿透;因此移袍,從以上代碼可以得出結論,

當兩個數(shù)組不是同一數(shù)組時老充,也就是a==a2 不成立葡盗。

當滿足以下任一條件時:

  • 兩個數(shù)組中有一個為空時
  • 兩個數(shù)組長度不等時
  • 兩個數(shù)組中包含任意不相等的元素時

就認為兩個數(shù)組不相等,反之則認為相等啡浊。對于對象數(shù)組觅够,相同位置的對象均為null是,認為是兩個相同的元素巷嚣。

deepEquals

關于deepEquals和equals的區(qū)別喘先,可以看看這篇文章Java中Arrays類的兩個方法:deepEquals和equals

其他

數(shù)組交換

    /**
     * Swaps x[a] with x[b].
     */
    private static void swap(Object[] x, int a, int b) {
        Object t = x[a];
        x[a] = x[b];
        x[b] = t;
    }

這個方法其實挺實用的,以后如果懶得寫了廷粒,可以直接一行代碼搞定窘拯。

最后

以上分析是基于Java jdk1.8 版本,在Java中由于lamdba表達式坝茎,函數(shù)式編程思想的引入涤姊,Arrays內(nèi)部新增了許多相關的類如Stream 等,考慮到使用頻率嗤放,暫時不展開討論了思喊。


參考內(nèi)容:

https://juejin.im/post/5989841b6fb9a03c38109b8c

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市次酌,隨后出現(xiàn)的幾起案子恨课,更是在濱河造成了極大的恐慌,老刑警劉巖岳服,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剂公,死亡現(xiàn)場離奇詭異,居然都是意外死亡派阱,警方通過查閱死者的電腦和手機诬留,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門斜纪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贫母,“玉大人,你說我怎么就攤上這事盒刚∠倭樱” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵因块,是天一觀的道長橘原。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么趾断? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任拒名,我火速辦了婚禮,結果婚禮上芋酌,老公的妹妹穿的比我還像新娘增显。我一直安慰自己,他們只是感情好脐帝,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布同云。 她就那樣靜靜地躺著,像睡著了一般堵腹。 火紅的嫁衣襯著肌膚如雪炸站。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天疚顷,我揣著相機與錄音旱易,去河邊找鬼。 笑死腿堤,一個胖子當著我的面吹牛咒唆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播释液,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼全释,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了误债?” 一聲冷哼從身側(cè)響起浸船,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寝蹈,沒想到半個月后李命,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡箫老,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年封字,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耍鬓。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡阔籽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牲蜀,到底是詐尸還是另有隱情笆制,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布涣达,位于F島的核電站在辆,受9級特大地震影響证薇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜匆篓,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一浑度、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸦概,春花似錦俺泣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谨设,卻和暖如春熟掂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扎拣。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工赴肚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人二蓝。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓誉券,卻偏偏與公主長得像,于是被迫代替她去往敵國和親刊愚。 傳聞我的和親對象是個殘疾皇子踊跟,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法鸥诽,內(nèi)部類的語法商玫,繼承相關的語法,異常的語法牡借,線程的語...
    子非魚_t_閱讀 31,622評論 18 399
  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法拳昌,而不是構造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Fa...
    孫小磊閱讀 1,981評論 0 3
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等钠龙,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,497評論 0 3
  • Java 語言支持的類型分為兩類:基本類型和引用類型炬藤。整型(byte 1, short 2, int 4, lon...
    xiaogmail閱讀 1,347評論 0 10
  • 我為什么不睡她們 昨天發(fā)了篇《你為什么泡不到妞》的文章受到大家的熱烈歡迎,某時間段內(nèi)一度風靡朋友圈碴里,當然“威逼力誘...
    easy喂閱讀 312評論 2 2