java 迭代器 isEmpty() 调用导致 noSuchElement 异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13053195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 11:20:20  来源:igfitidea点击:

Iterator isEmpty() call causing noSuchElement exception?

javaandroiditeratorhashmap

提问by kevinl

I currently have the following code

我目前有以下代码

while (!visibleTiles.isEmpty())) {
    tile = visibleTiles.keySet().iterator().next();
    if (tile != null){
        bitmap = visibleTiles.remove(tile);
        if(bitmap != null && !containsKey(tile)){ //safe to recycle if tile cache is not actively holding it
            bitmap.recycle();
        }
    }
}

However, I get a NoSuchElementException crash on the line

但是,我在线上遇到 NoSuchElementException 崩溃

tile = visibleTiles.keySet().iterator().next();

Is there a big difference in using the isEmpty() method and calling a hasNext() call? I know that hashmaps do not have a hasNext() call so I did the following:

使用 isEmpty() 方法和调用 hasNext() 调用有很大区别吗?我知道哈希图没有 hasNext() 调用,所以我做了以下操作:

while (visibleTiles.keySet().iterator().hasNext()) {
    tile = visibleTiles.keySet().iterator().next();
    if (tile != null){
        bitmap = visibleTiles.remove(tile);
        if(bitmap != null && !containsKey(tile)){ //safe to recycle if tile cache is not actively holding it
            bitmap.recycle();
        }
    }
}

Obviously, I know that I should just run the app and see if it crashes, but the issue is that it's difficult to reproduce the problem.

显然,我知道我应该运行应用程序并查看它是否崩溃,但问题是很难重现该问题。

Thanks!

谢谢!

回答by kosa

visibleTiles.isEmpty()

可见瓷砖.isEmpty()

just checks whether map is empty (or) it has any elements.

只是检查地图是否为空(或)它有任何元素。

ile = visibleTiles.keySet().iterator().next();

retrieves next element from iterator.

从迭代器中检索下一个元素。

You need to do hasNext()check before doing next()on iterator.

hasNext()next()对迭代器进行操作之前,您需要进行检查。

hasNext()javadocsays

hasNext()javadoc

Returns true if the iteration has more elements. (In other words, returns true if next would return an element rather than throwing an exception.)

如果迭代有更多元素,则返回 true。(换句话说,如果 next 将返回一个元素而不是抛出异常,则返回 true。)

so, if no element is available and call next()on iterator will return NoSuchElementException.

因此,如果没有可用的元素并且调用next()迭代器将返回NoSuchElementException.

On top of this, I think you really would like to do NOT empty check

最重要的是,我认为您真的很想不要空头支票

while (!visibleTiles.isEmpty())) {
....
}

回答by Swalih

You can use com.google.common.collect.Iterables class for empty check on Iterable values.

您可以使用 com.google.common.collect.Iterables 类对 Iterable 值进行空检查。

Iterables.isEmpty(yourIterable)

回答by arne.b

while (visibleTiles.keySet().iterator().hasNext())creates a new Iteratorevery time it is called*. If visibleTilesis not empty, this will always be true, as the next method of the respective iteratoris never called and the internal pointer never advances.

while (visibleTiles.keySet().iterator().hasNext())Iterator每次调用时都会创建一个新*。如果visibleTiles不为空,则始终为真,因为永远不会调用相应迭代器的下一个方法并且内部指针永远不会前进。

Iterators should be used like this:

迭代器应该这样使用:

Iterator<TileType> tileIt = visibleTiles.keySet().iterator();
while (tileIt.hasNext()) {
    TileType tile = tileIt.next();
    // ...
}

*The point here is that there may be several iterators referring to the same collection at the same time. Each iterator has its own internal state storing (not necessarily explicitly) which element was returned last and which shall be returned next by the next()call. This can be useful, for example, do perform some operation on all pairs of elements from a collection, where two iterators are needed.

*这里的重点是可能有多个迭代器同时引用同一个集合。每个迭代器都有自己的内部状态存储(不一定显式)哪个元素最后返回,哪个元素将被next()调用返回。这可能很有用,例如,对集合中的所有元素对执行某些操作,其中需要两个迭代器。

回答by Yogendra Singh

Fixing while condition should resolve the issue.

修复 while 条件应该可以解决问题

Change the condition to check not empty by adding not(!) in front:

通过在前面添加 not(!) 来更改条件以检查非空:

   while (!visibleTiles.isEmpty())) {

EDIT: Sample code:

编辑:示例代码:

    HashMap<String, Object> visibleTiles = new HashMap<String, Object>();
    visibleTiles.put("abc", new Object());
    visibleTiles.put("xyz", new Object());
    visibleTiles.put("def", new Object());
    String tile = null;
    while (!visibleTiles.isEmpty()) {
        tile = visibleTiles.keySet().iterator().next();
        if (tile != null){
            Object bitmap = visibleTiles.remove(tile);
            if(bitmap != null){ //safe to recycle if tile cache is not actively holding it
                System.out.println("Recycle");
            }
        }
    }

回答by Bhesh Gurung

This is not related to the question you are asking (which has been answered already) but just as a side note from the docs

这与您提出的问题(已经回答)无关,只是作为文档的旁注

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

请注意,无法保证迭代器的快速失败行为,因为一般而言,在存在非同步并发修改的情况下不可能做出任何硬保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。

You can avoid that you can go remove on keyset iterator (which ultimately removes entry from the map), instead of doing the remove on the map itself (that's modifying the map externally while you are iterating through the keyset).

您可以避免在键集迭代器(最终从地图中删除条目)上进行删除,而不是在地图本身上进行删除(即在迭代键集时从外部修改地图)。

The part of your code that looks vulnerable:

代码中看起来易受攻击的部分:

tile = visibleTiles.keySet().iterator().next();
if (tile != null){
    bitmap = visibleTiles.remove(tile);

Example:

例子:

The following resulted in an ConcurrentModificationException:

以下结果导致ConcurrentModificationException

Map<String, String> map = new HashMap<String, String>();         
map.put("1", "1");
map.put("2", "1");
map.put("3", "1");
Iterator<String> it = map.keySet().iterator();
while(it.hasNext()) {
    if(it.next().equals("2")) {
         map.remove("2");
    }
}

while the following removes the entry from the map:

而以下内容从地图中删除了条目:

Map<String, String> map = new HashMap<String, String>();         
map.put("1", "1");
map.put("2", "1");
map.put("3", "1");
Iterator<String> it = map.keySet().iterator();

while(it.hasNext()) {

    String key = it.next();

    if(key != null) {

         String value = map.get(key); // you have the value and
         it.remove(); //you are modifying the map only through the iterator itself

         //... do stuffs with the value
    }
}

回答by kevinl

Thanks to the good information on efficiency, I have fixed the no such element exception with the code from the original post by removing the isEmpty call. It looks like you generally want to use hasNext when you call next in the same area of code.

多亏了关于效率的良好信息,我通过删除 isEmpty 调用,使用原始帖子中的代码修复了 no such element 异常。看起来您通常希望在同一代码区域中调用 next 时使用 hasNext 。

while (visibleTiles.keySet().iterator().hasNext()) {
    tile = visibleTiles.keySet().iterator().next();
        if (tile != null){
            bitmap = visibleTiles.remove(tile);
            if(bitmap != null && !containsKey(tile)){ //safe to recycle if tile cache is not actively holding it
                bitmap.recycle();
            }
        }
    }