ホームページ  >  記事  >  Java  >  Java 改良章 (19) -----配列 2

Java 改良章 (19) -----配列 2

黄舟
黄舟オリジナル
2017-02-10 11:51:331009ブラウズ

前のセクションでは主に配列の基本概念を紹介しましたが、このブログ投稿では主に配列のその他の側面について詳しく説明します。

3. パフォーマンス?配列を優先してください

Java には一連のデータを格納する方法がたくさんありますが、配列よりも操作がはるかに便利ですか?しかし、なぜ配列を置き換えるのではなく、配列を使用する必要があるのでしょうか?配列は、効率、型、プリミティブ型を保持する機能という 3 つの点で他の種類のコンテナーと異なります。

Java では、配列はオブジェクト参照のシーケンスを格納し、ランダムにアクセスする最も効率的な方法です。

プロジェクト設計で配列が使用されることはますます少なくなり、確かに List や Set ほど便利ではありませんが、配列には依然として次のようないくつかの点で利点があります。コレクション クラスの最下層も配列を通じて実装されます。

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

配列とリストを使用していくつかの比較演算を実行してみましょう。

1. Sum

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毫秒


上記の時間消費量から、基本型の合計計算では配列はセットよりも約 5 倍高速です。実際、リスト コレクションには、list.get(i) という合計に致命的なアクションがあります。このアクションはアンボックス化アクションであり、Integer オブジェクトは intValue メソッドを通じて int 基本型に自動的に変換されるため、不必要なパフォーマンスの消費が発生します。

したがって、高いパフォーマンス要件が求められるシナリオではアレイを優先してください。 4. 可変長配列?

配列は固定長であり、一度初期化して宣言すると、長さを変更することはできません

。これは実際の開発において非常に不便です。賢明であれば、それを達成する方法を必ず見つけることができます。 Java では多重継承を実装できないのと同様に、内部クラスとインターフェイスを使用して多重継承を実現することもできます (「Java の改善 第 (9) 章 ----- 多重継承の実装」を参照してください)。

それでは、可変長配列を実装するにはどうすればよいでしょうか? List コレクションの add メソッドで拡張アイデアを使用して、実装をシミュレートできます。 ArrayList の展開メソッドは次のとおりです。

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

このコードの便利な部分は、if ステートメントの後ろにあります。このアイデアは、元の配列を新しい配列にコピーすることであり、新しい配列の長さは元の配列の 1.5 倍になります。したがって、シミュレートされた配列拡張コードは次のとおりです:

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

この回り道を通じて、配列拡張を実現できます。したがって、プロジェクトで可変長のデータ セットが本当に必要な場合、配列は固定長であるため、考慮の範囲内になります。

5。アレイコピーの問題

」5。配列コピーの問題Collectionコピーのコピーを1つずつコピーしないと、1つずつコピーが非常に厄介なので、単に使用しましたList.toArray() メソッドを配列に変換し、それを Arrays.copyOf でコピーして、コレクションに変換するという方法は、個人的には非常に便利だと感じていますが、すでにこの問題に陥っているとは知りません。トラップ!配列要素がオブジェクトの場合、配列内のデータはオブジェクト参照であることがわかります

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

結果から、person1 の値も変更されていることがわかりました。これは典型的な浅いコピーです。問題。したがって、Arrays.copyOf() メソッドによって生成された配列は浅いコピーです。同時に、配列の clone() メソッドも同様であり、コレクションの clone() メソッドも同様であるため、copy メソッドを使用する場合は、浅いコピーの問題に注意する必要があります。

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

      渐析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 は不変の長さのリストを返します。配列の長さ、および変換されたリストの長さはどれくらいですか? 追加または削除によって長さを増減することはできません。

上記は、Java 改善章 (19)-----配列 2 の内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (www.php.cn) にご注意ください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。