首頁  >  文章  >  Java  >  java提高篇(十九)-----數組之二

java提高篇(十九)-----數組之二

黄舟
黄舟原創
2017-02-10 11:51:331011瀏覽

        前一節主要介紹了陣列的基本概念,對什麼是數組稍微深入了一點點,在這篇博文中主要介紹數組的其他方面。

      三、性能?請優先考慮數組

       在java中有很多方式來儲存一系列數據,而且在操作上面比數組方便的多?但為什麼我們還需要使用數組,而不是取代它?陣列與其他種類的容器之間的差異有三個面向:效率、類型和保存基本類型的能力。 在java中,數組是一種效率最高的儲存和隨機存取物件引用序列的方式。

      在項目設計中數組使用的越來越少了,而且它確實是沒有List、Set這些集合使用方便,但是在某些方面數組還是存在一些優勢的,例如:速度,而且集合類別的底層也都是透過數組來實現的。

--------这是ArrayList的add()------
    public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
    }

       下面利用陣列和list來做一些操作比較。

       一、求和


Long time1 = System.currentTimeMillis();
        for(int i = 0 ; i < 100000000 ;i++){
            sum += arrays[i%10];
        }
        Long time2 = System.currentTimeMillis();
        System.out.println("数组求和所花费时间:" + (time2 - time1) + "毫秒");
        Long time3 = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            sum  += list.get(i%10);
        }
        Long time4 = System.currentTimeMillis();
        System.out.println("List求和所花费时间:" + (time4 - time3) + "毫秒");
--------------Output:
数组求和所花费时间:696毫秒
List求和所花费时间:3498毫秒


public void ensureCapacity(int minCapacity) {
        modCount++;  
        int oldCapacity = elementData.length;
        /**
         * 若当前需要的长度超过数组长度时进行扩容处理
         */
        if (minCapacity > oldCapacity) {
            Object oldData[] = elementData;    
            int newCapacity = (oldCapacity * 3) / 2 + 1;    //扩容
            if (newCapacity < minCapacity)
                newCapacity = minCapacity;
            //拷贝数组,生成新的数组
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
速率是集合的5倍左右。其實在list集合中,求和當中有一個致命的動作:list.get(i)。這個動作是進行拆箱動作,Integer物件透過intValue方法自動轉換成一個int基本型,在這裡就產生了不必要的效能消耗。

      所以在效能要求較高的場景中請優先考慮陣列。

       四、變長陣列?

       陣列是定長的,一旦初始化宣告後是不可變更長度的。這對我們在實際開發中是非常不方便的,聰明的我們肯定是可以找到方法來實現的。就如java不能實作多重繼承一樣,我們一樣可以利用內部類別和介面來實作(請參考:java提高篇(九)-----實作多重繼承)。

       那麼如何來實現變長數組呢?我們可以利用List集合add方法裡面的擴容思路來模擬實作。以下是ArrayList的擴容方法:

public class ArrayUtils {
    /**
     * @desc 对数组进行扩容
     * @author chenssy
     * @data 2013-12-8
     * @param <T>
     * @param datas 原始数组
     * @param newLen 扩容大小
     * @return T[]
     */
    public static <T> T[] expandCapacity(T[] datas,int newLen){
        newLen = newLen < 0 ? datas.length :datas.length + newLen;   
        //生成一个新的数组
        return Arrays.copyOf(datas, newLen);
    }
    
    /**
     * @desc 对数组进行扩容处理,1.5倍
     * @author chenssy
     * @data 2013-12-8
     * @param <T>
     * @param datas  原始数组
     * @return T[]
     */
    public static <T> T[] expandCapacity(T[] datas){
        int newLen = (datas.length * 3) / 2;      //扩容原始数组的1.5倍
        //生成一个新的数组
        return Arrays.copyOf(datas, newLen);
    }
    
    /**
     * @desc 对数组进行扩容处理,
     * @author chenssy
     * @data 2013-12-8
     * @param <T>
     * @param datas 原始数组
     * @param mulitiple 扩容的倍数
     * @return T[]
     */
    public static <T> T[] expandCapacityMul(T[] datas,int mulitiple){
        mulitiple = mulitiple < 0 ? 1 : mulitiple;
        int newLen = datas.length * mulitiple;
        return Arrays.copyOf(datas,newLen );
    }
}

       這段程式碼對我們有用的地方就在於我們有用的地方就這樣。它的想法是將原始數組拷貝到新數組中,新數組是原始數組長度的1.5倍。所以模擬的陣列擴容程式碼如下:

public class Test {
    public static void main(String[] args) {
        Person person_01 = new Person("chenssy_01");
        
        Person[] persons1 = new Person[]{person_01};
        Person[] persons2 = Arrays.copyOf(persons1,persons1.length);
        
        System.out.println("数组persons1:");
        display(persons1);
        System.out.println("---------------------");
        System.out.println("数组persons2:");
        display(persons2);
        //改变其值
        persons2[0].setName("chessy_02");
        System.out.println("------------改变其值后------------");
        System.out.println("数组persons1:");
        display(persons1);
        System.out.println("---------------------");
        System.out.println("数组persons2:");
        display(persons2);
    }
    public static void display(Person[] persons){
        for(Person person : persons){
            System.out.println(person.toString());
        }
    }
}
-------------Output:
数组persons1:
姓名是:chenssy_01
---------------------
数组persons2:
姓名是:chenssy_01
------------改变其值后------------
数组persons1:
姓名是:chessy_02
---------------------
数组persons2:
姓名是:chessy_02

       透過這種迂迴的方式我們可以實現陣列的擴充。因此在專案中如果確實需要變長的資料集,數組也是在考慮範圍之內的,我們不能因為他是固定長度而排斥他!

       五、數組複製問題

       以前在做脆皮的時候由於集合非常乾的時間由於集合轉換成數組然後再通過Arrays.copyOf拷貝,在轉換成集合,個人覺得非常方便,殊不知我已經陷入了其中的陷進!我們知道若數組元素為對象,則數組裡面資料是對象引用

public static void main(String[] args) {
        int[] datas = new int[]{1,2,3,4,5};
        List list = Arrays.asList(datas);
        System.out.println(list.size());
    }
------------Output:
1

       從結果中發現,persons1中的       從結果中發現,persons1中的值也發生了淺拷貝問題。所以透過Arrays.copyOf()方法產生的陣列是一個淺拷貝。同時數組的clone()方法也是,集合的clone()方法也是,所以我們在使用拷貝方法的同時一定要注意淺拷貝這問題。

🎜

       有关于深浅拷贝的博文,参考:

      渐析java的浅拷贝和深拷贝:http://www.php.cn/

      使用序列化实现对象的拷贝:http://www.php.cn/

       六、数组转换为List注意地方

       我们经常需要使用到Arrays这个工具的asList()方法将其转换成列表。方便是方便,但是有时候会出现莫名其妙的问题。如下:

public static void main(String[] args) {
        int[] datas = new int[]{1,2,3,4,5};
        List list = Arrays.asList(datas);
        System.out.println(list.size());
    }
------------Output:
1

       结果是1,是的你没有看错, 结果就是1。但是为什么会是1而不是5呢?先看asList()的源码

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

       注意这个参数:T…a,这个参数是一个泛型的变长参数,我们知道基本数据类型是不可能泛型化的,也是就说8个基本数据类型是不可作为泛型参数的,但是为什么编译器没有报错呢?这是因为在java中,数组会当做一个对象来处理,它是可以泛型的,所以我们的程序是把一个int型的数组作为了T的类型,所以在转换之后List中就只会存在一个类型为int数组的元素了。所以我们这样的程序System.out.println(datas.equals(list.get(0)));输出结果肯定是true。当然如果将int改为Integer,则长度就会变成5了。

       我们在看下面程序:

enum Week{Sum,Mon,Tue,Web,Thu,Fri,Sat}
    public static void main(String[] args) {
        Week[] weeks = {Week.Sum,Week.Mon,Week.Tue,Week.Web,Week.Thu,Week.Fri};
        List<Week> list = Arrays.asList(weeks);
        list.add(Week.Sat);
    }

       这个程序非常简单,就是讲一个数组转换成list,然后改变集合中值,但是运行呢?

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:131)
    at java.util.AbstractList.add(AbstractList.java:91)
    at com.array.Test.main(Test.java:18)

       编译没错,但是运行竟然出现了异常错误!UnsupportedOperationException ,当不支持请求的操作时,就会抛出该异常。从某种程度上来说就是不支持add方法,我们知道这是不可能的!什么原因引起这个异常呢?先看asList()的源代码:

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

       这里是直接返回一个ArrayList对象返回,但是注意这个ArrayList并不是java.util.ArrayList,而是Arrays工具类的一个内之类:

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) {
            if (array==null)
                throw new NullPointerException();
        a = array;
    }
       /** 省略方法 **/
    }

       但是这个内部类并没有提供add()方法,那么查看父类:

public boolean add(E e) {
    add(size(), e);
    return true;
    }
    public void add(int index, E element) {
    throw new UnsupportedOperationException();
    }

       这里父类仅仅只是提供了方法,方法的具体实现却没有,所以具体的实现需要子类自己来提供,但是非常遗憾

这个内部类ArrayList并没有提高add的实现方法。在ArrayList中,它主要提供了如下几个方法:

       1、size:元素数量

       2、toArray:转换为数组,实现了数组的浅拷贝。

       3、get:获得指定元素。

       4、contains:是否包含某元素。

       所以綜上所述,asList回傳的是長度不變的清單。數組是多長,轉換成的列表是多長,我們是無法透過add、remove來增加或減少其長度的。

以上就是java提高篇(十九)-----數組之二的內容,更多相關內容請關注PHP中文網(www.php.cn)!  


 

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn