在java.util.List上,get(0)始终是O(1)吗?

huangapple go评论78阅读模式
英文:

Is get(0) on java.util.List always O(1)?

问题

根据我的知识,以下是已实现的内容:

  • ArrayList
  • LinkedList
  • Vector
  • Stack

(基于 http://tutorials.jenkov.com/java-collections/list.html 如果有错误请纠正)

ArrayList 是动态数组实现,因此像数组一样,获取操作为 O(1);LinkedList 从头部获取为 O(1);Vector 和 Stack 基于 ArrayList,因此也为 O(1)。

所以,在任何内置实现(因为你可以自己创建,针对特定目的进行 get(0) 操作的 O(n!) 实现)的列表中,对于 get(0) 操作都是 O(1) 吗?

英文:

Of my knowledge, there are the following implementations:

  • ArrayList
  • LinkedList
  • Vector
  • Stack

(based on http://tutorials.jenkov.com/java-collections/list.html pls correct if wrong)

ArrayList is a dynamic array implementation, so, as array, get is O(1), LinkedList has O(1) for get from Head, Vector and Stack are based on ArrayList, hence, O(1).

So in EVERY case get(0) on any built-in (cause you can make your own, for a specific purpose on making get(0) TS of O(n!)) implementation of List if O(1)?

答案1

得分: 3

> 在 java.util.List 上调用 get(0) 是否总是 O(1)?

假设存在一个参数 N 代表列表的长度<sup>1</sup>。

对于你提到的那四种 List 实现,get(0) 确实是一个 O(1) 的操作:

  • ArrayListVectorStack 都是通过数组下标来实现 get(i),这是一个 O(1) 的操作。

  • LinkedList.get(i) 需要进行 i 次链表遍历,所以是 O(i)。但如果 i 是一个常数,那么复杂度就降低为 O(1)

然而,还有其他“内置的”List 实现。事实上,如果考虑各种非公开的实现,比如实现子列表、不可修改列表等,那么实现的数量相当可观。从那四种实现泛化到“所有的实现”是不准确的<sup>2</sup>。


但是,并不是所有可能的 List 实现中 get(0) 都是 O(1)

  • 考虑一个简单的链表,其中元素按相反的顺序链接。因为 get(0) 需要遍历到链表的末尾,需要 N 次链表遍历,复杂度为 O(N)

  • 考虑一个列表,第一次尝试检索列表元素时,它从数据库查询结果集的行中完全填充。第一次 get 调用至少是 O(N),因为你要获取 N 行。(如果数据库查询不是 O(N),那么情况可能更糟。)因此,对于任何 get 调用的最坏情况复杂度都是 O(N)……或者更糟。

实际上,通过一些巧妙的方法,可以发明一个自定义列表,其中 get(0) 的 Big-O 复杂度可以是任何你提出的。


<sup>1 - 我在这里故意含糊其辞。一方面,我们需要确定一个变量 N,表示复杂性分析的“问题”大小。(列表的长度是显而易见的选择。)另一方面,当考虑所有可能的接口实现方式时,List 的长度是一个令人惊讶且“有弹性”的概念。</sup>

<sup>2 - 我假设你之所以问这个问题,是因为你想编写一些依赖于 List.get(0)O(1) 的库代码。由于无法阻止其他人使用非内置的列表实现来使用你的库,所以你问题中的“假设它是内置的”限制实际上没有帮助……即使我们可以检查所有可能的(过去、现在或未来的)内置 List 实现。</sup>

英文:

> Is get(0) on java.util.List always O(1)?

Let us assume that there is a parameter N which stands for the length of the list<sup>1</sup>.

For the 4 implementations of List that you mentioned, get(0) is indeed an O(1) operation:

  • ArrayList, Vector and Stack all implement get(i) using array subscripting and that is an O(1) operation.

  • LinkedList.get(i) involves i link traversals which is O(i). But if i is a constant, that reduces to O(1).

However there are other "built in" implementations of List. Indeed, there are a considerable number of them if you include the various non-public implementations, such as the List classes that implement sublists, unmodifiable lists, and so on. Generalizing from those 4 to "all of them" is not sound<sup>2</sup>.


But get(0) won't be O(1) for all possible implementations of List.

  • Consider a simple linked list where the elements are chained in the reverse order. Since get(0) needs to traverse to the end of the list, which is N link traversals: O(N).

  • Consider a list that is fully populated from the rows in a database query's result set the first time that you attempt to retrieve a list element. The first get call will be at least O(N) because you are fetching N rows. (It could be worse than O(N) if the database query is not O(N).) So the worst case complexity for any call to get is O(N) ... or worse.

Indeed, with a some ingenuity, one could invent a custom list where get(0) has any Big-O complexity that you care to propose.


<sup>1 - I am being deliberately vague here. On the one hand, we need to identify a variable N denoting the "problem" size for complexity analysis to make sense. (The length of the list is the obvious choice.) On the other hand, the length of a List is a surprisingly "rubbery" concept when you consider all of the possible ways to implement the interface.</sup>

<sup>2 - I assume that you are asking this question because you want to write some library code that relies on List.get(0) being O(1). Since you can't prevent someone from using your library with a non-builtin list implementation, your "assume it is builtin" constraint in your question doesn't really help ... even if we could check all possible (past, current or future) builtin List implementations for you.</sup>

答案2

得分: 0

忽略自定义实现,仅考虑内置实现,就像问题末尾建议的那样,你仍然不能断言无论列表大小如何,get(0) 都是 O(1)

举个例子,在基于 LinkedList 的子列表上调用 get(0) 将是 O(n)

List<Integer> list = new LinkedList<>(Arrays.asList(1,2,3,4,5,6,7,8,9));
List<Integer> subList = list.subList(4, 8);
Integer num = subList.get(0); // <===== O(n),不是 O(1)

在这段代码中,subList.get(0) 在内部调用了 list.get(4),其时间复杂度为 O(n)

英文:

Ignoring custom implementations, and only looking at built-in implementations, like suggested at the end of the question, you still cannot say that get(0) will be O(1) regardless of list size.

As an example, calling get(0) on a sublist based on a LinkedList will be O(n):

List&lt;Integer&gt; list = new LinkedList&lt;&gt;(Arrays.asList(1,2,3,4,5,6,7,8,9));
List&lt;Integer&gt; subList = list.subList(4, 8);
Integer num = subList.get(0); // &lt;===== O(n), not O(1)

In that code, subList.get(0) internally calls list.get(4), which has O(n) time complexity.

答案3

得分: -2

是的,对于您提到的所有List的实现,get(0)的时间复杂度是O(1)。

英文:

Yes, for all implementations of List you mentioned get(0) is O(1).

huangapple
  • 本文由 发表于 2020年10月24日 16:03:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64511254.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定