【collection】集合学习——List 之 ArrayList

前言

jdk 版本 jdk1.8.0_161

集合实现:

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

 

InterfacesHash table ImplementationsResizable array ImplementationsTree ImplementationsLinked list ImplementationsHash table + Linked list Implementations
SetHashSet TreeSet LinkedHashSet
List ArrayList LinkedList 
Queue     
Deque ArrayDeque LinkedList 
MapHashMap 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):添加在尾部,时间复杂度为 O(1)

    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;

在指定索引处增加元素:涉及到索引,需要移动后续元素,相对费时。时间复杂度 O(n)

    /**
     * 在list 的指定位置插入指定元素
     * 将当前在该位置的元素(如果有的话)和任何后续元素向右移动
     *
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index); // 检查角标是否合法

        ensureCapacityInternal(size + 1);  //容量确认,是否需要扩容
      // 调用本地方法:完成数组中数据后移
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element; //插入新元素
        size++; //元素数量 + 1
    }

 

删除某个元素:因为元素是可重复,所以只移除第一次出现的元素,涉及到元素移动,相对耗时,时间复杂度 O(n)

    /**
     * 移除指定元素在 list 中第一次出现的元素
     * 即移除 指定元素的 最小索引元素
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * 跳过边界检查 并且不返回被移除的值
     * 
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1; // 需要移动的数据数量
        if (numMoved > 0)
     // 将移除元素之后的所有元素向左移动
           System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //垃圾回收 
    }

删除除指定角标的元素:相对于移除指定元素,省去了元素查找,仍然需要移动元素,时间复杂度 O(n)

    /**
     * 移除 list 中指定位置的元素
     * 左移后续元素
     */
    public E remove(int index) {
        rangeCheck(index);//检验角标

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;// 被移动元素的个数
        if (numMoved > 0)
// 元素移动
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // 垃圾回收

        return oldValue;
    }

修改指定位置的元素:时间复杂度  O(1)

    /**
     *使用 指定元素 替换指定位置的元素
     */
    public E set(int index, E element) {
        rangeCheck(index);//角标检查

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

查询指定位置的元素:时间复杂度 O(1)

    public E get(int index) {
        rangeCheck(index); //角标检查

        return elementData(index);
    }

 

函数式接口相关方法

 

因为函数式接口的出现,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;
    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值