关于iterator:Iterator与Generator

Iterator 概念Iterator 提供了一种对立的接口机制,为各种不同数据结构提供对立的拜访机制。定义 Iterator 就是提供一个具备 next() 办法的对象,每次调用 next() 都会返回一个后果对象,该后果对象有两个属性,value 示意以后的值,done 示意遍历是否完结。function makeIterator(Array){ let index = 0;return { next: function(){ return ( Array.length > index ? {value: Array[index++]}: {done: true} ) }}} let iterator = makeIterator(['1','2'])console.log(iterator.next()); // {value: '1'}console.log(iterator.next()); // {value: '2'}console.log(iterator.next()); // {done: true}复制代码Iterator 的作用: 为各种数据结构,提供一个对立的、简便的拜访接口;使得数据结构的成员可能按某种秩序排列;供 for...of 生产 默认 Iterator 接口ES6 提供了 for of 语句遍历迭代器对象,咱们将上述创立的迭代器应用 for of 语句遍历一下:let iterator = makeIterator(['1','2'])for (let value of iterator) { console.log(value);} // iterator is not iterable复制代码后果报错说 iterator is not iterable,这是为什么呢?ES6 规定默认的 Iterator 接口部署在数据结构的 Symbol.iterator 属性中,如果一个数据结构存在 Symbol.iterator 属性,则该数据结构可遍历。咱们将自定义的 makeIterator 革新如下:const MakeIterator = (Array) => ({ ...

August 19, 2022 · 4 min · jiezi

Java-迭代器引发-ConcurrentModificationException

引言ConcurrentModificationException这个异常大家都很熟悉,当在forEach进行删除时都会出现该异常。 如果你还不了解,请参考澍澍的博客:关于在list循环的过程中进行删除的处理 - 晨澍的博客 ConcurrentModificationException的解决方案之一是使用迭代器,但是不代表迭代器就一劳永逸了。 使用的时候还需斟酌数组的索引。 描述问题场景如下图所示: 原来的同步方法获取的节点是节点的父节点,根据父节点进行对应。 然而在同步更新文件的时候,发现这样并不好处理,在不改动原代码的情况下,设计了将列表转为树型结构的方法,这样可以从根节点向下开始遍历,便于操作。 也是在牛客网身经百战,实现这个难度不大。但在编写相关实现的时候,遇到了一个小问题。 迭代器智能吗?第一步,将列表中的根节点找出来。 @Overridepublic ClusterNode listToTree(List<ClusterNode> clusterNodes) { logger.debug("声明根节点名称"); final String ROOT_NAME = "ROOT"; logger.debug("声明根节点"); ClusterNode rootNode = null; logger.debug("获取迭代器,遍历节点列表"); Iterator<ClusterNode> iterator = clusterNodes.iterator(); while (iterator.hasNext()) { logger.debug("向后遍历"); ClusterNode clusterNode = iterator.next(); if (ROOT_NAME.equals(clusterNode.getName())) { logger.debug("获取到根节点,赋值,并从原列表中移除"); rootNode = clusterNode; iterator.remove(); break; } } logger.debug("设置子节点"); assert rootNode != null; setChildrenNode(rootNode, clusterNodes); return rootNode;}第二步,再从根节点开始,递归设置子节点。 /** * 为节点设置符合条件的子节点,同时递归,设置子节点的子节点 * @param parentNode 父节点 * @param clusterNodes 子节点列表 */private void setChildrenNode(ClusterNode parentNode, List<ClusterNode> clusterNodes) { logger.debug("清空原集合"); parentNode.getClusterNodes().clear(); logger.debug("遍历列表"); Iterator<ClusterNode> iterator = clusterNodes.iterator(); while (iterator.hasNext()) { ClusterNode clusterNode = iterator.next(); logger.debug("如果父节点匹配"); if (clusterNode.getParentClusterNode().getName().equals(parentNode.getName())) { logger.debug("将当前节点添加到父节点的子列表中"); parentNode.getClusterNodes().add(clusterNode); logger.debug("移除该节点"); iterator.remove(); logger.debug("递归设置子节点"); setChildrenNode(clusterNode, clusterNodes); } }}思想肯定是没问题的。 ...

July 10, 2019 · 1 min · jiezi

JavaScript-设计模式五迭代器模式

文章内容分两部分: 前半部分为 “迭代器模式” 概念;后半部分为 ES6 中 Iterator (迭代器)上半部分开始... 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。简单理解(白话理解):统一 “集合” 型数据结构的遍历接口,实现可循环遍历获取集合中各数据项(不关心数据项中的数据结构)。 生活小栗子:清单 TodoList。每日清单有学习类、生活类、工作类、运动类等项目,清单列表只管罗列,不管类别。 模式特点为遍历不同数据结构的 “集合” 提供统一的接口;能遍历访问 “集合” 数据中的项,不关心项的数据结构模式实现// 统一遍历接口实现var each = function(arr, callBack) { for (let i = 0, len = arr.length; i < len; i++) { // 将值,索引返回给回调函数callBack处理 if (callBack(i, arr[i]) === false) { break; // 中止迭代器,跳出循环 } }}// 外部调用each([1, 2, 3, 4, 5], function(index, value) { if (value > 3) { return false; // 返回false中止each } console.log([index, value]);})// 输出:[0, 1] [1, 2] [2, 3]“迭代器模式的核心,就是实现统一遍历接口。” ...

July 1, 2019 · 3 min · jiezi

Java中遍历Map对象的四种方法

方式一 这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Map.Entry<Integer, Integer> entry : map.entrySet()) { System.out.println(“Key = " + entry.getKey() + “, Value = " + entry.getValue()); }方法二 在for-each循环中遍历keys或values。如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。Map<Integer, Integer> map = new HashMap<Integer, Integer>(); //遍历map中的键 for (Integer key : map.keySet()) { System.out.println(“Key = " + key); } //遍历map中的值 for (Integer value : map.values()) { System.out.println(“Value = " + value); }方法三使用Iterator遍历使用泛型:Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<Integer, Integer> entry = entries.next(); System.out.println(“Key = " + entry.getKey() + “, Value = " + entry.getValue()); }不使用泛型:Map map = new HashMap(); Iterator entries = map.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Map.Entry) entries.next(); Integer key = (Integer)entry.getKey(); Integer value = (Integer)entry.getValue(); System.out.println(“Key = " + key + “, Value = " + value); }方法四、通过键找值遍历(效率低)Map<Integer, Integer> map = new HashMap<Integer, Integer>(); for (Integer key : map.keySet()) { Integer value = map.get(key); System.out.println(“Key = " + key + “, Value = " + value); ...

March 14, 2019 · 1 min · jiezi

深入理解ES6之迭代器与生成器

迭代器迭代器 iterator,在 Javascript 中,迭代器是一个对象(也可称作为迭代器对象),它提供了一个 next() 方法,用来返回迭代序列中的下一项。next 方法的定义,next 方法是一个函数,执行后返回一个对象包含两个属性:{ done: [boolean], value: [any] }// 创建一个迭代器对象function makeIterator(array) { var nextIndex = 0 return { next() { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true } } }}// iterator 是一个迭代器对象var iterator = makeIterator([10, 20, 30])iterator.next() // {value: 10, done: false}iterator.next() // {value: 20, done: false}iterator.next() // {value: 30, done: false}iterator.next() // {done: true}可迭代对象可迭代对象必须实现一个 @@iterator 方法,也就是说在这个对象或者它的原型链上必须有一个方法名是 Symbol.iterator 的方法,当调用这个方法时它返回一个迭代器对象。可迭代对象的表现形式为,可以使用 for…of 循环,解构赋值,拓展运算符(spread),yield* 这些语法来调用 Symbol.iterator 函数。也就是说这些语法糖在被调用时本质上都是在调用 Symbol.iterator 函数。内置可迭代对象String,Array,TypedArray,Map,Set,函数的arguments对象,NodeList对象都是内置的可迭代对象,他们的原型对象中都有一个 Symbol.iterator 方法。// 可迭代对象let iterable = [10, 20, 30]// 继承自原型链Symbol.iterator in iterable // trueiterable.hasOwnProperty(Symbol.iterator) // falsefor(let value of iterable){ console.log(value)}// 10// 20// 30自定义可迭代对象字面量对象 let o = {} 默认没有 Symbol.iterator 方法,但是我们在对象上自定义一个 @@iterator 方法,此时字面量对象也可以使用 for…of循环,拓展运算符等等语法糖。// 字面量对象默认是不可迭代对象// 自定义对var myIterable = {}myIterable[Symbol.iterator] = function(){ return { arr: [10, 20, 30], next: function(){ if(this.arr.length > 0){ return {value: this.arr.shift(), done: false} }else{ return {done: true} } } }}[…myIterable] // [10, 20, 30]生成器生成器 generator,在 Javascript 中生成器是一个函数(也可称作生成器函数),它可以作为创建迭代器的工厂函数。生成器函数的返回值是一个迭代器对象,同时这个对象也是一个可迭代对象。funtion* 这种声明方式可以定义一个生成器函数。生成器函数的语法规则是,调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器 (iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。调用 next()方法时,如果传入了参数,那么这个参数会作为上一条执行的 yield 语句的返回值。// 生成器函数function* generator(i){ yield i + 1 var y = yield ‘foo’ yield y}var iterator = generator(10) // 此时生成器函数不执行,返回一个迭代器iterator.next() // {value: 11, done: false} iterator.next() // {value ‘foo’, done: false}iterator.next(10) // {value: 10, done: false},将10赋值给上一条 yield ‘foo’ 左侧的值,即 y = 10,返回 yiterator.next() // {done: true}既然生成器函数可以创建迭代器对象,我们来试着将前面的例子用生成器函数的形式重写试试看。// 生成器函数function* makeIterator(array) { for (let i = 0; i < array.length; i++) { yield array[i] }}// 迭代器对象,实现和上文一样的功能var iteratorByGenerator = makeIterator([10, 20, 30])iteratorByGenerator.next() // {value: 10, done: false}iteratorByGenerator.next() // {value: 20, done: false}iteratorByGenerator.next() // {value: 30, done: false}iteratorByGenerator.next() // {done: true}从上面的代码我们可以看到,利用生成器函数来创建一个迭代器对象的方式相比于之前我们普通函数创建的方式更加简洁,也更加清晰的表明调用生成器函数返回的是一个迭代器对象。除此之外还有什么区别呢。上文已经提到,生成器函数返回的是一个可迭代的迭代器对象,这是什么意思呢?看下代码就明白了。// 生成器函数创建的迭代器对象Symbol.iterator in iteratorByGenerator // true[…iteratorByGenerator] // [10, 20, 30]// 普通函数创建的迭代器对象Symbol.iterator in iterator // false[…iterator] // Uncaught TypeError: iterator is not iterable综上所述,我们可以确定的说生成器函数是创建迭代器对象的语法糖,通过生成器函数我们可以用很简洁清晰的语法创建一个可迭代的迭代器对象。 ...

December 27, 2018 · 2 min · jiezi