List对象在每次执行add方法,自动增大数组大小,为什么都扩大到原来大小的1.5倍?

ecaol 2007-11-02 04:12:24
ArrayList类的ensureCapacity方法如下:
(此方法被ArrayList的Add方法调用)

public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, size);
}
}

这行代码的意义是什么?
int newCapacity = (oldCapacity * 3)/2 + 1;


为什么扩大到原来大小的1.5倍+1?
为什么不直接+1呢,难道考虑到定义List的人一般都要扩大2次以上吗?
...全文
688 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
johnsoncr 2007-11-16
  • 打赏
  • 举报
回复
to 5楼:

inputStream 是面向字节流的,那为什么它的read()返回 0 到 255 范围内的 int 值。而不直接返回byte型。
----------------------------------------------------------------------------------------------
“回楼上的
read()有个约定,就是达到流的结尾时返回-1
如果返回类型是byte,那么-1究竟是代表结束了呢?还是byte -1呢?

注:如果返回int,那么如果达到结尾就返回-1,如果byte -1,那么会返回 255”

=============================================================================================
为什么byte -1,会返回255,byte不也可以有负数值吗?
rainv 2007-11-05
  • 打赏
  • 举报
回复
还没到研究性能的阶段.
ls的精彩.
zhb_821012 2007-11-05
  • 打赏
  • 举报
回复
很不错
分析的很透彻
学习了
175cm 2007-11-05
  • 打赏
  • 举报
回复
精彩。。。
liuzi123 2007-11-05
  • 打赏
  • 举报
回复
关注,ls的解释的很精彩
timbear 2007-11-05
  • 打赏
  • 举报
回复
帮顶,学习中!
jeffsunboy 2007-11-04
  • 打赏
  • 举报
回复
在大型的开发中应该就能体现优势了,一般扩充速度是75%,add多了优势就很明显了,1000,10000。

计算机去申请内存还是要耗费很多精力的。
Dan1980 2007-11-04
  • 打赏
  • 举报
回复
大家知道,ArrayList在扩充的时候既有额外的时间开销,又有额外的空间开销。

额外的时间开销来自于,申请新的内存,拷贝数组。
额外的空间开销来自于,申请的空间多于需要的空间。

从时间效率上分析,应该尽量减少扩充的次数。所以,一次扩充的越多越好。
而从空间效率上分析,应该尽量“按需扩充”,需要多少个元素就扩充多少个位置,一次扩充一个当然是最省空间的。

可以看出,这两个效率因素是互相矛盾的,不能片面地追求任何一个。
并且实际上,时间效率总是优先于空间效率而被考虑的,所以,一次扩充一个是不可能的。
而一次扩充50%,应该是兼顾各个因素的一个结果。就像楼上有人说的,这可能确有统计数字支持。

随着容器的不断扩充,它本身的体积在不断增大,显然,如果按它的某个倍数来扩充,这个扩充速度会越来越快。而每次扩充0.5倍,而不是一倍,又在某种程度上限制了这种扩充速度。

Vector就是一次扩充一倍。既然ArrayList是Vector的“改进版本”,那么,就一定有它的优势。
如果你没有更好的做法,那么就相信java的开发团队吧。

by the way,那段英文注释是段废话,没有解释这个问题。
alphax 2007-11-04
  • 打赏
  • 举报
回复
其实大家在上面几贴都说得很清楚了,楼住自己在思考思考把,

不要太过于抓住某个细节不放,虽说list是基本数据类型,也是常用数据类型,他的实现是经过很多人的
思考,但是,就算是今天的开放源码的库实现,也并不是说每一段程序,甚至每一行代码都是精华来的,很多
都是很简单很普通的思路,具体实现时甚至有点随意性的,就像这个

newCapacity = (oldCapacity * 3)/2 + 1;

也可以写成
newCapacity = (oldcapacity+1) * 2

你也许会说,这样扩容岂不是太快?如果只执行小量的add,岂不是很浪费空间?是的,但是,在需要大量add
操作但又不能明确知道具体需求容量的时候,第二个实现就明显提高性能,因为他减少了分配和复制数组的次数。
关键是,当你认为capacity是性能的瓶颈时,你可以直接设置目标capacity,而不是依赖它自动扩容,这样可以
避免若干次不必要的扩容操作
sharpyuce 2007-11-03
  • 打赏
  • 举报
回复


/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity.
*/
public void ensureCapacity(int minCapacity) {
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = (E[])new Object[newCapacity];
System.arraycopy(oldData, 0, elementData, 0, size);
}
}
the minimum capacity argument:(最小容量参数)
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this(10);
}
最小容量参数 是指this(10)中10,也就是指:
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list.
* @exception IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = (E[])new Object[initialCapacity];
}
也就是指public ArrayList(int initialCapacity)中的int initialCapacity
rorey_008 2007-11-02
  • 打赏
  • 举报
回复
说白了,这就是个实现的机制,
你也可以一次就扩大2倍,你也可以一次只加一,但是你想想,每次容量的扩大都会有额外的开销,频繁的扩大必然影响性能

这就是算法里面讲到的,具体什么算法,楼主查一下吧
ecaol 2007-11-02
  • 打赏
  • 举报
回复
上面的 :
the minimum capacity argument

capacity是根据什么得出来的呢?
ecaol 2007-11-02
  • 打赏
  • 举报
回复
Increases the capacity of this <tt>ArrayList</tt> instance, if
necessary, to ensure that it can hold at least the number of elements
specified by the minimum capacity argument.
ecaol 2007-11-02
  • 打赏
  • 举报
回复
我英语不好啊,哪位老大把注释的精华部分翻译一下,给大家分享,多谢了。
ecaol 2007-11-02
  • 打赏
  • 举报
回复
如果 oldCapacity = 1,
则(oldCapacity * 3) / 2 = 1,
所以要加1,
=====================

(oldCapacity * 3) / 2 = 1 然后 +1 不正好是2 了吗?
正好达到了List.size()=1扩大一个变成2,和1+1=2没啥区别了,
我是想知道为啥一次要多扩大不少,一楼能否自己讲一下“所以要加1”的理由,谢谢。



--------------
楼上几位朋友分析的很有道理,但是我感觉有 add 1000次的机会,
为什么就没有只add 1次的机会呢?平时用到的List我感觉add多数是用循环加的,
但是次数如果按照1000次的说法,那么数据动辄几万条,也有几万条的可能啊,
那为什么只增加到1.5倍呢?要效率的话,何不多加一些,虽然倍数增加是几何级的,
但是仍然感觉对于1000条数据显得慢了一些,我就是想不太明白,
然后欢迎各路高手前来讨论哈,我先谢了。
alphax 2007-11-02
  • 打赏
  • 举报
回复
>1楼的回答没有解决“为什么不直接+1”的问题!
我也没看清楚,很简单拉,内存分配是很耗时的操作,不可能一次+1,如果add 1000次,岂不是要分配内存1000次,并且还要copy? 至于为什么是1。5倍,可能是有统计数据支持的,不过,如果预先知道要add很多次,可以预先设定一个大的capacity
imA 2007-11-02
  • 打赏
  • 举报
回复
但是又不能每次过大的扩展ArrayList内存空间,以避免造成浪费。


其实这些信息在ArrayList的注释信息就解释的很清楚了。

楼主可以仔细看看注释信息,不要光看代,呵呵。
「已注销」 2007-11-02
  • 打赏
  • 举报
回复
直接+1 效率太低
比如1开始初始化长度10
达到10之后,每次插入都要分配一次空间


其实也有把这个比例改为75%,或者干脆容量*2的
imA 2007-11-02
  • 打赏
  • 举报
回复
向ArrayList中添加大量元素的时候,使用ensureCapacity可以有效的减少分配内存操作的开销。
johnsoncr 2007-11-02
  • 打赏
  • 举报
回复
谢谢5楼的回答。终于想通了。嘿嘿。
(抢了LZ的问题,莫怪)
加载更多回复(6)

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧