【问题标题】:Where can I find a type safe UnmodifiableCollections implementation?在哪里可以找到类型安全的 UnmodifiableCollections 实现?
【发布时间】:2014-05-28 17:13:15
【问题描述】:

我想在获得类型安全的同时返回一个不可修改的集合。尽管我希望它很容易做到,但我似乎找不到它。

假设我有一个类 Foo,其中包含一个 large 类 Bar 集合,它足够大,以至于需要一段时间才能克隆。作为紧密循环的一部分,我希望遍历 Foo 中 Bar 的每个元素,所以我的 Foo 对象上有一个 getBars() 方法

我不想将 foo 中的集合直接传递给调用 getBar() 的人,因为这将允许任何人修改集合,这是不安全的。所以我的选择是

1) 将我的 Foo 集合复制到一个新集合中 2) 返回 Collections.unmodifiableCollections 第一个选项需要复制所有内容,这在紧密循环中代价高昂,而且我预计在返回集合后不需要修改集合。第二个选项做了我想要的,但它并没有让最终用户明白发生的事情。如果后来有人决定出于某种原因修改 getBar 返回的集合,他们将在调用添加或删除的那一刻得到一个异常,这在代码中没有明确记录(是的,API 会记录它,但不是一样)。

我想要的是有一个 UnmodifiableCollection 类,它包装了我的实际集合,并且只使 getter 可见,而 setter 不可见。它会很快,因为它只是包装而不是复制底层集合,并且它会自我记录,您无法修改集合,以后不会出现意外的运行时异常。

我想知道这是否存在!? Guava 似乎很基本,应该有它,但我在任何地方都找不到。如果它不存在,是否有某种原因使我想这样做会更难或更没用? 附言。据我所知,Guava immutableCollections 都会复制基础集合,因此它们相当于上面的选项 1

【问题讨论】:

  • "足够大,克隆需要一段时间" => 多大?除非它有数百万个条目,否则副本会很快。
  • 问题是Collection 接口指定了这些方法。因此,您可以拥有一个没有 setter 的类,或者拥有与 Collections 框架兼容的东西。将 Collection 接口拆分为仅 getter 部分和带有扩展它的 setter 的子接口会很好,但这可能太复杂且不需要包含足够的内容。

标签: java collections


【解决方案1】:

java.util.Collections API 设计无法实现。

所有标准集合 API 都设计为可变的,因此只要您想使用集合接口,您就无能为力

您可以切换到返回(您自己的实现)枚举(有点过时的接口),或者一个简单的迭代器(getBarIterator() 而不是 getBarList())。这避免了使用任何添加/删除方法的所有(或大部分)影响。您以这种方式暴露的信息/功能要少得多,这可能是一个问题或需要,具体取决于用例。对于 Iterator,您仍然可以在迭代器上使用 remove() 方法,客户端可以尝试使用该方法来获取 UnsupportedOperationException()/IllegalStateException()。

Collection API 的读取和修改方法都在同一个界面中,这一直困扰着我。无论出于何种原因,API 设计人员选择按原样进行设计,并且由于它现在已广泛建立,因此无法更改(不破坏兼容性,这与“不可更改”一样好)。

如果这些都不适合,并且您重视干净的 API 安全而不是使用标准 API,请推出您自己的不可变 API。

编辑: 我自己对这个困境的个人解决方案是创建一组 alterable 包装器。他们实现了普通的集合 API(List、Set、Map),最初他们只是将所有内容委托给他们的 source 集合。在第一次写入尝试时,它们会复制其源代码并在此之后作为正常的独立集合运行。请注意,实现,尤其是 iterator().remove() 的实现可能会很棘手。

【讨论】:

  • 对于您的编辑,有点像 CopyOnWriteArrayList 但对于所有内容?
  • 是的,含糊其辞。不同之处在于可更改包装器不针对或(本身)不适合多线程访问。
【解决方案2】:

将您的集合转换为不允许突变的类型是否可以接受?例如:

static <T> List<? extends T> unmodifiable(List<T> list) {
    return (List<? extends T>)list;
}

List<? extends String> list = unmodifiable(Arrays.asList("one", "two", "three"));
list.add("four"); // compilation error!
list.set(0, "bar"); // another compilation error!
String first = list.get(0); // but I can read Strings out of my List, at least

【讨论】:

    【解决方案3】:

    如果你使用Collections.unmodifiableCollections,那么包装器集合返回将抛出一个UnsupportedOperationException

    如果您创建一个标准包装器,那么您最终将获得与 Collections.unmodifiableCollections 相同的实现

    但是,如果您使用标准等不同方法创建自己的包装器,您最终将实现单独的类 或向您的客户公开自己的集合类和接口,这可能会在标准和特定之间混合您的实现。

    我认为简单的方法是使用Collections.unmodifiableCollections 或克隆并使用标准 Java 开发 最终实现你自己的包装类和方法。

    但是,如果您使用与标准集合中不同的方法创建自己的包装器, 它不会是集合的包装器,它将是一个在内部使用集合的自己的类。

    您可以简单地返回您自己的对象,该对象实现您的特定类并且不会分散您的命名客户。

    【讨论】:

      猜你喜欢
      • 2019-05-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 2017-02-14
      • 1970-01-01
      • 2011-03-11
      • 2012-09-17
      相关资源
      最近更新 更多