目前app的代码中不少使用Lists.newArrayListWithExpectedSize(), 针对此方法的使用是否规范做了一些分析
1. 关于 Maps.newHashMapWithExpectedSize()
看到Lists.newArrayListWithExpectedSize()此方法, 想必你定会想到HashMap的Maps.newHashMapWithExpectedSize()方法,
我们知道HashMap的默认加载因子是0.75, HashMap在put新数据时, 会计算当前元素个数是否超过当前容量的0.75倍, 若达到则扩容。
在初始化HashMap时, 若使用者已知道具体的元素个数, 则初始化的HashMap的容量应该是
初始容量 = 元素个数/0.75 + 1
Maps.newHashMapWithExpectedSize()方法的存在就是帮助使用者做了计算这一步
我们来看下源码:
static int capacity(int expectedSize) {
if (expectedSize < 3) {
checkNonnegative(expectedSize, "expectedSize");
return expectedSize + 1;
}
if (expectedSize < Ints.MAX_POWER_OF_TWO) {
// This is the calculation used in JDK8 to resize when a putAll
// happens; it seems to be the most conservative calculation we
// can make. 0.75 is the default load factor.
return (int) ((float) expectedSize / 0.75F + 1.0F);
}
return Integer.MAX_VALUE; // any large value
}
2. ArrayList的扩容机制
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//minCapacity为add后的元素个数
if (minCapacity - elementData.length > 0) {
//扩容操作
grow(minCapacity);
}
}
通过源码可见ArrayList的扩容机制与HashMap并不一样, ArrayList的在执行add方法时, 只有新增元素后的总长度大于容量时才进行扩容, 否则不扩容
具体扩容步骤如下
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);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
通过上述源码可以看到, ArrayList进行扩容时, 先进性一次扩容, 扩容后的容量大小为
oldCapacity + oldCapacity >>1
即原容量的1.5倍, 若当前元素个数(minCapacity) 还大于扩容后容量, 则取minCapacity为最新容量大小, 然后进行数据copy
3. Lists.newArrayListWithExpectedSize()的实现
static int computeArrayListCapacity(int arraySize) {
checkNonnegative(arraySize, "arraySize");
return Ints.saturatedCast(5L + arraySize + (arraySize / 10));
}
源码可以看出, newArrayListWithExpectedSize()方法对ArrayList的初始化容量的计算是
最终容量 = 5+预期值+ 预期值/10
若你要使用一个长度为100的集合, 使用该方法会初始化一个容量为 5+100+10=115的集合
看一下此方法的注释:
This method will soon be deprecated. Even in the rare case
* that you do want some amount of padding, it's best if you choose your
* desired amount explicitly.
这种方法很快就会被弃用。即使在罕见的情况下
*你需要一些填充物,最好选择
*明确要求的数量。
注释中说明 构建ArrayList时最好直接传入元素, 或者明确你要的容器数量
对参数的解释为:
an estimate of the eventual {@link List#size()}
对新列表的最终长度的估计值
也就是说, 当前方法是使用者不确定集合元素个数时, 但是有个预估值时去使用。
4. 目前代码中使用是否规范
目前代码中不少地方创建ArrayList是为了将一个实体类集合成为一个DTO集合的转换。也就是说是为了创建一个明确元素数量的ArrayList, 使用Lists.newArrayListWithExpectedSize() 则初始化的集合容量至少多出5, 造成了不必要的内存浪费
例如:
private List<ActivityInfoBo> getActiveBoList(List<Integer> activityIdList) {
List<ActivityInfoBo> activeList = Lists.newArrayListWithExpectedSize(activityIdList.size());
for (Integer activityId : activityIdList) {
ActivityInfoBo activityInfoBo = appConfig.getBrandWeekActivityBean()
.getActivityInfoByActivityId(activityId);
activeList.add(activityInfoBo);
}
return activeList;
}
当明确集合元素个数时, 初始化ArrayList可以使用ArrayList自带的构造函数
new ArrayList<>(n) n为集合长度值
或者使用Lists.newArrayListWithCapacity()方法, 此方法也是直接使用了ArrayList的构造
public static <E> ArrayList<E> newArrayListWithCapacity(int initialArraySize) {
checkNonnegative(initialArraySize, "initialArraySize"); // for GWT.
return new ArrayList<E>(initialArraySize);
}