[英]Why trimToSize/ensureCapacity methods provide 'public' level access?
[英]List Interface and ArrayList Class: ensureCapacity() and trimToSize() methods
我正在创建两个Arraylist
,然后我想应用ensureCapacity()
和trimToSize()
,但是
ArrayList<String> arrayList = new ArrayList<>();
arrayList.trimToSize();
arrayList.ensureCapacity(100);
List<String> list = new ArrayList<>();
list.trimToSize();
list.ensureCapacity(100);
当我从 Class 创建Arraylist
时,我可以这样做。
当我使用List
接口创建Arraylist
时,我不能。
你能告诉我为什么会这样吗?
我查看了 Javadoc,发现只有ArrayList
具有ensureCapacity()
和trimToSize()
方法。
但是为什么当我从List
接口创建ArrayList
时不能在我的 ArrayList 上使用它们呢?
trimToSize()
和ensureCapacity()
方法都只在ArrayList
类中可用。
List
接口中没有这样的方法。
原因是:这些方法旨在更改底层数组的大小。 trimToSize()
创建一个长度等于列表大小的新数组,并且ensureCapacity()
将创建一个给定长度的新底层数组。
这些方法对没有数组支持的LinkedList
没有意义,因此它们没有在List
接口中声明。
还值得指出的是,应该谨慎使用trimToSize()
,因为您可以创建一个循环或连续增长和修剪,而ArrayList
的性能会非常糟糕。
考虑一下:大约50%
的元素已从您的ArrayList
中删除,您可以使用trimToSize()
释放这个未占用的堆空间。 将创建一个小两倍的新底层数组,并将所有现有元素复制到其中。 但是随着下一个添加项ArrayList
将再次变大两倍(这意味着应该在内存中分配一个新数组,并且应该将所有元素复制到其中)。 听起来性能不是很好。
一般来说, trimToSize()
可能值得了解,因为它是 JDK 的一部分,并且因为有人可能会问你这个问题,例如在面试中。 但你永远不会使用它。
但是为什么当我从
List
接口创建ArrayList
时不能在我的 ArrayList 上使用它们呢?
当变量是List
类型时,您只能使用在 list 接口中声明的方法。 引用的类型定义了您如何与对象进行交互。 引用本身不是对象。 而且它无论如何都不会改变对象,你的ArrayList
仍然是一个ArrayList
。 引用类型List
定义了与对象(仍然是ArrayList
)交互的方式。
为了访问未在List
接口中定义的方法,您可以强制转换它:
List<String> list = new ArrayList<>();
ArrayList<String> list2 = (ArrayList) list;
list2.trimToSize();
list2.ensureCapacity(100);
但请注意,这种转换是不安全的,只有当变量list
指向ArrayList
而不是LinkedList
时,它才会成功。 正如我之前所说,使用这些方法是可以的,但将来你几乎不需要它们。
我认为 Java 缺少一些重要的东西。 你说:
当我从类中创建
ArrayList
时,我可以这样做。当我使用
List
接口创建ArrayList
时,我不能。
这是不正确的。 在这两种情况下,您都在使用new ArrayList<>()
创建一个ArrayList
。 这两个对象的创建方式完全相同。
两个版本之间的区别在于创建ArrayList
对象之后会发生什么。
ArrayList<String> arrayList = new ArrayList<>();
将新对象分配给类型为ArrayList<String>
的变量,但是
List<String> list = new ArrayList<>();
将新对象分配给类型为List<String>
的变量。
不同之处在于各个变量的类型......而不是对象的类型。
对于arrayList
,编译器知道变量引用的对象将是ArrayList
或ArrayList
的子类型。 因此它知道该对象将支持ArrayList
和/或其超类定义的所有方法。 这包括ArrayList.trimToSize()
。
对于list
,编译器只知道变量中的对象是某个实现List
接口的类。 所以它只知道它在List
API 中有方法。 编译器不知道对象实际上是ArrayList
还是LinkedList
还是CopyOnWriteList
或任何其他实现List
的类。 所以不在List
API 中的方法不能应用到它。
因此编译错误。
但是为什么当我从 List 接口创建
ArrayList
时不能在我的 ArrayList 上使用它们呢?
(你没有......见上文。)
因为您将ArrayList
对象分配给List
变量,所以您对编译器说:
“忘记这是一个
ArrayList
。把它想象成一个List
。”
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.