1

我有一个类,它维护类的功能列表。与读取相比,这些特征很少改变。读取几乎总是通过特征列表进行迭代。因此,我使用的是CopyOnWriteArrayList.

我想要这样的功能:

function Feature[] getFeatures() {
  .. implementation goes here ..
}

我承认,原因可能是有点懒惰。我想写这样的代码:

for (Feature f: object.getFeatures()) {
  .. do something interesting ..
}

而不是这个:

Iterator<Feature> iter = object.getFeatureIterator();
while (iter.hasNext()) {
  Feature f = iter.next();
  .. do something interesting ..
}

主要问题是 - 我在这里偷懒了吗?我将经常遵循这种模式,并且我认为第一段代码更容易维护。显然,我永远不会更改底层数组,我会将其放入文档中。

处理这种情况的正确方法是什么?

4

5 回答 5

1

我不明白您的原因:返回 a List<Feature>,使用您的CopyOnWriteArrayList或不可修改的副本,然后使用foreach. 为什么你特别想要一个数组?

于 2011-11-18T23:51:54.100 回答
1

只需调用列表中的 toArray 方法:

public Feature[] getFeatures() {
    return this.featureList.toArray(new Feature[this.featureList.size()]);
}

请注意,foreach 语法可以与所有 Iterable 对象一起使用,并且 List 是 Iterable,因此您可以只使用

public List<Feature> getFeatures() {
    return this.features;
}

并使用相同的 foreach 循环。如果您不希望调用者修改内部列表,请返回列表的不可修改视图:

public List<Feature> getFeatures() {
    return Collections.unmodifiableList(this.features);
}
于 2011-11-18T23:52:58.173 回答
1

CopyOnWriteArrayListimplements Iterable,这是您使用糖化for循环语法所需的全部内容。Iterator在上述情况下,您不需要明确掌握。

有没有发现编译不出来?

于 2011-11-18T23:53:49.890 回答
1

你可以克隆列表

public List<Feature> getFeatures() {
    return (List<Feature>)this.features.clone();
}

克隆 copyOnWriteArrayList 不会复制底层数组

于 2011-11-19T00:03:36.597 回答
0

对象的写时复制 (COW) 特性CopyOnWriteArrayList意味着,在列表的每次修改中,您都会获得其底层数组的新实例,除了从前一个实例复制的修改之外的所有条目。

当其他线程不断更改其内容时,为了在迭代列表时为您提供连贯的视图,对该方法的调用将iterator()迭代器绑定到数组,而不是列表。当修改更改列表时,新数组会保存新内容,但迭代器会继续遍历iterator()调用时可用的旧数组。这意味着您将浏览列表的连贯快照。

(相比之下,诸如循环之类的循环for (int i = 0; i < list.size(); ++i) doSomethingWith(list.get(i));并不能保护您免受其他线程的修改,如果在调用list.size()和相应的之间,您可能很容易跑出列表的末尾list.get(i),某些元素已被删除!)

由于 for each 风格的 for 循环在底层使用了迭代器,因此您可以获得这种连贯性保证。使用该forEach()方法时也会得到它。forEach()这在调用时可用的数组内迭代(使用数组索引) 。

最后,该clone()方法基本上拍摄了类似的快照。如果您的代码是您使用克隆版本的唯一地方,或者您将其设为不可修改,您将参考原始快照。原件的 COW 特性保护您的副本免受对原件的更改。如果您不想依赖clone()问题的 ,您可以复制出列表数据,但这至少涉及使用另一个分配复制数组。

于 2019-08-09T08:37:00.780 回答