我在哪里可以找到类型安全的UnmodifiableCollections实现?

时间:2014-05-28 17:13:16

标签: java collections

我希望在获得类型安全的同时返回一个不可验证的集合。我似乎无法找到这个,即使我希望它很容易做到。

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

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

1)将我的Foo集合复制到一个新集合中 2)返回Collections.unmodifiableCollections 第一个选项需要复制所有内容,这在紧凑的循环中是昂贵的,并且我预计在返回后不需要修改集合。第二个选项可以实现我想要的功能,但它并没有让最终用户明白这一点。如果有人后来决定他们想要修改getBar返回的集合由于某种原因他们将在调用添加或删除时获得异常,这在代码中没有明确记录(是的,API会记录它,但不是相同)。

我想要的是拥有一个UnmodifiableCollection类来包装我的实际集合,只让getter可见而不是setter。它会很快,因为它只包装而不是复制底层集合,而且它是自我记录的,你不能修改集合,没有人会在以后获得意外的运行时异常。

我想知道的是这是否存在!? Guava应该拥有它似乎是如此基本,但我无法在任何地方找到它。如果它不存在有一些原因,为什么这样做更难或更少有用,我想它会是? PS。据我所知,Guava immutableCollections都会执行底层集合的副本,所以它们等同于上面的选项1

3 个答案:

答案 0 :(得分:1)

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

所有标准集合API的设计都是可变的,因此只要您想使用集合接口,就可以执行 nothing

你可以切换到返回(你自己的实现)Enumeration(有点过时的接口),或者一个简单的Iterator(getBarIterator()而不是getBarList())。这避免了任何添加/删除方法的所有(或大多数)含义。根据用例,您可以通过这种方式显示更少的信息/功能,这可能是一个问题或需要。在Iterator的情况下,你仍然在迭代器上有remove()方法,客户端可以尝试仅使用它来获取UnsupportedOperationException()/ IllegalStateException()。

很长一段时间以来,我一直在告诉我,集合API的读取和修改方法都在同一个界面中。无论出于何种原因,API设计人员都选择按原样进行设计,并且由于它现在已广泛建立,因此无法进行更改(不会破坏兼容性,这与#34;不可更改")。 / p>

如果这些都不合适,并且您使用标准API重视上述API安全性,请滚动您自己的不可变API。

编辑: 我对这种困境的个人解决方案是创建一组可变的包装器。它们实现了普通的集合API(List,Set,Map),最初它们只是将所有内容委托给它们的 source 集合。在第一次写入尝试时,他们将其源和功能复制为正常的独立集合。请注意,实现,特别是iterator()。remove()的实现可能会很棘手。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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

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

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

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

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