我似乎在实现中偶然发现了一些ArrayList
我无法理解的有趣的东西。这是一些代码,说明了我的意思:
public class Sandbox {
private static final VarHandle VAR_HANDLE_ARRAY_LIST;
static {
try {
Lookup lookupArrayList = MethodHandles.privateLookupIn(ArrayList.class, MethodHandles.lookup());
VAR_HANDLE_ARRAY_LIST = lookupArrayList.findVarHandle(ArrayList.class, "elementData", Object[].class);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
public static void main(String[] args) {
List<String> defaultConstructorList = new ArrayList<>();
defaultConstructorList.add("one");
Object[] elementData = (Object[]) VAR_HANDLE_ARRAY_LIST.get(defaultConstructorList);
System.out.println(elementData.length);
List<String> zeroConstructorList = new ArrayList<>(0);
zeroConstructorList.add("one");
elementData = (Object[]) VAR_HANDLE_ARRAY_LIST.get(zeroConstructorList);
System.out.println(elementData.length);
}
}
这个想法是,如果你创建一个ArrayList
这样的:
List<String> defaultConstructorList = new ArrayList<>();
defaultConstructorList.add("one");
看看它会报告什么elementData
(所有元素都保存在哪里) 。因此,您添加了一个元素 - 您将获得 9 个未使用的额外插槽。Object[]
10
另一方面,如果您这样做:
List<String> zeroConstructorList = new ArrayList<>(0);
zeroConstructorList.add("one");
您添加一个元素,保留的空间仅用于该元素,仅此而已。
在内部,这是通过两个字段实现的:
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
当你创建一个ArrayList
via new ArrayList(0)
-EMPTY_ELEMENTDATA
时将被使用。
当你创建一个ArrayList
通过new Arraylist()
-DEFAULTCAPACITY_EMPTY_ELEMENTDATA
使用。
我内心最直观的部分——简单地尖叫“删除DEFAULTCAPACITY_EMPTY_ELEMENTDATA
”,让所有的案件都处理EMPTY_ELEMENTDATA
;当然是代码注释:
我们将此与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时要膨胀多少
确实有道理,但是为什么一个膨胀到10
(比我要求的要多得多)而另一个膨胀到1
(完全符合我的要求)。
即使您使用List<String> zeroConstructorList = new ArrayList<>(0)
并不断添加元素,最终您也会达到elementData
比请求更大的点:
List<String> zeroConstructorList = new ArrayList<>(0);
zeroConstructorList.add("one");
zeroConstructorList.add("two");
zeroConstructorList.add("three");
zeroConstructorList.add("four");
zeroConstructorList.add("five"); // elementData will report 6, though there are 5 elements only
但是它的增长速度小于默认构造函数的情况。
这让我想起了HashMap
实现,桶的数量几乎总是比你要求的多;但是这样做是因为需要“两个的力量”桶,但这里的情况并非如此。
所以问题是 - 有人可以向我解释这种差异吗?