【java】集合学习——ArrayList

前言

jdk 版本 jdk1.8.0_161

集合实现:

通用实现:为日常使用而设计的

Interfaces Hash table Implementations Resizable array Implementations Tree Implementations Linked list Implementations Hash table + Linked list Implementations
Set HashSet   TreeSet   LinkedHashSet
List   ArrayList   LinkedList  
Queue          
Deque   ArrayDeque   LinkedList  
Map HashMap   TreeMap   LinkedHashMap


概览

相关类接口 UML 图(idea 生成)

        

说明:

Iterable : 始于 1.5版本,实现该接口的类 可以使用 for-each 循环语句完成遍历。持有 迭代器对象 Iterator;

Collection:  基类;

AbstractCollection:  Collection 接口的一个骨架实现,目的是减少后续实现类实现抽象方法的数量;

List: 有序集合(也被称为一个序列(sequence)),使用该接口可以精确的控制列表中每个元素的插入位置,可以通过索引快速访问、查询元素。list 允许重复元素,允许多个null 元素;

RandomAccess:标记接口(marker interface),用来表示 List 实现类 支持 快速随机访问。这个接口的目的是当 随机 或者 顺序 获取 list 时,允许 泛型算法 来改变 他们的行为 以 提供 好的性能;

AbstractList:List 接口的一个骨架实现;对于需要 随机存取的  数据类型需要继承该类,比如 ArrayList 的数组,该抽象类对 hashcode 方法 和 equals 方法 进行了重写

Cloneable:实现该接口 表示 ,对象支持  克隆,否则会抛出异常;

Serializable:实现该接口的类表示 开启 类的序列化;

ArrayList :

List 接口 可变数组的实现,和 Vector基本相同, 区别在于 ArrayList 是 非同步的,Vector 是 同步的 (线程安全的);

特点:有序的(元素的插入顺序有序);可以保存 null 值;允许重复值;

size,isEmpty,get,set,iterator,listIterator 操作花费是 常量时间;

add 操作 以 摊还常量时间( amortized constant time) 运行,即 添加 n个元素的时间花费 为 O(n);

其他操作是线性时间运行(粗略来说),常量因子比 LinkedList 的低;

每个 ArrayList 实例 有一个 capacity, capacity 是 存储 元素的数组的大小。默认为 10,当元素 被添加到 ArrayList 中,他的 capacity 会自动增长。

该实现类是非同步的,如果在多线程下使用,可以使用 Collections.synchronizedList() 静态方法实现。

通过 Iterator 类和 ListIterator 方法 返回的  iterators 是 快速失败(fail-fast):如果 在 iterator 被创建后,list 结构上被修改,这种修改不是通过 iterator 自己的 remove 和 add 方法实现的,那么 iterator 会抛出 一个 ConcurrentModificationException;如下示例将抛出异常:

    @Test
    public void failFastTest() {
        List<String> names = this.createList();
        for (Iterator iterator = names.iterator();iterator.hasNext();) {
//            iterator.next();
//            iterator.remove();
            names.remove(iterator.next());
        }
//        LOGGER.debug("names size is {}", names.size()); // 0
    }

    private List<String> createList() {
        List<String> names = new ArrayList<>();
        names.add("小明");
        names.add("小强");
        names.add("小红");
        names.add("小张");
        return names;
    }


ArrayList 源码 解析


构造函数:

 private static final int DEFAULT_CAPACITY = 10;

private static final Object[] EMPTY_ELEMENTDATA = {};

  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

transient Object[] elementData; 


   /**
     * 构造一个空 list,当需要添加元素时,初始化容量,最小为10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 通过给定的初始容量值构造一个 空 
 list
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }


    /**
     * 构造一个包含指定 collection 中元素 的 list, 顺序为 collection 对应 迭代器返回的顺序
     *
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

扩容机制:向容器中添加元素时会进行容量确认

添加单个元素(添加多个元素 为 size + numNew):

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //处理内部容量,size 为 容器中存储的元素个数,此时总元素个数为 size 加 1
        elementData[size++] = e;//先在容器的 size 处添加元素,然后 size 加 1
        return true;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    // 计算容量,是否为默认容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// == 比较地址,是否为默认容量
            return Math.max(DEFAULT_CAPACITY, minCapacity);//取默认容量 和 传入 容量值中最大的一个
        }
        return minCapacity;
    }


    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//AbstractList 中定义的字段,记录 list 结构上修改的次数,给迭代器使用

        // 超出 数组长度,扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }


    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//原始容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容后的容量,左移一位除以2,扩容 1.5倍
        if (newCapacity - minCapacity < 0)//扩容后的容量仍然不能满足需求
            newCapacity = minCapacity;//直接使用要求的容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)//扩容后的容量超出数组最大界定容量
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//构造新数组
    }


    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        //三目运算符,请求容量 大于数组最大可分配容量的话,返回 整型最大值,
       //否则返回数组的可分配最大容量
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }


    /**
     * 数组可以分配的最大容量
     * 一些 虚拟机会在数组中保留 一些 头信息
     *如果尝试分配更大容量,则会导致 
     * OutOfMemoryError:请求数组大小超出 VM 限制
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

因为函数式接口的出现,jdk 1.8 新增了不少 方法:

forEach 方法:可以直接遍历 集合;重写 Iterable 接口中的默认方法;使用 Consumer 函数式接口

Iterable 接口中的默认方法:

    default void forEach(Consumer<? super T> action) { // Consumer 接收一个参数,不返回任何值函数式接口
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t); //调用 Consumer 中的方法
        }
    }

ArrayList 类中重写的方法:

    @Override
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) { //最原始的循环方法
            action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

spliterator 方法:Spliterator 是一个用于 遍历 和 分隔 源数据 的函数式接口 ;

    @Override
    public Spliterator<E> spliterator() {
        return new ArrayListSpliterator<>(this, 0, -1, 0);
    }

removeIf 方法:移除符合条件的 元素;使用 Predicate 函数式接口

@Override
    public boolean removeIf(Predicate<? super E> filter) { // Predicate 断言一个 参数值 的 函数式接口,返回 true or  false
        Objects.requireNonNull(filter);
        // figure out which elements are to be removed
        // any exception thrown from the filter predicate at this stage
        // will leave the collection unmodified
        int removeCount = 0;
        final BitSet removeSet = new BitSet(size);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {
                removeSet.set(i);
                removeCount++;
            }
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }

        // shift surviving elements left over the spaces left by removed elements
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {
            final int newSize = size - removeCount;
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            for (int k=newSize; k < size; k++) {
                elementData[k] = null;  // Let gc do its work
            }
            this.size = newSize;
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }

        return anyToRemove;
    }

replaceAll 方法: 替换指定的元素;使用 UnaryOperator 函数式接口

    @Override
    @SuppressWarnings("unchecked")
    public void replaceAll(UnaryOperator<E> operator) {// UnaryOperator 对单个数的操作,返回和操作数类型相同的结果
        Objects.requireNonNull(operator);
        final int expectedModCount = modCount;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            elementData[i] = operator.apply((E) elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

sort 方法: 排序;使用 Comparator 函数式接口,

    @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

代码展示:

    @Test
    public void methodTest() {
        List<String> fruits = this.createFruit();
        //forEach方法 的 lambda
        fruits.forEach(fruit -> {
            System.out.println(fruit);
        });
        //forEach方法 的方法引用
        fruits.forEach(System.out::println);


        // removeIf 方法
        fruits.removeIf(s -> s.equals("orange"));

        // replaceAll 方法
        fruits.replaceAll(s -> {
            if ("apple".equals(s)) {
                return "watermelon";
            }
            return s;
        });
        // 避免 lambda 体的 写法
        fruits.replaceAll(this::unaryOperator);
        System.out.println(fruits);
    }

    private String unaryOperator(String s) {
        if ("apple".equals(s)) {
            return "watermelon";
        }
        return s;
    }

    public List<String> createFruit() {
        List<String> fruits = new ArrayList<>();
        fruits.add("apple");
        fruits.add("orange");
        return fruits;
    }


猜你喜欢

转载自blog.csdn.net/lihuapiao/article/details/79917015