作业12:List集合类

一 为什么使用List?

1 List与数组

List 数组
可变长度 固定长度
方便的接口(支持Java8的Stream) /

2 List的实现方式

  • 数组:ArrayList
    • 查询效率比较高
    • 插入效率比较低(指的是add(int index, E element)方法,不是add(E element)方法。)
    • 特殊情况下,数量多时,空间占用比LinkedList高(当数组刚刚扩容1.5,约等于3分之1的空间没有利用。)
  • 链表:LinkedList
    • 查询效率比较低
    • 插入效率比较高
    • 数量少时,空间占用比ArrayList高
  • 空间占用计算
默认开启指针压缩:对象头占用12字节,8字节填充格式

new Integer(1) = 12 bytes 对象头 + 4 bytes int value = 16 bytes
new Integer[1] = 12 bytes 对象头 + 4 bytes 数组指针 + 4 bytes int value + 4 bytes 填充 = 24 bytes

验证数量少时,LinkedList的空间占用高(以单向链表计算)
(1)单个Node占用 = 12 bytes 对象头 + 4 bytes next 指针 +16 bytes 元素占用(以Integer为例子)= 32 bytes
new MyLinkedList() 并 添加N个元素 = N * 32 bytes Node 占用 +( 2*4 bytes first 和 last 指针占用 + 4 bytes size 字段占用(int)+ 12 bytes 对象头 ) =  32N+24 bytes

(2)new MyArrayList() 并 添加N个元素 = (12 bytes 对象头 + 4 bytes 数组指针占用 + 4*N + 填充字节?) + (4 bytes 数组指针占用 + 4 bytes size字段占用 + 12 bytes 对象头 + 4 bytes 填充 )= 20 + 4N + 填充字节? + 24 bytes

但是数组可能存在3分之1的浪费,所以随着N的增长,总有个时刻,ArrayList的占用比LinkedList高。

对象占用空间大小参考:https://blog.csdn.net/u013256816/article/details/51008443

3 自己实现一个可变数组

(1)数组方式

import java.util.Iterator;

/**
 * 因为jdk的List接口太多方法了,所以没实现。
 * @param <E>
 */
public class MyArrayList<E> {
    private int size;
    private Object[] objs;

    public MyArrayList() {
        // 从零开始:因为可能创建对象之后一直不添加元素。
        this(0);
    }

    public MyArrayList(int size) {
        this.objs = new Object[size];
        this.size = size;
    }

    private void resize(int capacity) {
        if (capacity == 0) capacity = 1;
        Object[] copy = new Object[capacity];
        for (int i = 0; i < size; i++)
            copy[i] = objs[i];
        objs = copy;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public Iterator<E> iterator() {
        return new ArrayIterator<>();
    }

    public void add(E e) {
        // 扩容方式:翻倍
        if (size == objs.length) resize(2 * objs.length);
        objs[size++] = e;
    }

    private void checkArgument(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException();
    }

    public E get(int index) {
        checkArgument(index);
        return (E) objs[index];
    }

    private void shift(int index) {
        for (int i = index; i < size - 1; i++)
            objs[i] = objs[i + 1];
        objs[--size] = null;
    }

    public E remove(int index) {
        checkArgument(index);
        E obj = (E) objs[index];
        shift(index);
        // 扩容方式:减半
        if (size > 0 && size == objs.length / 4)
            resize(objs.length / 2);
        return obj;
    }

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            str.append(objs[i].toString());
            if (i != size - 1)
                str.append(",");
        }
        return  str.append("]").toString();
    }

    private class ArrayIterator<E> implements Iterator<E> {
        private int next = 0;

        @Override
        public boolean hasNext() {
            return next < size;
        }

        @Override
        public E next() {
            if (!hasNext())
                throw new RuntimeException("List hasn't next element.");
            return (E) objs[next++];
        }
    }

    // 测试方法
    public static void main(String[] args) {
        MyArrayList<String> list = new MyArrayList<>();
        list.add("My");
        list.add("Hello");
        list.add("World");
        System.out.println(list);
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next);
        }
    }
}

(2)链表方式

public class MyLinkedList<E> {
    private Node first;
    private Node last;
    private int size;

    public MyLinkedList() {
        first = null;
        last = null;
        size = 0;
    }

    // 单向链表
    private class Node {
        public Object t;
        public Node next;

        Node(Object t) {
            this.t = t;
        }
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public Iterator<E> iterator() {
        return new LinkedListIteraotr();
    }

    private class LinkedListIteraotr implements Iterator {
        private Node current = MyLinkedList.this.first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public E next() {
            if (!hasNext()) throw new NoSuchElementException();
            E t = (E) current.t;
            current = current.next;
            return t;
        }
    }

    public void add(E e) {
        Node node = new Node(e);
        if (isEmpty()) first = node;
        else last.next = node;
        last = node;
        size++;
    }

    private void checkArgument(int index) {
        if (index < 0 || index >= size)
            throw new IndexOutOfBoundsException();
    }

    public E get(int index) {
        checkArgument(index);
        return (E) findNode(index).t;
    }

    private Node findNode(int index) {
        Node node = first;
        for (int i = index; i > 0; i--)
            node = node.next;
        return node;
    }

    public E remove(int index) {
        checkArgument(index);
        E e ;
        if (size == 1) {
            Node node = findNode(index);
            e = (E) node.t;
            first = last = null;
        } else {
            if (index == 0) {
                Node node = findNode(index);
                e = (E) node.t;
                first = node.next;
            } else if (index == size - 1) {
                Node preNode = findNode(index - 1);
                last = preNode;
                e = (E) preNode.next.t;
                preNode.next = null;
            } else {
                Node preNode = findNode(index - 1);
                Node midNode = preNode.next;
                e = (E) midNode.t;
                preNode.next = midNode.next;
            }
        }
        size--;
        return e;
    }

    @Override
    public String toString() {
        StringBuilder str = new StringBuilder("[");
        for (Node node = first; node != null; node = node.next) {
            str.append(node.t.toString());
            if (node.next != null)
                str.append(",");
        }
        return str.append("]").toString();
    }

    public static void main(String[] args) {
        MyLinkedList<Integer> list = new MyLinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
//        list.remove(0);
//        list.remove(1);
        System.out.println(list);
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

}

温馨提示:Coursera 有基础算法课程,不是计算机专业,可以通过这个网站学习一下。

4 JDK与自己实现比对

(1)数组:ArrayList

  • 扩容:jdk为1.5倍,自己实现为2倍
  • 自己实现省略了异常和检测,如:容量不能超过Integer.MAX_VALUE
  • 省略了fail-fast(其他方案:fail-safe,简单来说就是复制一份数据进行遍历,这样多线程修改也不会发生异常,但是牺牲了实时性,空间开销较大。)异常检测机制
    • 定义:java集合类的一种错误机制,看源码可知多线程状态下去删除集合中的元素,将导致ConcurrentModificationException.
    • 实现:迭代前记住修改次数(modCount),其他线程进行修改时,modCount改变,与原来的不相符,所以直接抛出异常。
    • 注意:modCount没有用volatile修饰,意味着当一个线程修改了modCount,其他的线程并不能马上感知,即不保证fail-fast机制一定会发生
    • 猜想:至于jdk为什么不采用volatile修饰,我个人认为ArrayList既然不是线程安全的类,那么就不要在多线程中使用,这样就不会发生错误。多线程相关的操作:可见性也好,原子性也好,或多或少都会影响操作性能,所以jdk没有将volatile修饰为volatile吧。

volatile的可见性参考:https://www.cnblogs.com/zhengbin/p/5654805.html

  • 其他API没实现,不做比较
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{   
   
    transient Object[] elementData;
    
    // 1.扩容和异常检测
    //调用关系:add(E) -> add(E,Object[],int) -> grow -> grow(int) -> newCapacity(int)
    public boolean add(E e) {
        modCount++;// fail-fast机制,定义在AbstractList中
        add(e, elementData, size);
        return true;
    }
    
    private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        // 扩容具体:1.5倍
        // 其他就是越界检测
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity-MAX_ARRAY_SIZE<=0)? newCapacity: hugeCapacity(minCapacity);
    }
    
    // 3.fail-fast
    // ArrayList非线程安全,不在多线程情况下使用,所以没有实现fail-fast机制。
    private class Itr implements Iterator<E> {
        // 省略了一部分方法
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        // prevent creating a synthetic constructor
        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            // 具体看这个方法就好
            checkForComodification();
            int i = cursor;
            if (i >= size) throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        
        // 调用List.remove不修改expectedModCount,即单线程下需要在Iterator.remove删除元素。
        public void remove() {
            if (lastRet < 0) throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        // 很简单的方法:比对List的当前modCount和原先modCount
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    
}

(2)链表:LinkedList

  • JDK实现了Deque接口(双向队列),所以内部的Node多了prev节点,即双向链表,我内部使用单向链表
  • 与ArrayList相同都有fail-fast机制,其他差不多,不贴代码了。
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
    
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
}

5 增强For循环的实现原理

(1)Java代码

List<Integer> list = new ArrayList<>();
list.add(new Integer(1));
for (Object i : list){ }
for (Integer i : list){ } // 类型检查并转换 Object -> Integer

(2)字节码

0: new           #2                  // class java/util/ArrayList
3: dup
4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: new           #4                  // class java/lang/Integer
12: dup
13: iconst_1
14: invokespecial #5                  // Method java/lang/Integer."<init>":(I)V
17: invokeinterface #6,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z  类型擦除,存入Object类型的元素
22: pop
23: aload_1
24: invokeinterface #7,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
29: astore_2
30: aload_2
31: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
36: ifeq          49                  // 当栈顶int型数值等于0时跳转,hasNext返回0或1,上一期作业说过,jvm字节码没有布尔类型,True -> 1,False -> 0
39: aload_2
40: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
45: astore_3
46: goto          30                  // 无条件跳转
49: aload_1
50: invokeinterface #7,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
55: astore_2
56: aload_2
57: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
62: ifeq          78
65: aload_2
66: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
71: checkcast     #4                  // class java/lang/Integer
74: astore_3
75: goto          56
78: return

(3)结论

  • 泛型存入List时,进行类型擦除,将泛型转换为Object类型。
  • 从List中取出泛型时,需要类型检查和转换,Object -> 泛型。
  • 增强for循环实现原理,其实是编译器编译为iterator循环。

(4)自己实现的List支持增强for循环吗?

// 可以支持,但是必需实现Iterable接口,不然编译器不通过
// 错误提示:for-each要求数组或者java.lang.Iterable
public class MyArrayList<E> implements Iterable<E> {
    // @Override注解,加不加无所谓,非强制
    @Override
    public Iterator<E> iterator() {
        return new ArrayIterator<>();
    }
}

(5)数组的增强for循环

  • JAVA代码
int[] ints = new int[0];
for (int i :ints){}
  • 字节码(这一段有点点复杂,冷静分析就好了,但是我不做过多的解释了,下面解释应该足够了。)
0 iconst_1
1 newarray 10 (int) // 创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
3 astore_1
4 aload_1
5 astore_2   //ints
6 aload_2
7 arraylength       // 获得数组的长度值并压入栈顶
8 istore_3
9 iconst_0          // 获取常数0
10 istore 4         // 栈顶的值赋值给本地变量,0赋值给int
12 iload 4
14 iload_3
15 if_icmpge 30 (+15)  // 比较栈顶两int型数值大小,当结果大于等于0时跳转
18 aload_2   // 将第三个引用类型本地变量推送至栈顶,指的是ints
19 iload 4   // 将指定的int型本地变量推送至栈顶,指的是i
21 iaload
22 istore 5  // 将栈顶int型数值存入指定本地变量,指的是tmp
24 iinc 4 by 1 //将指定int型变量增加指定1
27 goto 12 (-15)
30 return
  • 翻译字节码为java代码
int[] ints = new int[0];
int length = ints.length;
for (int i = 0; i - length < 0; i++){
    int tmp = ints[i];
}
  • newarray 10 // 其中10代表类型

参考:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.newarray

Array Type atype
T_BOOLEAN 4
T_CHAR 5
T_FLOAT 6
T_DOUBLE 7
T_BYTE 8
T_SHORT 9
T_INT 10
T_LONG 11

6 数组与List转换

  • 数组 => List:Arrays.asList(T[] arr)
  • List => 数组:list.toArray(T[] arr)
Integer[] integers = new Integer[2];
integers[0] = 1;
integers[1] = Integer.valueOf(2);
List<Integer> list = Arrays.asList(integers);
Object[] objects = list.toArray();
// 尽量不用,大部分jdk版本都运行时报错,只有少部分支持(1.8.0_171不报错)
// Integer[] newInteger1 = (Integer[]) objects; 
Integer[] newInteger2 = list.toArray(new Integer[list.size()]);
System.out.println(Arrays.toString(newInteger2)); // [1, 2]

二 线程安全的List

1 Vector

  • 基本所有方法都用了synchronized,即用来对象锁,同一时刻只能有一个线程访问该对象。
public synchronized void addElement(E obj) {
    modCount++;
    add(obj, elementData, elementCount);
}

public synchronized boolean removeElement(Object obj) {
    modCount++;
    int i = indexOf(obj);
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}
......

2 Collections.synchronizedList(ArrayList)

  • RandomAccess:空接口,只用于标识能够快速随机访问。
// 快速随机访问重要表现:下标访问遍历 runs faster than 迭代器遍历:
for (int i=0, n=list.size(); i < n; i++)
   list.get(i);

for (Iterator i=list.iterator(); i.hasNext(); )
   i.next();
  • 根据是否实现RandomAccess接口,创建不同的线程安全集合
 public static <T> List<T> synchronizedList(List<T> list) {
     return (list instanceof RandomAccess ?
             new SynchronizedRandomAccessList<>(list) :
             new SynchronizedList<>(list));
 }

// SynchronizedRandomAccessList继承了SynchronizedList
static class SynchronizedRandomAccessList<E> extends SynchronizedList<E> implements RandomAccess {

    SynchronizedRandomAccessList(List<E> list) {
        super(list);
    }

    // ......
}

// SynchronizedList继承了SynchronizedCollection
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
     private static final long serialVersionUID = -7754090372962971524L;
     final List<E> list;

    SynchronizedList(List<E> list) {
         super(list);
         this.list = list;
     }
    
     // ......
}

// 很明显对象锁,与Vector差不多,就是提供多一点的Api而已
// 唯一不同就是mutex对象锁,可以外部指定,非必定为this对象
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 3053995032091335093L;

    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize

    SynchronizedCollection(Collection<E> c) {
        this.c = Objects.requireNonNull(c);
        mutex = this;
    }

    SynchronizedCollection(Collection<E> c, Object mutex) {
        this.c = Objects.requireNonNull(c);
        this.mutex = Objects.requireNonNull(mutex);
    }

    public boolean add(E e) {
        synchronized (mutex) {return c.add(e);}
    }
    
    public boolean remove(Object o) {
        synchronized (mutex) {return c.remove(o);}
    }
    
    // ......
}

3 CopyOnWriteArrayList

  • 适用于读多写少,复制需要消耗一定的性能
  • 缺失了一定实时性,写过程中,其他线程读取到的都是旧值
// java10
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

    /**
     * The lock protecting all mutators.  (We have a mild preference
     * for builtin monitors over ReentrantLock when either will do.)
     */
    // java8不是Object,而是ReentrantLock
    final transient Object lock = new Object();
    
    private transient volatile Object[] array;
    
    // 读取操作不加锁
    public E get(int index) {
        return elementAt(getArray(), index);
    }
    
    @SuppressWarnings("unchecked")
    static <E> E elementAt(Object[] a, int index) {
        return (E) a[index];
    }
    
    // 写操作加对象锁
    public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            // CopyOnWrite的由来:由于读不加锁,我们希望在写过程中,读取多少次数据都得到相同结果,所以不对原来的数组进行修改,而是复制一份出来。
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements); // array使用volatile修饰具有可见性,可以让其他读取线程马上知道
            return true;
        }
    }
    
    public E remove(int index) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            E oldValue = elementAt(elements, index);
            int numMoved = len - index - 1;
            if (numMoved == 0)
                setArray(Arrays.copyOf(elements, len - 1));
            else {
                Object[] newElements = new Object[len - 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index + 1, newElements, index,
                                 numMoved);
                setArray(newElements);
            }
            return oldValue;
        }
    }
    
    // ......
}
  • 重点介绍下复制方法
// Arrays类
@SuppressWarnings("unchecked")
public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

// Arrays类
@HotSpotIntrinsicCandidate
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    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;
}

// Array类
public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException {
    return newArray(componentType, length);
}

// Array类
@HotSpotIntrinsicCandidate
private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException;

// System类
@HotSpotIntrinsicCandidate
public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

// 稍微展示一下JVM中的TypeArray的copy_array
// 在/hotspot/src/share/vm/oops/typeArrayKlass.cpp
void TypeArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d, int dst_pos, int length, TRAPS) { 
  // ......

  // 前面都是异常检查,上下界检查,忽略即可,重要的是下面  
    
  // This is an attempt to make the copy_array fast.
  int l2es = log2_element_size();
  int ihs = array_header_in_bytes() / wordSize;
  char* src = (char*) ((oop*)s + ihs) + ((size_t)src_pos << l2es);
  char* dst = (char*) ((oop*)d + ihs) + ((size_t)dst_pos << l2es);
  Copy::conjoint_memory_atomic(src, dst, (size_t)length << l2es);
}

void Copy::conjoint_memory_atomic(void* from, void* to, size_t size) {
  address src = (address) from;
  address dst = (address) to;
  uintptr_t bits = (uintptr_t) src | (uintptr_t) dst | (uintptr_t) size;

  if (bits % sizeof(jlong) == 0) {
    Copy::conjoint_jlongs_atomic((jlong*) src, (jlong*) dst, size / sizeof(jlong));
  } else if (bits % sizeof(jint) == 0) {
    Copy::conjoint_jints_atomic((jint*) src, (jint*) dst, size / sizeof(jint));
  } else if (bits % sizeof(jshort) == 0) {
    Copy::conjoint_jshorts_atomic((jshort*) src, (jshort*) dst, size / sizeof(jshort));
  } else {
    // Not aligned, so no need to be atomic.
    Copy::conjoint_jbytes((void*) src, (void*) dst, size);
  }
}

// /hotspot/src/share/vm/utilities/copy.hpp
static void conjoint_jbytes(void* from, void* to, size_t count) {
    pd_conjoint_bytes(from, to, count);
}

// 最后发现该方法与平台相关
static void pd_conjoint_bytes(void* from, void* to, size_t count) {
#ifdef AMD64
  (void)memmove(to, from, count);
#else
  // Includes a zero-count check.
  intx temp;
  __asm__ volatile("        testl   %6,%6          ;"
                   "        jz      13f            ;"
                   "        leal    -1(%4,%6),%3   ;"
                   //...中间是一大段汇编代码,大家应该不感兴趣,省略
                   "        jbe     1f             ;"
                   "        addl    $3,%1          ;"
                   "11:     rep;    smovb          ;"
                   "12:     cld                    ;"
                   "13:     nop                    ;"
                   : "=S" (from), "=D" (to), "=c" (count), "=r" (temp)
                   : "0"  (from), "1"  (to), "2"  (count), "3"  (temp)
                   : "memory", "flags", "%edx");
#endif // AMD64
}

// Class类:数组类型的Class才有返回值,返回数组存放的类型
public Class<?> getComponentType() {
    // Only return for array types. Storage may be reused for Class for instance types.
    if (isArray()) {
        return componentType;
    } else {
        return null;
    }
}

// 简单测试一下

// Test1:API的基本使用
int[] src = {1,2,3,4,5,6,7,8,9,10};
int[] des = new int[5];
System.arraycopy(src,0,des,0,des.length);
System.out.println(Arrays.toString(des)); // [1, 2, 3, 4, 5]

// Test2:getComponentType方法
System.out.println(int[].class.getComponentType()); //int
System.out.println(Integer[].class.getComponentType()); //class java.lang.Integer

// Test3
 System.out.println((Object) Object[].class == (Object) Object[].class);// true
 System.out.println((Object) Object[].class == (Object) Integer[].class);// false

// Test4:Array.newInstance的作用:创建泛型数组创建并且强转不报错
Integer[] objs = (Integer[]) Array.newInstance(Integer[].class.getComponentType(), 3);
//  Integer[] objs = (Integer[]) new Object[3]; // 这里会报错,所以对于不是Object数组,不嫩那个使用new Object[length]的方式创建
objs[0] = Integer.valueOf(1);
objs[1] = Integer.valueOf(2);
objs[2] = Integer.valueOf(3);
System.out.println(Arrays.toString(objs));//[1, 2, 3]

猜你喜欢

转载自www.cnblogs.com/linzhanfly/p/9571180.html