我有一个包含私有可变数据列表的类。
我需要在以下条件下公开列表项:
哪个getter函数应该标记为推荐方法?或者您能提供更好的解决方案吗?
class DataProcessor {
private final ArrayList<String> simpleData = new ArrayList<>();
private final CopyOnWriteArrayList<String> copyData = new CopyOnWriteArrayList<>();
public void modifyData() {
...
}
public Iterable<String> getUnmodifiableIterable() {
return Collections.unmodifiableCollection(simpleData);
}
public Iterator<String> getUnmodifiableIterator() {
return Collections.unmodifiableCollection(simpleData).iterator();
}
public Iterable<String> getCopyIterable() {
return copyData;
}
public Iterator<String> getCopyIterator() {
return copyData.iterator();
}
}
UPD:这个问题来自关于列表getter实现的最佳实践的真正代码审查讨论
通过封装规则,您必须始终返回一个不可修改的列表,在本例中是一个设计规则,因此返回集合。unmodifiableCollection,您不需要将该方法命名为getUnmodifiable,使用getter命名约定并使用Javadoc告诉其他开发人员您返回的列表类型以及原因…粗心的用户将收到异常警报!!
通常,迭代器仅与迭代对象一起使用,用于 for-每个循环。看到一个非迭代类型包含一个返回迭代器的方法会很奇怪,并且可能会让用户感到不安,因为它不能在每个循环中使用。
所以在这种情况下我建议Iterable。如果有意义,您甚至可以让您的类实现Iterable
。
如果您想跳上Java8旅行车,返回Stream
可能是一种更“现代”的方法。
“最佳”解决方案实际上取决于预期的应用程序模式(而不是“意见”,正如一位势均力敌的投票者所建议的)。每个可能的解决方案都有可以客观判断的优点和缺点(并且必须由开发人员来判断)。
编辑:已经有一个问题“我应该返回集合还是流?”,Brian Goetz给出了详细的答案。在做出任何决定之前,您也应该参考这些答案。我的答案不是指流,而是指将数据公开为集合的不同方式,指出不同方法的优缺点和含义。
返回迭代器
只返回迭代器
是不方便的,无论进一步的细节如何,例如它是否允许修改。单独的迭代器
不能在foreach
循环中使用。所以客户端必须编写
Iterator<String> it = data.getUnmodifiableIterator();
while (it.hasNext()) {
String s = it.next();
process(s);
}
而基本上所有其他解决方案都允许他们只写
for (String s : data.getUnmodifiableIterable()) {
process(s);
}
公开< code > Collections.unmodifiable...查看内部数据:
您可以公开内部数据结构,并将其包装到相应的 Collections.unmodifiable...
集合中。任何修改返回的集合的尝试都将导致引发“不支持的操作异常”
,明确指出客户端不应修改数据。
这里设计空间的一个自由度是你是否隐藏额外的信息:当你有一个< code>List时,你可以提供一个方法
private List<String> internalData;
List<String> getData() {
return Collections.unmodifiableList(internalData);
}
或者,您可以对内部数据的类型不太具体:
List#get(int index)
方法进行索引访问,那么您可以将此方法的返回类型更改为Collection
还要考虑,在公开不太特定的接口时,您可以选择将内部数据的类型更改为<code>集
公开内部数据的副本:
一个非常简单的解决方案是只返回列表的副本:
private List<String> internalData;
List<String> getData() {
return new ArrayList<String>(internalData);
}
这可能具有(可能很大且频繁的)内存副本的缺点,因此只有在集合“小”时才应考虑。
此外,调用方将能够修改列表,并且他可能希望更改反映在内部状态中(事实并非如此)。这个问题可以通过另外将新列表包装到
Collections.unmodifiableList 中来
缓解。
公开
CopyOnWriteArrayList
通过< code>Iterator
或作为< code>Iterable来公开< code > CopyOnWriteArrayList 可能不是一个好主意:调用方可以选择通过< code>Iterator#remove调用来修改它,而您显然希望避免这种情况。
公开<code>CopyOnWriteArrayList<code>的解决方案,它被包装到<code>集合中。不可修改列表可能是一个选项。乍一看,它可能看起来像一个多余的厚防火墙,但它肯定是合理的-见下一段。
一般考虑
无论如何,你应该虔诚地记录这种行为。特别是,您应该记录调用方不应以任何方式更改返回的数据(无论是否可能不导致异常)。
除此之外,还有一个令人不安的权衡:您可以在留档中精确,或者避免在留档中暴露实现细节。
考虑以下情况:
/**
* Returns the data. The returned list is unmodifiable.
*/
List<String> getData() {
return Collections.unmodifiableList(internalData);
}
这里的文档实际上还应该说明...
/* ...
* The returned list is a VIEW on the internal data.
* Changes in the internal data will be visible in
* the returned list.
*/
考虑到线程安全和迭代期间的行为,这可能是一个重要的信息。考虑一个在内部数据的不可修改视图上迭代的循环。假设在这个循环中,有人调用了一个导致内部数据修改的函数:
for (String s : data.getData()) {
...
data.changeInternalData();
}
此循环将因< code > ConcurrentModificationException 而中断,因为内部数据在迭代时被修改。
这里关于留档的权衡是指,一旦指定了某种行为,客户端就会依赖这种行为。想象客户端这样做:
List<String> list = data.getList();
int oldSize = list.size();
data.insertElementToInternalData();
// Here, the client relies on the fact that he received
// a VIEW on the internal data:
int newSize = list.size();
assertTrue(newSize == oldSize+1);
如果返回了内部数据的真实副本,或者通过使用CopyOnWriteArrayList
(每个都包含在Collection.unmodifiableList
中),则可以避免像ConcurrentModificationException
这样的事情。在这方面,这将是“最安全”的解决方案:
但是,人们必须考虑各个应用案例是否真的需要如此多的“安全性”,以及如何以一种仍然允许更改内部实现细节的方式记录下来。
嗨,伙计们,我把这个作为面试问题来回答,但我遇到了麻烦。我熟悉泛型/集合 问题是:所提供的工作区中包含cocI,它是一个类的开始,该类实现了一个迭代器,可用于迭代集合集合。集合集合被传递到类的构造函数中。迭代器应该首先遍历内容深度。 例如,如果集合集合如下所示: 然后迭代器应按以下顺序返回内容:“A”、“B”、“C”、“D”、“E”、“F” Q.在cocI中提供hasNext()和next()方法
Iterator(迭代器)是一个接口,它的作用就是遍历容器的所有元素,也是 Java 集合框架的成员,但它与 Collection 和 Map 系列的集合不一样,Collection 和 Map 系列集合主要用于盛装其他对象,而 Iterator 则主要用于遍历(即迭代访问)Collection 集合中的元素。 Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提
问题内容: 我了解像Hashtable这样的集合是同步的,但是有人可以向我解释它是 如何 工作的,在什么时候访问仅限于并发调用?例如,假设我使用了一些像这样的迭代器: 有人可以解释一下从不同线程中随机调用这些函数是否有陷阱吗?特别是,迭代器如何进行同步,尤其是在使用entrySet()时,似乎也需要同步?如果在循环之一进行时调用clear()会发生什么?如果removesomething()删
迭代器 乍看来,迭代器似乎很直观。但凑近了看,你会发现标准STL容器提供了四种不同的迭代器:iterator、const_iterator、reverse_iterator和const_reverse_iterator。很快你会注意到在这四种类型中,容器的insert和erase的某些形式只接受其中一种。那是问题的开始。为什么有四种迭代器?它们之间的关系是什么?它们可以互相转化吗?在调用算法和ST
For freedom Christ has set us free. Stand firm, therefore, and do not submit again to a yoke of slavery. 基督释放了我们,叫我们得以自由,所以要站立得稳,不要再被奴仆的轭挟制。(GALATIANS 5:1) 迭代器 迭代,对于读者已经不陌生了,曾有专门一节来讲述,如果印象不深,请复习《迭代》。
在Rust中,迭代器共分为三个部分:迭代器、适配器、消费者。 其中,迭代器本身提供了一个惰性的序列,适配器对这个序列进行诸如筛选、拼接、转换查找等操作,消费者则在前两者的基础上生成最后的数值集合。 但是,孤立的看这三者其实是没有意义的,因此,本章将在一个大节里联系写出三者。 迭代器、适配器、消费者