java-集合-ArrayList

  • ArrayList 
  1. 基于数组实现,是一个动态数组,扩容机制请看下文
  2. 非线程安全
  3. 实现了Serializable接口,支持序列化,能够通过序列化传输,
  4. 实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问
  5. 实现了Cloneable接口,能被克隆。
  • ArrayList 扩容机制(基于JDK1.8.0_92)
  1. 如果未定义初始大小,则容量为0,第一次扩容时直接扩容为10,之后每次扩容增加当前容量的一半容量;
  2. 如果定义初始大小,之后每次扩容增加当前容量的一半容量;
public static void main(String[] args) {
        List list = new ArrayList();
        System.out.println(ListDemo.getArrayListCapacity(list));//容量为0

        for(int i = 1;i<=1;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//添加1个元素后,总元素1,容量扩容为10

        for(int i = 1;i<=10;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加10个元素后,总元素11,容量扩容为15

        for(int i = 1;i<=5;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加5个元素后,总元素16,容量扩容为22

        for(int i = 1;i<=7;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加7个元素后,总元素23,容量扩容为33
    }

输出结果:
0
10
15
22
33

结论:由输出结果可知,如果未定义初始大小,则容量为0,第一次扩容时直接扩容为10,之后每次扩容增加当前容量的一半容量;


public static void main(String[] args) {
        List list = new ArrayList(1);
        System.out.println(ListDemo.getArrayListCapacity(list));//容量为1

        for(int i = 1;i<=2;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//添加2个元素后,总元素2,容量扩容为2

        for(int i = 1;i<=1;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加1个元素后,总元素3,容量扩容为3

        for(int i = 1;i<=1;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加1个元素后,总元素4,容量扩容为4

        for(int i = 1;i<=1;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加1个元素后,总元素5,容量扩容为6

        for(int i = 1;i<=2;i++){
            list.add(i);
        }
        System.out.println(ListDemo.getArrayListCapacity(list));//再添加2个元素后,总元素7,容量扩容为9
    }

输出结果:
1
2
3
4
6
9

结论:由输出结果可知,如果定义初始大小,之后每次扩容增加当前容量的一半容量;

// 获取ArrayList容量
public static int getArrayListCapacity(List arrayList) {
    try {
        Field field = ArrayList.class.getDeclaredField("elementData");
        field.setAccessible(true);
        Object[] objects = (Object[])field.get(arrayList);
        return objects.length;
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
        return -1;
    } catch (IllegalAccessException e) {
        e.printStackTrace();
        return -1;
    }
}
  • ArrayList 实现了Serializable接口,为什么它的elementData属性用 transient修饰?
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
  1. 用transient修饰的属性不会被序列化
  2. ArrayList在序列化的时候会调用它的writeObject()方法,这个方法是根据其size将elementData中的元素写进ObjectOutputStream,;反序列化时调用readObject()方法,从ObjectInputStream获取size和element,再恢复到elementData。
  3. 原因是elementData是一个缓存数组,它通常会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就可以保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。
  • ArrayList 添加、删除元素
  1. ArrayList 每次add元素时都会调用 ensureCapacityInternal(size + 1); 检查数组容量是否够用,如果不够用就进行扩容
  2. ArrayList 扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。
  3. ArrayList 新增、删除元素时会进行元素的移位
  4. ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法(Arrays.copyof()内部会创建一个新的数组,然后调用System.arraycopy()进行数组复制,System.arraycopy()是一个native方法,由C语言实现)
  • ​​​​​​​常用方法
// 数组修身,即将list长度修为其size大小的长度
// 原理 利用Arrays.copyOf(elementData, size);又copy到一个新的数组中
trimToSize()

// 转为静态数组toArray
// 原理 利用Arrays.copyOf(elementData, size);又copy到一个新的数组中
toArray()
  • ArrayList 实现线程安全的方法
// 1、使用Collections类中提供的静态工厂方法创建的同步容器类
// 注:此方法并非绝对同步,还会存在线程安全问题
List list = Collections.synchronizedList(new ArrayList());
  • ArrayList 和 Vector
  1. 都采用数组方式存储数据
  2. vector 是线程安全的,会影响性能
  3. 如果不考虑到线程的安全因素,ArrayList 效率比较高。
  4. Vector 默认容量为10,默认扩容为当前容量的1倍;也可以根据构造方法指定容量和扩容倍数
  • 写一段代码在遍历 ArrayList 时移除一个元素
//使用迭代器
Iterator it= list.iterator();
while(it.hasNext()) {
      if(...) {
        it.remove();
      }
}

//使用lanbda表达式
list.removeIf(integer -> integer == 2);

列子:
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(6);
        for(int i = 1;i<=6;i++){
            list.add(i);
        }
        list.forEach(integer -> {
            System.out.println(integer);
        });
        System.out.println("删除元素后:");
        list.removeIf(integer -> integer == 2);
        list.forEach(integer -> {
            System.out.println(integer);
        });
    }

输出结果:
1
2
3
4
5
6
删除元素后:
1
3
4
5
6

 

  • 简单集合关系图

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值