【问题标题】:Is it better to use List or Collection?使用 List 还是 Collection 更好?
【发布时间】:2011-02-24 10:45:06
【问题描述】:

我有一个将一些数据存储在列表中的对象。实现以后可能会改变,我不想将内部实现暴露给最终用户。但是,用户必须具有修改和访问此数据集合的能力。目前我有这样的事情:

public List<SomeDataType> getData() {
   return this.data;
}

public void setData(List<SomeDataType> data) {
   this.data = data;
}

这是否意味着我允许内部实现细节泄露出去?我应该这样做吗?

public Collection<SomeDataType> getData() {
   return this.data;
}

public void setData(Collection<SomeDataType> data) {
   this.data = new ArrayList<SomeDataType>(data);
}

【问题讨论】:

  • 要记住的一件事是,如果您像这样返回实际的集合或列表,您就允许其他人做他们想做的任何事情,包括删除项目甚至清除整个事情。您最好返回一个不可变的包装器或列表的副本。
  • @PaulTomblin 是的,但它对系统来说有点矫枉过正和人为的开销。在处理由持久性中间件管理的对象时,不可修改的包装器是有意义的,即使在极少数情况下也是如此。
  • @comeGetSome,这就是为什么我说“可能”而不是“必须”。这取决于用例——如果我正在制作一个 API 供其他人使用,我会返回一个副本或一个包装器。如果是为了我自己或值得信赖的同事,我会在 javadocs 中写一个很大的“不要修改这个值”并保留它。

标签: java list collections abstraction


【解决方案1】:

这取决于,您是否希望您的用户能够对数据进行索引?如果是,请使用列表。两者都是接口,因此您不会泄露实现细节,实际上,您只需要确定所需的最低功能即可。

【讨论】:

  • 如果我们追求最小的功能,那么似乎我们应该每次都返回 Iterable。如果这是他们想要的,用户总是可以建立一个新的列表,对吗?我更喜欢我的方法来返回列表。
【解决方案2】:

如果我担心向外部用户隐藏我的数据的内部表示,我会使用 XML 或 JSON。无论哪种方式,它们都相当普遍。

【讨论】:

  • 这是否意味着总是返回字符串类型?
  • 当然。 XML 和 JSON 旨在非常通用,还有什么比字符串更通用的呢?
  • 好吧,如果我在应用程序之间传递数据,我将使用这种编码。但如果我说的是函数的返回值……将其转换为 XML,将其作为字符串返回,然后调用者必须解析 XML?仅仅返回一个数组就非常复杂和开销。
【解决方案3】:

是的,如果方法总是返回 List 不是接口契约的一部分,那么您的第一个替代方案确实会泄漏实现细节。此外,允许用户代码替换您的集合实例有些危险,因为他们传入的实现可能不会像您预期的那样运行。

当然,这完全取决于您对用户的信任程度。如果您采用“我们在这里都是成年人”的 Python 哲学,那么第一种方法就可以了。如果您认为您的库将被没有经验的开发人员使用,并且您需要尽一切可能“照顾”他们并确保他们不会做错事,那么最好不要让他们设置集合甚至不返回实际收藏。而是返回它的(浅)副本。

【讨论】:

  • java.util.Collections 包含像 unmodifiableList() 这样的静态方法,它们简单地包装集合,这样所有修改集合的方法都会返回错误。由于实例化包装器是一个恒定时间的操作,因此它甚至比浅拷贝更可取(如果需要可变集合,客户端代码可以自行生成。)
【解决方案4】:

这取决于您要向用户提供什么保证。如果数据是顺序的,因此元素的顺序很重要并且您允许重复,那么使用列表。如果元素的顺序无关紧要并且可能允许也可能不允许重复,则使用集合。由于您实际上是在返回基础集合,因此您不应该同时具有 get 和 set 函数,只有 get 函数,因为返回的集合可能会发生变异。此外,提供 set 函数允许用户更改集合的类型,而您可能希望特定类型由您控制。

【讨论】:

    【解决方案5】:

    使用最通用的类​​型,即 Collection,是最有意义的,除非有一些明确的理由使用更具体的类型 - List。但是无论你做什么,如果这是一个供公众使用的 API,请在文档中明确它的作用;如果它返回集合的浅表副本,请说明。

    【讨论】:

      【解决方案6】:

      当返回高层次结构中的接口或类的实现时,经验法则是声明的返回类型应该是最高级别,它提供您准备向调用者保证的最低功能,并且调用者合理需要的。例如,假设您真正返回的是一个 ArrayList。 ArrayList 实现 List 和 Collection(除其他外)。如果您希望调用者需要使用 get(int x) 函数,那么返回 Collection 将不起作用,您需要返回 List 或 ArrayList。只要你没有看到任何理由改变你的实现以使用列表以外的东西——比如一个集合——那么正确的答案就是返回一个列表。我不确定 ArrayList 中是否有任何不在 List 中的函数,但如果有,同样的推理将适用。另一方面,一旦你返回一个列表而不是一个集合,你现在已经在某种程度上锁定了你的实现。您在 API 中投入的越少,对未来改进的限制就越少。

      (实际上,在这种情况下,我几乎总是返回一个 List,它从来没有让我感到很痛苦。但我可能真的应该返回一个 Collection。)

      【讨论】:

        【解决方案7】:

        独立于通过 List.get(int) 索引到列表中的能力,用户(或您)是否期望集合的元素处于可靠且可预测的顺序?集合可以有多个相同的项目吗?这两者都是对更一般的集合不常见的列表的期望。这些是我在确定向最终用户公开哪个抽象时使用的测试。

        【讨论】:

          【解决方案8】:

          返回一个列表与编程到最合适的接口是一致的。

          返回一个集合会给用户带来歧义,因为返回的集合可能是:Set、List 或 Queue。

          【讨论】:

          • 非常老的问题,但是... 为后代回复 :) 返回集合允许更改内部实现(可能出于性能原因)并保护客户端代码免受此类更改;你让用户假设和使用的越多,保持灵活性就越难,这在创建 API 时很重要。特别是如果您想允许用户更改您返回的数据结构...返回任何提到的子接口也指示修改结构的方法。
          • 是的,这是一个提高抽象水平的有效用例。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-24
          • 2014-04-19
          • 2012-10-06
          • 1970-01-01
          • 2023-04-02
          • 2011-03-24
          相关资源
          最近更新 更多