【问题标题】:Why does the Java List interface not support getLast()?为什么Java List 接口不支持getLast()?
【发布时间】:2010-11-06 04:15:50
【问题描述】:

我正在尝试了解 Java 标准集合库中的 API 不一致。

List 或 AbstractList 中没有获取最后一项的方法,尽管可以使用 size 和 getIndex() 进行模拟。

但是,LinkedList 支持该功能。

知道为什么决定在界面中不支持此方法吗?

【问题讨论】:

  • 在代码中处理了几次之后,我很确定这是为了让维护程序员对 API 设计者生气。 :-) Greenfield 程序员最终会绕过 LinkedList(而不是 List),因此他们可以使用 getLast()。维护人员发现 ArrayList 更合适,并且必须处理所有这些指定的 LinkedLists,这些 LinkedLists 本来应该是 List (并且可能除了 List 接口中的这​​个严重故障之外)......跨度>

标签: java api collections


【解决方案1】:

接口的目标是以尽可能少的公共方法提供最大的可用性。支持的方法越少越好。正如你所说,没有 getLast() 方法,因为它可以派生。

另一方面,LinkedList 是一个具体的实现,因此不存在这些问题。

编辑:正如 Skaffman 指出的,这不是界面的主要目标。它更像是一个次要目标,它允许这个接口的实现更容易维护。接口的主要目的是将具体实现与使用它的对象分离。

【讨论】:

  • 不,接口的目标是将实现与接口分开。操作的类型与它无关。
  • 我不确定我是否同意,但我想这取决于某人想要列表中最后一项的可能性。
  • 我想知道除了内置的 LinkedList/ArrayList 之外,还有哪些其他的列表实现是可能的,通用的 getFirst() getLast() 接口方法不会是 O(1)?
  • @skaffman:你说得对。当然,这是界面的主要目标。我更多地从次要目标的角度谈论。我将更新我的答案以反映这一点。
  • 随着 Java 8 的 default 方法的到来,您可以通过为接口的用户提供好的方法而不强迫实现者自己编写它们来获得两全其美的效果。所以他们可以添加default T getLast(),每个人都会很高兴。
【解决方案2】:

通常的原因是他们想为每个函数指定一个 Big-O 要求,并认为 getLast() 无法在所有列表上有效地实现。因此,他们在每个级别都引入了它的 Big-O 承诺。

或者它可能只是一个疏忽,或者他们觉得它不够普遍,如果你需要它,你可以使用 size/getIndex 来获得它。

【讨论】:

  • 我有时希望他们至少也将它添加到 ArrayList 中。一遍又一遍地编写 arraylist.get(arraylist.size() - 1) 有点像样板。
  • public class MyArrayList extends ArrayList { public E getLast() {return get(size()-1);} }
  • @Charlie:除非您使用列表接口并且不想直接知道您的底层实现是什么。
  • 我猜查理会通过让 MyList 扩展 List 和 MyArrayList 实现 MyList 来解决这个问题。
  • @akarnokd 那么你将不得不更改所有代码,让读者想知道为什么要自定义列表,所有这些只是为了getLast() 方便。我也觉得它不在列表界面中很烦人,我理解极简主义的方法,但有时你只需添加一点点帮助就可以获得很多,比如isEmpty()
【解决方案3】:

正如您所说,使用 List 您可以使用 getIndex() 获取最后一个,因为它是基于索引的。所以就我个人而言,我看不出有什么好的理由来写它(因为你可以自己写)。

虽然 LinkedList 不是基于数组的,因此没有索引,但提供这样的方法是有意义的,有时您需要知道最后一项。

【讨论】:

    【解决方案4】:

    getLast() 无法在单向链表中实现。单向链表上的 getIndex() 可能会从头开始扫描整个列表,因此它具有效率含义。

    【讨论】:

    • 除非它只是将最后一个元素存储在一个字段中。你觉得 getSize() 是怎么实现的?
    • 这就是为什么 java 的 LinkedList 是双链接的,或者在单链接的情况下,列表只包含对其最后一个元素的引用,以允许在列表末尾添加 O(1)。
    【解决方案5】:

    java.util.List 接口不支持getLast(),因为设计人员采用了“最小接口”。由于定义的方法数量最少,因此更容易理解和更快地学习。

    这与试图提供执行常见操作的方法(例如getLast())的“人性化界面”(例如在Ruby array class 中使用的)形成对比。由于列表这样的基本概念可以用于许多用途,这往往会导致更大的接口。

    有关详细信息,请参阅 Martin Fowler 的 Minimal InterfaceHumane Interface 说明。

    至于LinkedList为什么支持getLast()等,引用javadoc:

    ... LinkedList 类提供统一命名的方法来获取、删除和插入列表开头和结尾的元素。这些操作允许将链表用作堆栈、队列或双端队列(deque)。

    据推测,对于这些特定的用例来说,一般的列表是不够的。

    作为对 Java Collections API 的主要设计者 (Joshua Bloch) 的洞察力,他提供了this list of API design maxims,供他工作。其中,与这个问题最相关的是:

    API 的早期草稿应该很短,通常是一页,带有类和方法签名以及一行描述。这样一来,当您第一次没有做好 API 时,就可以轻松地重新构建 API。

    如有疑问,请忽略它。如果 API 设计有一个基本定理,就是这样。它同样适用于功能、类、方法和参数。 API 的每个方面都应该尽可能小,但不能更小。你以后可以随时添加东西,但你不能把它们拿走。最小化概念权重比类数或方法数更重要。

    保持 API 不受实现细节的影响。它们使用户感到困惑并抑制了发展的灵活性。什么是实现细节并不总是很明显:小心过度规范。

    最小化可访问性;如有疑问,请将其设为私有。这简化了 API 并减少了耦合。

    考虑 API 设计决策对性能的影响,但不要扭曲 API 以实现性能提升。幸运的是,好的 API 通常有助于快速实现。

    不过他也说:

    不要让客户做图书馆能做的任何事情。违反此规则会导致客户端中出现样板代码,这很烦人且容易出错。

    这只是表明设计准则经常发生冲突,而 API 设计师工作中最难的部分是平衡这些冲突。

    【讨论】:

    • 很好的解释。让我想知道为什么有一个 isEmpty 方法。
    • @peskal 在 Java 8 中,我们可以使用 isEmpty 作为方法引用,这很好。
    • @ThorbjørnRavnAndersen 但它在 Java 8 之前就已经存在了;)
    【解决方案6】:

    getLast() 方法来自 LinkedList 实现的 Deque 接口。如果你想要一个支持数组的版本,你可以使用 ArrayDeque 。我猜它不是 List 接口的一部分,因为他们想将不同的抽象数据类型分离到单独的接口中,即使像 LinkedList 这样的实现可以实现多个接口。

    【讨论】:

    • Deque 接口仅在 Java 6 中引入。LinkedList 类(及其 getLast() 方法)比它早了几个版本(在 1.2 中引入)。
    【解决方案7】:

    List 接口没有该类型的任何便捷方法。没有 getFirst() 或 getLast()。它是严格基于索引的。出于性能原因,LinkedList 需要一种避免基于索引的查找的方法,因此 getLast 确实是一种优化,而不是一种方便的方法。

    我确信设计者希望减少方法的数量,所以他们没有添加很多可能很方便的方法。

    【讨论】:

    • 如果您查看 LinkedList 实现,它包含对基于索引的锁定的优化:如果索引在列表的一半大小之后,则遍历从末尾开始并向后移动。这样,get(size - 1) 实际上与 getLast() 具有相同的时间成本。
    • @kd304,这是一个实现细节,它没有解决基于索引的查找对于 LinkedList 不是最佳的一般情况。 LinkedList 上没有规定某些基于索引的查找会很快的合同。
    猜你喜欢
    • 1970-01-01
    • 2013-06-05
    • 2011-04-03
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    • 2011-01-27
    • 2011-05-06
    • 1970-01-01
    相关资源
    最近更新 更多