据我所知,有两种方法:
- 遍历集合的副本
- 使用实际征收的迭代器
例如,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
和
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
是否有任何理由更喜欢一种方法比其他(如喜欢的可读性的原因很简单,第一种方法)?
让我举一些替代品的几个例子,以避免ConcurrentModificationException
。
假设我们有图书以下集合
List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));
收集和删除
第一种技术包括收集所有我们想要删除的对象(例如,使用for循环的增强),我们完成迭代后,我们会删除所有找到的对象。
ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);
这是假设你想要做的操作是“删除”。
如果你想“增加”这种做法也将工作,但我会假设你会遍历不同的集合,以确定您要添加到第二个集合,然后发出一个什么样的元素addAll
在最后方法。
使用的ListIterator
如果您在操作列表,另一种方法包括使用ListIterator
具有用于删除和添加项目的迭代过程本身的支持。
ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}
同样,我用本例中的“删除”方法上面这是你的问题似乎有什么暗示,但是你也可以使用该add
的方法来迭代过程中增加新的元素。
使用JDK> = 8
对于那些使用Java 8或上级版本的工作,有一对夫妇,你可以用它来利用它的其他技术。
你可以使用新的removeIf
的方法Collection
的基类:
ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));
或者使用新的流程API:
ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());
在最后这种情况下,过滤元件从集合中,重新分配的原始参考过滤收集(即books = filtered
)或所使用的过滤收集removeAll
从原始集合所找到的元件(即books.removeAll(filtered)
)。
使用子表或子
还有其他的替代方案以及。 如果列表进行排序,并要删除连续元素,你可以创建一个子表,然后将其清除:
books.subList(0,5).clear();
由于子列表是由原始列表支持,这将是移除元素的这个子集合的有效方式。
类似的东西可以用有序集合来实现NavigableSet.subSet
法或任何提供有切片的方法。
注意事项:
你用什么方法,可能取决于你打算做什么
- 在收集和
removeAl
技术也适用于任何集合(集合,列表,集等)。 - 该
ListIterator
技术显然只是与名单的作品,前提是他们给出ListIterator
实现提供支持,添加和删除操作。 - 该
Iterator
方法将与任何类型的集合的工作,但它仅支持删除操作。 - 随着
ListIterator
/ Iterator
接近显而易见的好处是不必复制任何东西,因为我们删除,因为我们迭代。 所以,这是非常有效的。 - 在JDK 8流例如没有实际删除任何东西,但找了所需的元素,然后我们替换为新的一个原始集合的参考,让老被垃圾收集。 所以,我们在收集循环只有一次,这将是有效的。
- 在收集和
removeAll
方法的缺点是,我们必须重复两次。 首先我们反复在福尔环找一个对象,符合我们的拆除标准,一旦我们找到了,我们要求从原来的集合,这将意味着第二次迭代工作,以寻找这个项目将其删除去掉它。 - 我认为这是值得一提的是,中remove方法
Iterator
接口被标记为的Javadoc“可选”,这意味着,有可能是Iterator
实现抛出UnsupportedOperationException
,如果我们调用remove方法。 因此,我会说这种方法比别人少安全的,如果我们不能保证去除元素的迭代器支持。
是否有任何理由更喜欢一种方法比其他
第一种方法将工作,但复制列表明显的开销。
第二种方法是行不通的,因为许多容器不迭代过程中允许修改。 这包括ArrayList
。
如果只修改是删除当前元素,您可以通过使用使第二种方法工作itr.remove()
即,使用迭代器的remove()
方法,而不是容器的)。 这将是我的首选方法,用于支持迭代器remove()
在Java 8,还有另一种方法。 收藏#removeIf
例如:
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.removeIf(i -> i > 2);
只有第二种方法会奏效。 您可以使用迭代过程中修改集合iterator.remove()
只。 所有其他的企图将导致ConcurrentModificationException
。
你不能做第二个,因为即使你使用remove()
上的方法迭代器 , 你会得到抛出的异常 。
就个人而言,我宁愿先为所有Collection
的情况下,尽管增加了偷听创建新的Collection
,我觉得其他开发人员在编辑过程不那么容易出错。 在某些集合实现,迭代器remove()
的支持,对其他事实并非如此。 您可以在文档的阅读更多迭代 。
第三种方法,是创建一个新的Collection
,遍历原来,和第一的所有成员加入Collection
到第二Collection
所有不为删除。 根据大小Collection
和删除的数量,这可能显著节省内存,相对于第一种方式时。
我会选择第二个,你不必做内存的拷贝和迭代器工作更快。 所以你节省内存和时间。
还有一个简单的解决方案,以“迭代”一个Collection
和删除每个项目。
List<String> list = new ArrayList<>();
//Fill the list
它只是conciste上循环,直到列表为空,并在每次迭代中,我们删除了第一个元素remove(0)
while(!list.isEmpty()){
String s = list.remove(0);
// do you thing
}
我不认为这有比作任何改进Iterator
,它仍然需要有一个可变的列表,但我喜欢这个解决方案的简单性。
为什么不呢?
for( int i = 0; i < Foo.size(); i++ )
{
if( Foo.get(i).equals( some test ) )
{
Foo.remove(i);
}
}
如果这是一个地图,而不是一个列表,你可以使用键集()