学校为什么要在List上教数组?[关闭]


36

我学校中大部分的初始编程课程作业都要求我使用数组。我现在全职工作,而且从未将数组用于我从事的任何项目。即使在现有的项目中,我也从未见过在任何地方使用数组。我认为,List更易于使用,是一个标准。教授为什么要告诉学生在作业中使用数组?只是为了让学生了解基础知识?

由于大多数大学都教授Java,因此这个问题是Java特有的。


7
数组更基础。
user253751'3

评论不作进一步讨论;此对话已转移至聊天
世界工程师

Answers:


120

因为数组教授诸如索引和边界之类的概念,所以这是计算机编程中两个根本重要的概念。

列表不是 “标准”。数组非常适合各种问题空间。


评论不作进一步讨论;此对话已转移至聊天
世界工程师

48

他们可能想从最准确地表示计算机工作方式的数据结构入手,因此在他们开始引入更简单的列表之类的更高级抽象之前,您应该熟悉基础知识。否则,您将无法理解为什么某些结构或算法对于某些操作而不是其他某些操作速度慢/快。如果计算机内存实际上是由链表制成的,那么世界将是一个截然不同的地方。

出于同样的原因,我在大学的第一门编程课是C语言,其余的课程全部是C ++,Java和其他语言。并不是说C会变得更好或更简单,而是C(和数组)对您没有任何隐藏。


7
列表更容易在实际代码中使用,但是数组更易于(完全)理解,并且更重要,因为数组是基本的和通用的,而数组则不是(Java的)列表。至少那是我的看法。
Ixrec 2015年

21
将资料存储在数组中并在以后访问它的分配是数据结构分配。您可能还没有意识到。您必须问自己,是什么因素会造就更好的工程师,了解基本的计算模型或学习语言的标准库?普遍的共识(我同意,第一点)是,如果不先了解计算的基础知识,就不会了解很多库的内容。
肯特A.15年

12
LinkedList,ArrayList,OrderedList等。您应该首先学习哪个列表?如果您不了解为什么它们首先存在,您将如何欣赏它们为您所做的工作?
肯特

10
+1到@KentAnderson。学校必须培养对底层数据结构了解的工程师;理想情况下,生产工程师可以轻松地用C实现基本结构。其他任何程序都只是JavaSchool。
克里斯蒂安·威尔曼,2015年

2
“他们可能想从最准确地表示计算机工作方式的数据结构开始” –那么,具有列表结构或对象结构内存的计算机又如何呢?“并不是C会变得更好或更简单,而是C(和数组)对您没有任何隐藏。” –除非您的计算机没有指针(例如AS / 400),否则C本质上在VM中运行,并且几乎为您隐藏了所有东西
约尔格W¯¯米塔格

21

上面的答案很棒,但是我有另一个想法。Java的main()方法意味着学生很早就遇到基础数组,通常是在上课的第一天。为什么?

public static void main(String[] args)

编写《 Hello World及更高版本》是您要做的第一件事。(我看过一些课程首先使用了诸如BlueJ这样的教学IDE,它允许您点击并运行任意方法,但我们将其搁置一旁...)尽管值得一提的是,这些关键字有一段时间了,大多数老师迟早会想对它们进行解释。实际上,一个经典的初学者水平的测试问题是要求学生在基本的Hello World程序中给出每个关键字的含义。作为主要方法签名的一部分,我们能找到什么?数组(其原因部分是历史原因。Java1.0中不存在ArrayList)。数组是该基础知识集的一部分。清单不是。

就是说,类在稍后的课程中引入ArrayList的情况并不少见,特别是在对象和它们的用途被覆盖之后。即使是Java的AP计算机科学课程,也包括ArrayList(我知道它曾经用过,Google似乎表明它仍然这样做),尽管它忽略了ArrayList实现List和其余Collections Framework的事实。

最后,根据我的经验,大学的CS程序使用Java作为探索CS和编程概念的方法,而不是教学生如何成为优秀的Java开发人员。有些程序可能更着重于培养专业的开发人员,而另一些程序则更着重于理论,但无论哪种情况,都有很多关于如何在实际的专业工作中使用Java的知识,这在大多数大学课程中都不会讲到。范围从有效Java中的设计模式和技术到Spring,Hibernate或JUnit等框架,甚至更常见的JSP或JDBC。考虑到这一原理,在更常用的ArrayList上强调数组会更有意义。


3
@Deduplicator确定。它与我关于数组的观点无关,因此我省略了它。我也没有包含System.out.println(“ H​​ello World!”); 要么。
扎克·利普顿

+1代表概念,而有效技术则代表“现实世界”。也没有人在教版本控制,至少在我上学的时候也没教过-但那时我很古老:)
David David

我参加了一个操作系统课程,在该课程中,我们必须实现各种程序,例如调度程序。我们一次(排他地)专注于一件这样的作品,因为那时我们无法做更多的事情。但是,试图让所有的工作件的思维,同时是什么给了我(的开始)的“真实世界”的发展/“在大型编程”的复杂性表示赞赏。
大卫

14

第一年编程班使用数组的一个原因是传统的:这是教授们在我们开始使用带有动态列表的标准库之前最初学到的方法。使用原始数据类型也更普遍:数组几乎存在于任何计算机中阳光下的语言(可以在少数汇编说明中实现)。当我第一次学习编程时,实现链接列表是其中的一项任务。

从第一原理开始,然后说“这是基本结构。这种语言(或其库)为您提供了可以执行所有操作的高级数据结构,但是却为您提供了x,y和z,”就是说:“这就是这些高级数据结构,现在就在这里。” 学习确定是否使用LinkedList与ArrayList(或HashSet与TreeSet)通常是第二年或第三年的算法课程。列表和映射具有相同的接口,并提供相同的结果,但是在任何大小的应用程序中,它们的行为都可能截然不同。一旦您退出了编程101,就无法保证编程102将使用相同的语言。如果您从数组的概念入手,您可以说“

在入门课程中,更喜欢“数组”而不是“列表”的另一个原因是数组从根本上很容易理解:bytes20个数组占用20个字节(加上一对来表示数组的结尾或长度,具体取决于实现方式) )。

“列表”是完全不同的鱼,可以通过许多不同的方式(ArrayList,LinkedList,以及我可能忘记的几个方式)来实现,并且具有根本不同的性能特征。如果不了解List类的不同之处,就无法对何时应使用List foo = new ArrayList()vs 进行有意义的讨论List foo = new LinkedList()。如果您尝试让学生使用List实现,那么有人会问您为什么使用ArrayList而不是其他实现之一。而且“ ArrayList”包含单词“ Array”,并由一个单词支持,因此从“ array”到“ ArrayList”的逻辑跳跃并不是很大。

与流行的看法相反,在某些情况下,在列表上使用数组是有意义的,尤其是在处理静态大小的列表时。这是一对:

  • 查找和迭代的速度稍快,因为你不处理开销的方法调用:foo[n]取消引用,并做一些幕后的指针运算,而foo.get(n)有取消引用,做一个方法调用,做了第二次提领,然后也许做指针运算(如果您使用的是ArrayList,则LinkedLists可能需要遍历List的每个元素)。
  • 初始化更加干净:int[] foo = new int[]{1, 2, 3, 4, 5}另一个StackOverflow问题中的建议相比

数组大打败数组的另一种情况是元素可用的序列与将要知道其最终位置的序列匹配,但不匹配最终序列。如果perm是具有int[256]排列的a,则可以很容易地用一个数组将其取反:int[] inv = new int[256]; for (int i=0; i<256; i++) inv[perm[i]]=i; 我认为用a可以写的任何东西ArrayList<>都不会那么干净。
超级猫

@supercat动态数组并不是真正的问题,只是特定的实现。ArrayList可以很容易地得到一个构造函数,该构造函数创建一个特定大小的默认初始化列表。
Doval 2015年

您是否有消息来源支持数组列表必须处理方法调用开销的说法?理论上是这样,但在实践中我想如果感到惊讶get,并set没有得到联。
Doval 2015年

@Doval:设计合理的语言/框架没有理由不应该提供可以方便地按顺序添加项目的动态数组类型,但是Java不提供这种类型。
超级猫

@Doval:即使get并且set可以内联,类似的myList.set(23,myList.get(23)+1)效率也远不及myArray[23]+=1。此外,即使对于不需要装箱的类型,如果任何JITter都能将等同for (i=0; i<1000; i++) myList2.set(i+2000,myList2.get(i+3000));于其性能的任何东西都令我感到惊讶,那么System.arrayCopy(myArray1,3000,myArray2,2000,1000); 框架的动态数组类型没有理由不应该提供快速的复制范围操作,但是Java没有。
超级猫

7

Java允许将任何类型的变量存储到数组中。相反,ArrayList仅允许存储引用。因此,在ArrayList不首先讨论自动装箱如何将原语转换为引用类型以及自动拆箱有时将参考类型转换为原语的情况下,可能没有用的讨论:

for (int i=10; i<=10000; i*=10)
{
    ArrayList<Integer> l = new ArrayList<Integer>();
    l.add(i);
    l.add(i);
    l.add(l.get(0));
    System.out.print("i=" + i);
    System.out.print(" #0==i:" + (l.get(0)==i));
    System.out.print(" #1==i:" + (l.get(1)==i));
    System.out.print(" #2==i:" + (l.get(2)==i));
    System.out.print(" #0=#1:" + (l.get(0)==l.get(1)));
    System.out.print(" #1=#2:" + (l.get(1)==l.get(2)));
    System.out.println(" #0=#2:" + (l.get(0)==l.get(2)));
}

如果代码使用int[3]而不是ArrayList,就不会感到惊讶。所有这三个元素将相互比较iArrayList但是,即使使用list的所有三个元素总是比较等于i,并且第一个和第三个元素始终总是比较相等,使用,即使前者在1、10 i或100 时也只会彼此相等,但是(在大多数实现中)不是i1000或10000。


您能解释一下为什么#0和#1在1或10或100时相等吗?我一直跟踪到那一点。

2
@MatthewRead:Integer该类具有嵌套的类,该类IntegerCache包含Integer用于一系列通常扩展为-128..127的值的预初始化对象;对超出该范围的值进行装箱将需要创建一个新的对象,但是对在缓存范围内的值进行装箱将仅返回对存储在中的预初始化对象之一的引用IntegerCache.cache。任何优秀的Java程序员都需要意识到,Integer封装相同值的两种类型的变量可能比较或不相等,但是过早引入该思想可能会使学生惊恐万状。
超级猫

never,永远都不会猜到这是由于预先初始化的对象引起的。谢谢(你的)信息!

1
@MatthewRead:我希望Java禁止使用进行某些隐式转换==。考虑到自动拆箱的可能性,我倾向于完全禁止它使用,但是它的行为==尤其可怕。该==运营商还表现在严重的情况下一样16777217==16777216f[报告真],而对于隐式转换long v=Math.Round(123456789), w=Math.Round(123456789012345)都容易出现意想不到的[你能猜出这些表达式将产生?]
supercat

至于IntegerCache,有时候它可以帮助提高性能,但是它经常会导致代码出现类错误。在某些方面,我希望有一种装箱方式,该装箱方式将准随机地从两个整数缓存中返回对象,并用新的准随机方式替换缓存项,以便依赖于值-128..127的装箱行为的代码将是不可能长期成功。
超级猫

6

由于ArrayList内部使用数组,因此我认为先教数组的用法是有意义的。的ArrayList类有称为成员变量elementData这是一个Object数组。

从JDK ArrayList 源代码

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer.
 */
private transient Object[] elementData;

当您从中添加,更新,检索或删除元素时,ArrayList它会使用此内部数组执行这些操作。正如用户Ixrec所指出的那样- ArrayList只是一个更高级别的抽象,通常更易于使用。


但是,数组在内部使用指向内存块的指针。为什么从特定的抽象级别开始?

问题被标记为Java-这是在问为什么通常在您可以使用时教导数组ArrayList。Java没有指针。对与错,我认为C / C ++是比Java更适合入门的语言。通过了解C / C ++,可以更好地理解编程中的许多主题。
Derek W

3

如您所说,假设该列表确实更易于使用-没关系。学习更多是关于“从基础到复杂”而不是“从容易到困难”。如果基础知识不重要,那么计算机科学就不会成为学术领域。您可以从在线教程中了解如何使用现有框架/库将应用程序一起单击。(当然,有人必须编写这些库...,并且ArrayList首先必须实现...。)


在许多情况下,ArrayList可以使用单独的数组和“ liveItems”计数来更好地处理使用情况,除了没有一种简便的方法可以使用该数组并一起计数(除非通过汇总)。
超级猫

2

这是因为在学术教育中最关键的事情是教您使用正确的术语来描述您所做的事情。

列表是那个数组以外的东西。而且您不能java.util.List在Java中使用它,因为它是一个接口。通常使用java.util.ArrayList哪个是List实现,而不是列表,而是动态数组周围的对象包装。因此,您说使用“列表”,但使用数组。

跳过术语混乱并仅使用数组来学习学生什么是数组是非常合理的。如果在Java中使用数组,则至少要使用数组。

坦白地说,这也是为什么使用Java进行编程教学不是一个好主意的观点。很难正确学习编程的基本概念。


哈希图呢?阵列仅仅是一个类型散列映射的,在某种程度上..
Thufir

@Thufir数组如何成为哈希图?这些是完全不同的数据结构。
Danubian Sailor'Mar

您可以用哈希图表示数组。哪个更“基本”?我认为哈希图在概念上更有趣。
Thufir 2015年

1
@Thufir不,你不能。您只能效仿。内部每个数据结构都是它的样子。这些在根本上和概念上都是不同的。这就是为什么开始使用Java或JavaScript这样的高度抽象的语言学习编程的坏主意的原因。目前尚不清楚数据结构到底是什么。
Danubian Sailor

1

您实际上根本没有看过或使用过任何数组吗?除了列表,我们一直都在使用它们。我们通常不使用Java,但是我们使用许多其他具有明显相似性的语言。

在数组和列表之间,一个人的重量更轻,而且指向更多点,而另一个人的功能更多。作为编程的一般规则,当基本上有两种相似的类型沿这些线划分时,应该选择重量较轻的类型,除非您实际上需要更高级的一种。除了减少开销外,这实际上还有助于处理程序,尤其是其代码中的混乱情况和状态。如果在测试过程中出现问题,则可以减少寻找的地方。更重要的是,在使用数组与列表的情况下,人们可以更好地了解您实际使用它并尝试使用它的范围有限。

是的,从学术的角度来看,还有其他向学生教授基础知识的原因。但是,这会更深一些。数组和列表是体积较大的类型的一个很好的例子,体积较大的类型通常只是建立在轻量级类型的基础实例之上,并且通常只是包裹在基础实例的周围。即使List没有底层数组,它们也会像它们一样向外表现。教别人什么是列表的一部分就是教他们什么是数组。

我可以看到,这在像C ++这样的语言中可能会失控,在这种语言中,数组的精简基本上不能比其精简得多,但是在高级语言中,它们几乎是自己列出的。如果它们在给定的情况下完全适合您的需求,为什么还要使用其他东西?


我不会说列表具有“更多”功能,而是具有“不同”功能。一个新的T[]将立即准备就绪,可以按任何顺序接受项目,而则ArrayList<T>需要按顺序添加项目,然后才能对其进行修改。A T[]可以T[]相对较快地ArrayList<T>将任意范围的项目复制到另一个大小相似的范围内,而这又需要从一个列表中逐个读取项目并将它们存储到另一个列表中-这要慢得多,也不太方便。
超级猫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.