为什么数组需要在定义时设置维度

本文关键字:设置 定义 数组 为什么 | 更新日期: 2023-09-27 17:53:40

我在想,为什么我们不能这样定义呢:

int[] arr = new int[];
arr[0] = 1;

像列表一样,自动调整大小。我想知道的是为什么这是不可能的,我们需要以以下方式设置每次的大小:

int[] arr = new int[1];
arr[0] = 1;

为什么数组需要在定义时设置维度

List<T>的调整大小是基于在需要的时候在幕后创建一个新的数组。

想想这里的底层实现是什么样子的。当您分配一个数组时,它保留了一块内存,并且引用有效地直接指向该内存。如果你需要存储比你保留的更多的值,你需要分配更多的内存,在其他地方…但是你不能改变引用来引用新的内存,因为这些引用是分散在各处的。(数组不知道是什么在引用它)

解决这个问题的明显方法是有一个间接级别——这样初始引用就指向一个对象,该对象跟踪实际数据的存储位置,以便它可以在需要时重新分配。这正是List<T>所做的……但这确实意味着存在额外的间接层面。这在效率上是有代价的,部分原因是你可能在List对象本身的内存中有很长的实际数据,这不利于缓存……仅仅通过额外的间接层次本身就有成本。

基本上,如果你想要一个动态大小的集合,使用List<T> -这就是它的作用。如果您从一开始就知道最终大小,并希望从数组的"更接近金属"方面获益,请使用它们。

数组是一个相对较低层次的概念——如果你想要一个高层次的抽象,使用一个…

由于数组在内存中是连续的,因此在创建数组时必须为其内容分配足够的内存。

假设你有一个包含100个元素的数组。现在你又增加了1个,你必须在第100个条目之后申请内存地址。如果这个地址已经被使用了怎么办?

这就是为什么你不能动态调整数组的大小。

我的猜测是'因为它是这样指定的' -这给了数组特定的性能特征和优化能力,这是非常理想的。虽然这些优点会被设计者注意到,并且会影响使用类似结构的语言(C, c++, Java -脚注1),但他们真的可以定义它来做任何的事情。例如,

List<T>必须调整内部数组的大小,通常会分配比需要的更多的内存(除非您恰好使用了与分配的一样多的内存)以及创建垃圾。

c#语言规范的

1.8有以下规定:

数组是一种包含许多变量的数据结构通过计算索引访问。中包含的变量数组,也称为数组的元素,都是相同的类型,该类型称为数组的元素类型。

数组类型是引用类型,是数组变量的声明简单地为数组实例的引用留出空间。实际数组实例在运行时使用new方法动态创建操作符。 new操作指定新数组的长度实例,然后在实例的生命周期内将其固定。数组元素的下标取值范围为0到Length - 1。的New操作符自动将数组元素初始化为它们的默认值,例如,对于所有数字类型都是零所有引用类型都为null。

此外,没有人提到数组边界检查被认为是"软件工程原则"-正如ECMA 334规范中描述其设计目标时所述:

语言及其实现应该提供以下支持软件工程原理,如强类型检查,数组边界检查,检测尝试使用未初始化的变量;以及自动垃圾收集。软件健壮性、耐用性和程序员的生产力很重要。

脚注1 - c#语言规范的介绍:

c#(发音为"See Sharp")是一种简单、现代、面向对象的语言类型安全的编程语言。c#起源于C家族语言和将立即熟悉C, c++和Java程序员。

这是因为数组分配内存,而框架需要知道有多少。

如果你想要一个动态大小的'数组',使用List

列表实际上包装了一个数组。当在当前数组中添加更多的项目时,创建一个新数组(通常是双倍大小),旧的项目被移动到新数组中。删除则相反。

因为数组的大小在定义上是静态的,而不是动态的。

如果您想要一个动态大小的集合,还有其他可用的集合,比如您提到的列表。

你可以做的是延迟数组大小的声明:

int[] numbers;
numbers = new int[10];  
numbers = new int[20];