北京网站设计方案,网站设计与制作教程,重庆传媒公司前十名,广告设计专业烧钱吗随着本学期的结束#xff0c;我想我会分享一个关于我对Java迭代器非常非常熟悉的小故事。 现实世界语境 就上下文而言#xff0c;我教第二年的软件组件课程#xff0c;这是尝试进入该专业的学生的最后障碍。 当然#xff0c;这门课程对学生来说压力很大#xff0c;我经常… 随着本学期的结束我想我会分享一个关于我对Java迭代器非常非常熟悉的小故事。 现实世界语境 就上下文而言我教第二年的软件组件课程这是尝试进入该专业的学生的最后障碍。 当然这门课程对学生来说压力很大我经常必须加倍努力为他们提供一切成功的机会。 不幸的是本学期我们被大流行所笼罩不得不转换为在线教学。 结果我们不得不对教学做出一些快速决策从而改变了学生的学习方式。 特别是我们将所有的纸笔考试都转换为在线测验。 对于某些学生来说这是一个很大的祝福。 毕竟这些测验并没有比考试更困难因此我们将其设为公开考试。 换句话说我们使课程变得更容易让他们通过。 当然学生遍布世界各地他们无法获得所需的帮助。 此外学生没有像考试那样认真地学习。 这种组合创造了一些非常糟糕的测验分数。 到我们进行第四个测验时学生们已经非常沮丧。 实际上我从几位教师那里听说他们的学生已经厌倦了“技巧性问题”。 作为一名讲师听到这有些令人沮丧因为它们是非常典型的考试问题。 我们并没有为他们增加困难但这是我第一次听到这些抱怨。 示例问题 然后发生了一些奇怪的事情。 我们给了他们一个我真的不知道答案的问题结果有点类似以下内容 以下代码片段后面的Set NaturalNumber nums变量的值是什么 SetNaturalNumber nums new SomeSetImplementation(); nums.add( new NaturalNumber2( 1 )); nums.add( new NaturalNumber2( 5 )); nums.add( new NaturalNumber2( 6 )); for (NaturalNumber n : nums) { n.increment(); } 当然学生的选择如下 nums {156267} nums {267} nums {156} 从提供的信息中无法分辨。 现在就上下文而言此示例中有一些内部组件。 首先NaturalNumber是一个可变的类表示无界的非负整数。 换句话说NaturalNumber的范围可以从零到无穷大。 此外可以使用一系列基本数学运算来修改NaturalNumber如下所示 increment()加1 this add(NaturalNumber n) 将n添加this 此外这个问题让使用的Set是类似于一个数学集合。 这里的想法是Set具有两个主要属性 Set缺少重复项即{121}不是合法集合。 Set是无序的即{123}和{321}是等效的。 作为参考如果您有兴趣详细信息请在课程网站上完整地记录这两个组件 。 所有组件均使用“按合同设计”编写因此每种方法都将包括一个适当的合同其中前置条件用requires表示后置条件用ensures表示。 此外我们使用 restores updates clears和replaces等参数模式标记每个参数。 当然这超出了本文的范围。 解决问题 现在我重申一下我一开始不确定确切的答案。 显然第一个答案即{1、5、6、2、6、7}是错误的因为增加基础值不会为Set添加新值-或我认为。 使用相同的逻辑我还假设第三组即{156}显然是不正确的因为我们显然是在改变基础值。 在这一点上我相当有信心第二个答案即{267}是正确的我的学生中有87也是正确的。 当然我有答案键因此我不得不挑战自己以理解为什么正确答案实际上是最终答案即“无法从提供的信息中分辨出来。”。 现在根据本文的标题您可能已经遥遥领先于我。 没关系 但是我没有立即得出这个结论。 相反我退后一步决定实际绘制Set 。 当然当您尝试这样做时会遇到几个主要问题。 首先正如我之前提到的 Set没有顺序。 结果我们如何推断迭代期间哪个元素优先 我们会尝试所有可能的配置吗 这些是我还没有准备好应对的问题。 幸运的是事实证明按外观顺序进行迭代可以节省很多时间。 看一看 { 1 , 5 , 6 } // Initial state { 2 , 5 , 6 } // After incrementing the first element { 2 , 6 , 6 } // After incrementing the second element 哦哦 我们打破了第一条规则 Set不能包含重复项。 因此我们无法确定结果Set将是什么样。 我的最终答案是D“无法从提供的信息中分辨出来。” 不幸的是这种解释并不令我满意。 就像我知道Set不能包含重复项但是打破该规则的实际后果是什么 换句话说如果情况如此糟糕我们为什么还要授予用户访问基础数据的权限 我认为用户仅应在删除数据后才能访问数据。 总的来说我认为图书馆在这方面做得很好。 如果Set没有实现Iterable 那么我们将Iterable 。 Java迭代器简介 这给我带来了一个甚至更奇怪的问题Java迭代器。 为了使此代码起作用 Set必须实现Iterable这意味着为基础体系结构定义一个Iterator。 现在如果您曾经编写自己的迭代器那么您就需要执行以下操作 new IteratorT() { Override public boolean hasNext() { ... } Override public T next() { ... } Override public void remove() { ... } } 这里基本思想是我们定义某种可以充当惰性数据结构的结构。 如果您熟悉其他语言例如Python的生成器表达式 则有相同的想法我们创建了一个对象该对象可以从一系列项目中一次返回一个项目。 在实践中 Iterator工作方式是继续通过next()方法提供项直到没有返回值为止 这可能永远不会发生 。 在有界序列中我们知道何时停止因为hasNext()方法将返回false 。 这些方法一起可以作为循环机制的核心 while (iter.hasNext()) { T item next(); } 通过使一个类实现Iterable 我们可以利用一些Java语法糖称为for-each循环 for (T item: collection) { ... } Java迭代器警告 在上面定义的问题中我们能够遍历Set因为它实现了Iterable 。 当然仅因为我们能够遍历数据结构并不意味着我们不会遇到任何问题。 毕竟 Iterator类具有一些自己的规则。 也许最重要的规则可以在remove()方法的描述中找到 从基础集合中移除此迭代器返回的最后一个元素可选操作。 每次调用next()只能调用一次此方法。 如果在迭代进行过程中以其他方式而不是通过调用此方法修改了基础集合则未指定迭代器的行为。 Java 8文档 捕获于04/23/2020 记住我曾说过修改NaturalNumber是不好的因为它可能导致重复。 好吧基于此定义修改Set可能会导致无法预测的行为。 当然这对我提出了一个问题 修改基础集合意味着什么。 对于Java集合for-each循环不允许从集合中添加或删除项目。 在这些情况下我们可以期望看到ConcurrentModificationException docs 。 现在该错误并不普遍。 毕竟 Iterator如何知道集合是否已被修改 事实证明该行为是自定义地烘焙到每个集合的next()方法中的。 例如使用List集合 当列表的大小更改时抛出 ConcurrentModificationException 。 换句话说每次调用next()都会检查数据结构的完整性。 由于集合利用泛型类型因此不可能考虑可能出现的所有不同类型的情况。 结果 next()无法检测是否有任何数据在没有跟踪状态的情况下发生了变异。 例如检查列表中是否有任何值更改可能需要存储先前状态的副本并定期检查该先前状态。 那不便宜 更糟糕的是我们还没有真正讨论修改基础数据对实际迭代过程可能产生的影响。 例如如果next()以某种方式依赖于基础数据则对其进行更改显然会更改接下来要执行的操作。 想象一下我们有一个用于列表的Iterator 其项必须实现Comparable 。 然后我们以始终返回已排序顺序的下一个值的方式制作此Iterator 。 如果然后要修改基础值则可以创建一个永远不会遍历整个列表的循环 [ 1 , 2 , 3 ] // next() returns 1 which we scale by 5 [ 5 , 2 , 3 ] // hasNext() claims there are no other values 现在这并不理想。 通常您希望for-each循环实际上遍历整个数据结构而这根本没有做到这一点。 再谈集合问题 在这一点上我们有机会从两个不同的角度来讨论Set问题 如果我们通过生成重复项来使Set无效会发生什么情况 如果我们通过修改基础数据结构使for-each循环无效会发生什么 现在我想借此机会谈谈执行问题代码片段时实际可能发生的情况 SetNaturalNumber nums new SomeSetImplementation(); nums.add( new NaturalNumber2( 1 )); nums.add( new NaturalNumber2( 5 )); nums.add( new NaturalNumber2( 6 )); for (NaturalNumber n : nums) { n.increment(); } 假设Set的Iterator没有花哨的修改检测则大多数人期望的结果可能是相同的Set {267}。 另一个可能的结果是我们得到一个Set 其中仅某些值递增。 就像我之前说过的那样 next()方法可能取决于基础数据来决定接下来要做什么。 在这种情况下我们可能会得到增量输出的任何组合 {256} {166} {157} {266} {257} {167} 无论哪种情况我们都不是完全安全的。 当然 Set看起来一样但是真的一样吗 让我们想象一下该Set是使用哈希表实现的。 这提供了能够快速检查重复项的优点但是需要更多的维护。 例如如果要更改Set的值则必须重新计算哈希并检查冲突。 当我们直接修改NaturalNumber 我们将跳过此维护阶段。 结果我们的哈希表仍将包含原始的三个哈希。 例如当有人检查Set中是否包含两个时该方法将错误地返回false 。 当然这是一个实现细节。 很可能根本没有发现任何问题。 该程序继续平稳运行没有人注意。 但是与所有实现细节一样我们不能依赖于它们的假定行为。 换句话说该程序仍然是不可预测的。 除了未成年人 Set的Java实现实际上指出了这个确切的问题 注意如果将可变对象用作集合元素则必须格外小心。 如果对象的值更改为影响相等比较的方式而该对象是集合中的元素则不指定集合的行为。 此禁止的一种特殊情况是不允许集合将自身包含为元素。 Java Set文档 查看04/24/2020 看起来很难组合一个不存在可变类型问题的Set实现。 我不知道那是关于可变类型的... 什么是外卖 最后我认为Iterator文档的编写方式让用户玩的很好。 换句话说当它说 如果在迭代进行过程中以其他方式而不是通过调用此方法修改了基础集合则未指定迭代器的行为。 它的真正含义是“ 以任何方式” 。 当然我永远无法证实这些怀疑所以我很想看看其他人怎么说。 同时如果您喜欢这篇文章那么如果您借此机会学习如何可以帮助该站点的发展 我将不胜感激。 在该文章中您将了解我的邮件列表以及Patreon。 否则这是一些适合您的相关文章 余数运算符在Java中用于Doubles 复制可变数据类型时要小心 否则感谢您的坚持。 希望我深夜的研究生学习对您有用 翻译自: https://www.javacodegeeks.com/2020/04/be-careful-when-modifying-data-while-using-a-java-iterator.html