浅析Java-ArrayList,源代码分析

ArrayList

这里写图片描述ArrayList是Java集合框架中的一个重要的类,也是大家经常用到的类。她是如何实现的了?
根据上图我们来分析下.

  • 她继承AbstractList类
  • 实现List<E>接口:说明她是一个可变长度的集合
  • 实现RandomAccess接口:说明她支持快速访问
  • 实现Cloneable接口:说明她可被复制
  • 实现Serializable接口:说明她可被序列化
  • 提供了增、删、改、查等相应功能
源代码分析:
transient Object[] elementData; 

通过上面的代码我们可以看出ArrayList的存储方式其实是按照数组方式存储、为类内部访问权限且是Object类型,表明我们常用的数据类型基本都可存储于ArrayList中。

private static final int DEFAULT_CAPACITY = 10;

ArrayList集合默认长度为10。

    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

ArrayList初始化数组定义,为应对不用的构造函数(如下) ArrayList提供了3种构造函数

  • public ArrayList(int initialCapacity):根据参数(长度)初始化指定长度的Object数组
    • 当长度大于0则创建指定长度Object数组
    • 长度为0则创建一个不设置长度的Object对象EMPTY_ELEMENTDATA
    • 如果长度小于0则抛出参数不合法异常IllegalArgumentException
  • public ArrayList():构建一个不设置长度(长度根据集合大小变化)Object数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  • public ArrayList(Collection<? extends E> c):根据一个集合创建Object数组
    • 将集合c转为数组
    • 将ArrayList的长度修改为集合长度
      • 如果数组类型不是Object[].class则将数组类型转换为Object[].class类型
    • ArrayList长度修改失败则初始化一个可变长度的空集合EMPTY_ELEMENTDATA
  • public void trimToSize():重新根据集合长度构造/复制数组使集合长度与数组长度一致。
    • modCount是父类AbstractList继承过来用于记录集合修改次数。
    • 重置集合长度,当集合的长度size小于存储数组(elementData)的长度(length)则重新根据集合长度构造/复制数组使集合长度与数组长度一致。
    • 博主看到这个方案还是纠结了比较久。什么时候会用到该方法?当使用该方法的时候是否意味着我们的集合将面临丢失数据的情况(答案在下方)。
  • public void ensureCapacity(int minCapacity):该方法为集合/数组扩容,数组对象不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA则原有长度为0否则为默认值10,如果当前数组长度小于准备扩容的长度则调用ensureExplicitCapacity实现扩容。
  • private void ensureCapacityInternal(int minCapacity):如果当前数组对象为DEFAULTCAPACITY_EMPTY_ELEMENTDATA则比较准备修改的长度是否小于默认值大小,调用ensureExplicitCapacity执行修改
  • private void ensureExplicitCapacity(int minCapacity):集合修改次数自增一次,调用grow实现扩容
  • private void grow(int minCapacity):扩容操作实际实现。先将数组长度修改为原长度的1.5倍(oldCapacity >> 1 将oldCapacity2进制移动1位相当于除以2)如果还是不够则修改为为参数指定长度,如果参数指定长度大于默认最大值MAX_ARRAY_SIZE,小于则使用Arrays.copyOf复制,不足的参数使用null不足,反之则调用hugeCapacity检查长度是否溢出。
  • private static int hugeCapacity(int minCapacity):检查重置长度是否合规,小于0抛出异常,移除则取默认最大值。
  • public int size():获取集合长度
  • public boolean isEmpty():验证集合是否为空
  • public boolean contains(Object o):集合是否包含指定的元素
  • public int indexOf(Object o):对象在集合中第一次出现的索引,不存在返回-1
  • public int lastIndexOf(Object o):对象在集合中最后一次出现的索引,不存在返回-1
  • public Object clone():复制一个具有相同属性的集合对象,注:返回值为全新对象,新旧对象的任何操作互不影响,复制出的对象的操作次数设置为0
  • public Object[] toArray:集合转为数组Object[]
  • public <T> T[] toArray(T[] a) 返回指定类型的数组T[]
  • E elementData(int index):返回指定位置的元素(私有)
  • public E get(int index):返回指定索引位置的元素(公有)
  • public E set(int index, E element) :重新赋值指定位置元素
  • public boolean add(E e) :添加元素到集合结尾
  • public void add(int index, E element) :向指定索引位置添加元素,后续元素索引+1
  • public E remove(int index):移除指定索引位置的元素,后续元素索引-1
  • public boolean remove(Object o):移除元素对象。注:如存在重复元素则只会移除第一个
  • private void fastRemove(int index):移除集合指定索引位置的元素(私有)
  • public void clear():将集合中所有索引的对象设置为null并把size设置为0。题外话:调用这个方法后在释放集合对象之前,集合都将占用内存空间,所以使用后最好跟着调用trimToSize()方法将集合多余内存释放(个人意见,大家有什么好的看法,请纠正)
  • public boolean addAll(Collection<? extends E> c):在集合后面追加集合
  • public boolean addAll(int index, Collection<? extends E> c):向指定索引位置添加集合列表
  • protected void removeRange(int fromIndex, int toIndex):移除指定索引范围内的所有元素
  • private void rangeCheck(int index) :验证指定位置元素是否存在,不存在则抛出异常(注:如果索引<0不会抛出异常)
  • private void rangeCheckForAdd(int index):验证指定位置元素是否存在,不存在则抛出异常(包括<0,别被名字误导,不光是验证是否可插入)
  • private String outOfBoundsMsg(int index) :拼接消息字符串
  • public boolean removeAll(Collection<?> c):根据集合移除集合中存在的所有元素
  • public boolean retainAll(Collection<?> c):根据集合移除集合中不存在的所有元素(removeAll反义)
  • private boolean batchRemove(Collection<?> c, boolean complement):根据参数执行移除
  • private void writeObject(java.io.ObjectOutputStream s):将集合写入ObjectOutputStream流中
  • private void readObject(java.io.ObjectInputStream s):将流中的对象读取出来(跟writeObject对应)这两个方法似乎有些鸡肋,均为私有方法外部无法访问且内部并未调用。
  • public ListIterator<E> listIterator(int index):返回指定索引位置开始的迭代器
  • public ListIterator<E> listIterator();返回集合所有元素的迭代器
  • public Iterator<E> iterator():使用内部类Itr返回迭代器
  • public List<E> subList(int fromIndex, int toIndex):返回fromIndex到toIndex子集合
  • static void subListRangeCheck(int fromIndex, int toIndex, int size):验证获取子集合的索引起止位置是否存在,不存在抛出异常
  • public void forEach(Consumer<? super E> action):定义Lambda表达式遍历方法forEach(Java8新特性)ArrayList间接实现了Iterable接口,该方法是Iterable的 forEach实现/重写。Iterable的forEach代码如下
    /**
     * 对每个{@code Iterable}元素执行特定的操作直到所有的元素被处理或者抛出一个异常。
     * 除非实现类另有定义,否则操作将被按照顺序迭代器的顺序执行(如果迭代器被指定)。
     * 操作抛出的异常将传递给其调用者。
     *
     * @implSpec
     * <p>默认实现类似下边这样::
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action 每个元素将要被执行的操作
     * @throws NullPointerException 如果指定的操作action是空的
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
// 使用匿名内部类实现
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(new UnaryOperator<String>(){
    @Override
    public String apply(String str){
        if(str.length()>3)
            return str.toUpperCase();
        return str;
    }
});
  • public void sort(Comparator<? super E> c):集合排序,使用了Comparator比较器来比较,实现里面的compare方法,compareTo比较之后,返回的参数是int类型,0则表示相等,1表示大于,-1负数则表示小于。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ArrayListJava中的一个类,它可以动态地存储一组元素,可以根据需要动态增加或减少元素的数量,是一种非常方便的数据结构。 ArrayList的基本用法包括创建ArrayList对象、添加元素、获取元素、修改元素、删除元素等。首先,可以使用如下代码创建一个空的ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 上面代码创建了一个类型为String的ArrayList对象,该对象初始为空。然后,可以使用add()方法向ArrayList中添加元素,例如: ```java list.add("apple"); list.add("banana"); list.add("orange"); ``` 上述代码向list中添加了三个字符串元素。可以使用get()方法获取ArrayList中的元素,例如: ```java String first = list.get(0); // 获取第一个元素 ``` 可以使用set()方法修改ArrayList中的元素,例如: ```java list.set(1, "pear"); // 将第2个元素改为"pear" ``` 可以使用remove()方法删除ArrayList中的元素,例如: ```java list.remove(2); // 删除第3个元素 ``` 以上就是ArrayList的基本用法。需要注意的是,ArrayList中的索引从0开始。 ### 回答2: ArrayListJava中非常常用的数据结构。它提供了一个可以动态添加、删除、修改的可变长度的序列。 使用ArrayList时,首先需要引入它的包:java.util。然后可以使用如下语法创建一个ArrayList对象: ```java ArrayList<String> list = new ArrayList<String>(); ``` 这里的`<String>`说明了这个ArrayList中的元素类型是String。当然,也可以使用其他类型作为元素类型。例如: ```java ArrayList<Integer> numbers = new ArrayList<Integer>(); ArrayList<Double> prices = new ArrayList<Double>(); ``` 可以使用`add()`方法来向ArrayList中添加元素: ```java list.add("apple"); list.add("orange"); list.add("banana"); ``` 可以使用`get()`方法来获取指定位置的元素: ```java String fruit = list.get(1); //获取第二个元素,即"orange" ``` 可以使用`size()`方法来获取ArrayList中元素的个数: ```java int size = list.size(); //获取ArrayList中元素的个数 ``` 可以使用`set()`方法来修改指定位置的元素: ```java list.set(1, "pear"); //将第二个元素修改为"pear" ``` 可以使用`remove()`方法来删除指定位置的元素: ```java list.remove(2); //删除第三个元素,即"banana" ``` 需要注意的是,ArrayList中的元素是有序的,且可以重复。因此,可以使用循环来遍历ArrayList中的元素: ```java for(int i=0; i<list.size(); i++){ String fruit = list.get(i); System.out.println(fruit); } ``` 或者使用增强型循环(foreach): ```java for(String fruit : list){ System.out.println(fruit); } ``` 总之,使用ArrayList可以方便地处理可变长度的序列。在实际开发中,它有着广泛的应用场景,例如处理文件或数据库中的数据,实现算法或数据结构等。 ### 回答3: ArrayListJava中一个非常常用的容器类。他的优点是可以存储任意类型的对象,可以动态扩容,因此在使用上非常的方便。 使用ArrayList需要在代码中首先调用import java.util.ArrayList进行导入,随后可以通过ArrayList<类型> name = new ArrayList<类型>()这个语句声明一个ArrayList,并将其命名为name,同时指定ArrayList中存储的对象类型为类型。当我们需要添加元素时,可以通过name.add(element)将元素添加到ArrayList中。我们也可以通过name.get(index)方法获取ArrayList中指定位置的元素,通过name.set(index,value)方法将ArrayList中某个位置的元素替换为新的元素。同时,我们也可以调用name.size()方法获取ArrayList中元素的数量。 值得注意的是,ArrayList中的元素是以索引的方式存储的,这意味着我们可以根据元素的位置进行添加、修改、删除等操作。而且,由于ArrayList的容量是可变的,因此其内部必须动态地管理数据的内存,这会影响到ArrayList的性能。当然,这个影响是很小的,不会对代码的运行产生显著的影响。 总之,ArrayListJava中非常常用的容器类,其可以存储任意类型的对象,同时调用也非常方便。但在使用时需要注意其操作的复杂度,以及不能存储基本数据类型。如果需要在ArrayList中存储基本数据类型,需要借助Boxing和Unboxing机制将其转换为对应的包装类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值