我似乎在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
(Object[]
中保留了所有元素)的内部查看它将报告的内容10
。因此,您添加了一个元素--您得到了9个未使用的额外插槽。
另一方面,如果您这样做:
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 = {};
我们将它与EMPTY_ELEMENTDATA区分开来,以了解添加第一个元素时要膨胀多少
这是有道理的,但为什么一个膨胀到10
(比我要求的多得多),而另一个膨胀到1
(和我要求的完全一样)。
即使您使用list
,并不断添加元素,最终还是会出现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
但它的增长速度小于默认构造html" target="_blank">函数的情况。
这让我想起了hashmap
实现,其中的桶数几乎总是比您要求的多;但这是因为需要“两个”桶的功率,而不是这里的情况。
所以问题来了--有人能给我解释一下这个区别吗?
即使是在实现方式不同的旧版本中,您也可以精确地得到您所要求的内容,所指定的内容也是不同的:
构造初始容量为10的空列表。
构造具有指定初始容量的空列表。
即使将初始容量指定为零时也适用。
Java ;8增加了一个优化,即ten elements数组的创建被推迟到添加第一个元素之后。这是针对arraylist
实例(使用默认容量创建)在很长一段时间内甚至整个生存期内保持为空的常见情况。此外,当第一个实际操作是addall
时,它可能会跳过第一个数组大小调整操作。这不会影响具有显式初始容量的列表,因为这些列表通常是谨慎选择的。
正如本答复所述:
我正在尝试编写一个Python实用程序,将Oracle的RAW字节字符串(作为字符串)转换为Guid,反之亦然。我试图重用我在C#中构建的同一个实用程序中的算法,但是从同一个字节数组构造一个和一个会产生不同的Guid/UUID。它们是一样的,不是吗?我读过UUID只是一个更好的术语。 在C#中,我有一个字节数组,< code>byte_array如下所示: 在 Python 中,我有一个字节数组,
问题内容: 在C ++中,从构造函数内部调用虚拟函数时,它的行为不像虚拟函数。 我认为第一次遇到这种行为的每个人都会感到惊讶,但第二次认为这是有道理的: 只要派生的构造函数没有被执行的对象是 不是 又一个 衍生 实例。 那么如何调用派生函数呢?前提条件还没有建立的机会。例: Java和.NET完全相同,但是他们选择了另一种方式,这可能是 产生最少惊讶原则 的唯一原因吗? 您认为哪个是正确的选择?
我注意到我正在开发的应用程序中有一个奇怪的bug。在修改一个类时,我将一组属性移动到构造函数中进行自动连接,而不是使用字段注入,然而,由于循环依赖关系,这导致我在启动时出错。下面是导致错误的依赖关系的分解: 我正在尝试在TargetClass的内部使用ServiceA 服务A通过其构造函数注入了服务B 通过现场注入,服务B已注入服务C 通过现场注入,ServiceC已注入TargetClass 我
允许您执行以下操作: 但不是这个: 可能是因为返回类型不是函数签名的一部分。但是是一个类类型,它被赋予一个返回类型,并且知道构造它的函数对象的返回类型。所以这里可能有编译器错误。 为什么没有编译器错误?
我有一个抽象的超类,它有一个形式的构造函数 并希望创建该抽象类的一个子类,该子类不是以字符串作为其第一个参数,而是采用一个表示给定字符串名称的整数值,例如,0代表某个字符串,1代表另一个字符串,依此类推。 当我尝试编写窗体子类(int number,int amount)的构造函数时,我得到一个格式为“Implicit super constructor is undefined.必须显式调用另一
问题内容: 为什么不能和这两个一起在构造函数中使用? 合并这样的东西的原因是什么? 问题答案: 将在同一类中调用另一个构造函数,而将调用超级构造函数。如果构造函数中没有,则编译器将隐式添加一个。 因此,如果两者都允许,您最终可能会两次调用构造函数。 示例(不要在参数中寻找含义): 现在,如果调用,将调用以下构造函数: 更新 : 如果您能够使用并且可能会得到如下结果: ( 注意 :这是为了说明 如果