Java中的List集合无法初始化size?size和容量的区别是什么?

Java中的List集合无法初始化容量


我们都知道Java的List集合是有进行一个懒加载的优化的,当你没有在集合里面存放元素的时候,集合的size为0

可我记得List集合是可以指定大小的,于是我写了一段代码去进行测试

我这个时候就非常疑惑,我不是指定了大小吗?为什么size还是0?

想不明白,我就开始翻源码看看

原来size是记录的集合里面的元素个数,而不是集合的容量大小

  • 先看空参构造器

    从这段代码我们可以看得出懒加载机制,当没有往集合中放任何东西 的时候,就会初始化一个{}

java 复制代码
		private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
		transient Object[] elementData;
		public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 再看看add()源码
java 复制代码
		public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 添加到集合之前先把判断一下当前容量size + 1是否会超出容量大小
        elementData[size++] = e;
        return true;
    }

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

		private static final int DEFAULT_CAPACITY = 10;

		private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          	// 这里开始判断了,如果说你是第一次初始化,即集合为{}的话,我就会给你分配 DEFAULT_CAPACITY = 10
          	// 需要注意的是这里并没有把 size的值设置为10,size只记录集合里面有多少元素,而不是集合容量
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity; // 如果说你不是第一次添加了,就返回当前的容量 + 1的值
        // 这个地方有点绕,需要看传参 add方法里面的 ensureCapacityInternal(size + 1) 这个size + 1 就是minCapacity
    }

		private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // 这里的modCount先不用管,因为ArrayList线程不安全,当modCount和expectModCount不一致就会抛异常

        // 当这个minCapacity大于数组长度的时候,也就意味着数组放不下了,这个时候就需要扩容了
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

		private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 从源码我们可以看出,ArrayList集合底层的扩容机制就是通过数组的复制
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  • 说点题外话,ArrayList集合是如何扩容的,拆分一下grow源码
java 复制代码
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

这里可以看到newCapacity会把你原来集合的大小加上你原来集合大小的一半来进行扩容,就是1.5倍扩容,这是第一次

第二次扩容是什么时候呢?

java 复制代码
 if (newCapacity - minCapacity < 0)
     newCapacity = minCapacity;

当第一次扩容结束后,发现还是放不下,就会按照你给的集合大小来扩容了

当发现扩容后的大小超出了 MAX_ARRAY_SIZE 就会把整数的最大值给你了 Integer.MAX_VALUE

java 复制代码
		private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

 		if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
     
		private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

总结


经过这次奇怪的尝试,终于搞清楚了size到底是什么了

我如果想要初始化List集合,就不能初始化集合容量 ,而是初始化集合的size

也就是说,如果想要真正的初始化这个size,就必须要往集合里面添加元素才能改变这个size属性

相关推荐
小小菜鸡ing14 分钟前
pymysql
java·服务器·数据库
getapi16 分钟前
shareId 的产生与传递链路
java
桦说编程26 分钟前
爆赞!完全认同!《软件设计的哲学》这本书深得我心
后端
thinktik37 分钟前
还在手把手教AI写代码么? 让你的AWS Kiro AI IDE直接读飞书需求文档给你打工吧!
后端·serverless·aws
我没想到原来他们都是一堆坏人1 小时前
(未完待续...)如何编写一个用于构建python web项目镜像的dockerfile文件
java·前端·python
沙二原住民1 小时前
提升数据库性能的秘密武器:深入解析慢查询、连接池与Druid监控
java·数据库·oracle
Jerry&Grj2 小时前
SpringBoot埋点功能技术实现方案深度解析:架构设计、性能优化与扩展性实践
java·微服务·性能优化·springboot·架构设计·埋点技术
没有bug.的程序员2 小时前
Redis Stream:轻量级消息队列深度解析
java·数据库·chrome·redis·消息队列
用户8160791833332 小时前
告别“魔法”:包你解决 Gradle 的下载慢问题
java