java.util.NoSuchElementException没有这样的元素

问题结论:异常的字面含义是“未找到这样的元素”,但这个问题的本质还是我们常见的下标越界问题,即程序尝试去获取一个不存在的元素,一个超出了集合范围的元素。

今天一位同事就出现了这样的问题,不过问题稍微复杂些,代码如下:

public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> numbers2 = Arrays.asList(2, 3, 4, 5, 6, 7);

        // 遍历过滤后的结果
        Iterator<Integer> iterator = numbers.iterator();
        while (iterator.hasNext()) {
            List<Integer> collect = numbers2.stream().filter(tem -> iterator.next().equals(tem)).collect(Collectors.toList());
            System.out.println(collect.toArray());
        }

    }

执行以上程序会发现,代码在List<Integer> collect = numbers2.stream().filter(tem -> iterator.next().equals(tem)).collect(Collectors.toList());这一行发生了java.util.NoSuchElementException异常,打断点后发现,异常发生在collect(Collectors.toList())方法里,为什么会这样呢?

原因是这里有两个关键点被忽略了:

关键点1. iterator.next()方法每调用一次,下标cursor向前移动一位(初始下标为0,最终下标为numbers.size()-1)

关键点2. Stream 的 filter 方法是惰性求值的,它并不会立即执行过滤操作,而是等到遍历时才会应用过滤条件

发生错误的原因就是忽略了这两个关键点,更确切一点来说,是忽略了关键点2,导致关键点1的问题被暴露出来,从而导致发生NoSuchElementException异常发生。

此代码修复方式:

一,这里在迭代器里获取元素对象时,采用Integer next = iterator.next(); 这样的方式来获取元素,获取到next 后再去使用,而不应在具体使用时,直接通过iterator.next()的方式应用。直接使用iterator.next()很容易发生迭代器下标多次移位,导致预期的对象与实际对象不符。上文代码就是出现了这个问题。

二,不要在filter方法里直接使用迭代器next方法。在filter方法中直接使用iterator.next()刚巧就导致了下标多次移位,为什么会这样,原因就是关键点2提到的。 关键点2具体来说就是,当你调用 Stream 的 filter 方法时,它并不会立即对集合中的元素进行过滤操作,而是会创建一个新的 Stream,其中包含了过滤条件,但并没有立即执行过滤操作,当你对 Stream 进行终端操作(如 forEach、collect 等)时,才会触发 Stream 的执行,此时才会应用过滤条件并处理集合中的元素。因此,直到遍历numbers2集合时才会真正执行iterator.next()过滤操作,而不是在调用 filter 方法时立即执行,这就导致了iterator.next()方法被执行了numbers2.size()次,而numbers2有6个元素,numbers有5个元素,当iterator.next()被执行第6遍时,就抛出了NoSuchElementException异常。这就是问题发生的根本原因。

filter方法惰性求值优点:

这种惰性求值的特性使得 Stream 操作更加灵活和高效,可以根据需要组合多个操作,并在终端操作时才真正执行整个操作链。这样可以避免不必要的计算和提高性能。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值