精华内容
下载资源
问答
  • Map, List<UserDto>> map = new HashMap, List<UserDto>>(); String key = ""; for (int i = 0 ;i (); i++) { key = userErrorList.get(i).getUserId(); List<UserDto> userListDto = new ...
  • private Map&lt;String,List&lt;Selectopts&gt;&gt; map2 = new HashMap&lt;String,List&lt;Selectopts&gt;&gt;(); List&lt;Selectopts&gt; selectoptsVOList1 = new ...
    private Map<String,List<Selectopts>> map2 = new HashMap<String,List<Selectopts>>();
    
    List<Selectopts> selectoptsVOList1 = new ArrayList<Selectopts>();
    		List<Selectopts> selectoptsVOList2 = new ArrayList<Selectopts>();
    		selectoptsVOList1 = this.gwlcmxService.getSelectopts("mxlx");
    		selectoptsVOList2 = this.gwlcmxService.getSelectopts("fmdl");
    		map2.put("mxlx", selectoptsVOList1);
    		map2.put("fmdl", selectoptsVOList2);
    
    

     

    <tr>
           <td width="10%">模型分类</td>
           <td width="35%">
               <select id="mxflid" name="gwlcmxVO.mxflid" style="width: 200px;border:none;">
    		<option value="">====请选择====</option>
    		<c:forEach var="data" items="${map2.mxlx}" varStatus="status">
    		    <option value="${data.code_id }">${data.code_name }</option>
    		</c:forEach>
    	    </select><span id="inspntnumqymcSpan" class="star">*</span>
    	</td>
    	<td width="10%">公文表单模板</td>
    	<td width="35%">
                <select id="fmdlid" name="gwlcmxVO.gwfmdlid" style="width: 200px;border:none;">
                    <option value="">====请选择====</option>
                        <c:forEach var="data" items="${map2.fmdl}" varStatus="status">
    		    <option value="${data.code_id }">${data.code_name }</option>
    		</c:forEach>
    	    </select><span id="inspntnumqymcSpan" class="star">*</span>
    	</td>
    	</tr>

     

    展开全文
  • 很遗憾的是到目前为止,没有找到一个具体的例子能回答这个问题。这篇文章中我们讨论不用任何循环如何处理 JavaScript 数组,最终得出的效果是可以降低代码复杂性。 循环是一种很重要的控制结构,它很难被重用,也很...
  • 多重嵌套 for 循环太丑,运行效率又低,有没有什么办法可以降低它的嵌套层数?游戏内怎么简洁优雅的...先看一个简单的例子,数组去重:let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) {let newArra...
    多重嵌套 for 循环太丑,运行效率又低,有没有什么办法可以降低它的嵌套层数?游戏内怎么简洁优雅的获取我想要的数据,又有良好的智能提示效果?有没有通用的提高运行效率的方案?没错,ES6 的 Map 就是为你定制的。先看一个简单的例子,数组去重:
    let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) {let newArray = [];for (let i = 0, l = array.length; i < l; i++) {for (let j = i + 1; j < l; j++) {if (array[i] === array[j]) {                j = ++i;            }        }        newArray.push(array[i]);    }return newArray;}console.warn(getArray(myArray));
    上面用了最基本的2重 for 循环遍历数组达到去重的效果,代码运行效率低,时间复杂度达到了O(n2),如何优化成一重for循环呢?用 Map 试一试!Map 是 ES6 新出的一种数据结构,用来表示键值对,object也是键值对结构,Map算是对object的一种加强,object的键只能为string,Map的键可以为任意值。我们用Map来存储中间遍历的值,从而可以达到一轮for循环就完成去重的目的,如下:
    let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) {    let newArray = [];    let map = new Map();for (let i = 0, l = array.length; i < l; i++) {if(typeof map.get(array[i]) == "undefined"){map.set(array[i], array[i]);            newArray.push(array[i]);        }    }return newArray;}console.warn(getArray(myArray));
    很明显,上面通过 Map 减少了一层for循环,将时间复杂度降到了O(n),提高了运行效率。Map这种键值对结构(字典,哈希表),可以直接根据键名找到对应的值,不需要遍历,大大提高了查找的效率。我们常用的对象和数组结构也有同样的效果,但是Map功能更强大,配合ts的智能提示效果更好。理论分析:为什么用 Map 能提高运行效率呢,仔细思考,发现,这个里面蕴含了一个重要的计算机理论知识——空间换时间!计算机理论里面,对于一个算法,他的时间负责度和空间复杂度是对立的。简单的理解就是,占用空间多,运行就快,占用空间少,运行就慢。上面的 Map 就是开辟的额外存储空间,用来保存一些中间状态,从而将2重 for 循环降低为1重 for 循环,降低了运行时间。Map的强大还体现在他的充足的智能提示的地方:e9f72d00c24b7f3f43dd3dcaaad1f7b7.png上面通过声明键和值的类型,当调用对应的 API 时候,会对参数和返回类型有一个提示作用,这是对象和数组没有的优势,而且 Map 的 key 也可以为对象等任意类型,这在有些地方也能起到意向不到的方便作用。所以游戏中,凡是想提高运行效率,想降低查找等时间消耗的地方,都请尽可能的用Map来解决吧,这一定是一个百试不爽的万精油方案,而且是一个一定能有效果的方案,因为背后有理论知识支撑着他258eee59cd4e084e421c07052656ac74.pngafee5601ea87c557051d40d46ebfda5e.png关注公众号,教你更优雅简洁的开发游戏,write less,do more!
    展开全文
  • ![图片说明](https://img-ask.csdn.net/upload/201909/05/1567647465_126538.png) 这HashMap存的map的值循环一次就会被覆盖,应该怎么解决呢??? 能让它存入每一次put的值。
  • 最近在java 项目上碰到mybatis 循环添加一个问题 先看代码)如果把传进去的值为,long>类型则没有问题 如果是map,long>这会拿不到值 不知道怎么回事 求大神解决![图片说明]...
  • 好兄弟萌,从今天开始呢,每天九点就开始分享些技术文章,希望我们一起可以讨论下,营造一下技术氛围;...那么集合有多少种,我们从两维度去说它。Collection 和 Map 两种。Collection 存储着对象的集合,而...

          好兄弟萌,从今天开始呢,每天九点就开始分享些技术文章,希望我们一起可以讨论下,营造一下技术氛围;

          说到集合可以在你的脑海里面浮现出集中数据结构,我觉得java跟数学很相似,也可以用数形结合的思想去理解学习它。我们可以想到链表,数组,红黑树等等;那么集合有多少种,我们从两个维度去说它。 Collection 和 Map 两种。Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。

    45191fc52c3507372c2a59f983e9aa71.png

    Set

        1、TreeSet:基于红黑树实现,支持有序性操作,例如:根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
         2、HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
        3、LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。

          到这里,我们一般会听到这样的说过:你在项目里面用过这些集合吗?分布式的应用凡是用到map这种一般都是会出现问题的,因为map里面存放的数据是在各自的java内存当中的,并不能保证线程安全。所以有了像redis这样的缓存中间件。因为redis的设计从6.0之后是网络单线程,IO多线程的;它是单线程的,能够保证线程安全。我们熟知的redis五种数据结构: string、hash、链表、Set(集合)、Zset(有序集合)。

    List
         1、ArrayList:基于动态数组实现,支持随机访问。
         2、Vector:和 ArrayList 类似,但它是线程安全的。
         3、LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。

    Map
          1、 TreeMap:基于红黑树实现。
          2、 HashMap:基于哈希表实现。
         3、 HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
          4、LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

    我们从网上百度几个高频面试题。

     ArrayList 和 LinkedList 的区别?

        一般地,我们在对比容器的时候,首先会想到是否是线程安全的,数据结构,时间复杂度和空间复杂度;

           是否保证线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

          底层数据结构:Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别)

           插入和删除是否受元素位置的影响:① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。

           是否支持快速随机访问:LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

           内存空间占用:ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。

    注意:

          ArrayList 的增删未必就是比 LinkedList 要慢:

          如果增删都是在末尾来操作【每次调用的都是 remove() 和 add()】,此时 ArrayList 就不需要移动和复制数组来进行操作了。如果数据量有百万级的时,速度是会比 LinkedList 要快的。

         如果删除操作的位置是在中间。由于 LinkedList 的消耗主要是在遍历上,ArrayList 的消耗主要是在移动和复制上(底层调用的是 arrayCopy() 方法,是 native 方法)。LinkedList 的遍历速度是要慢于 ArrayList 的复制移动速度的如果数据量有百万级的时,还是 ArrayList 要快。

    ArrayList 实现 RandomAccess 接口有何作用?为何 LinkedList 却没实现这个接口?

           RandomAccess 接口只是一个标志接口,只要 List 集合实现这个接口,就能支持快速随机访问。通过查看 Collections 类中的 binarySearch() 方法,可以看出,判断 List 是否实现 RandomAccess 接口来实行indexedBinarySerach(list, key) 或 iteratorBinarySerach(list, key)方法。再通过查看这两个方法的源码发现:实现 RandomAccess 接口的 List 集合采用一般的 for 循环遍历,而未实现这接口则采用迭代器,即 ArrayList 一般采用 for 循环遍历,而 LinkedList 一般采用迭代器遍历;

         ArrayList 用 for 循环遍历比 iterator 迭代器遍历快,LinkedList 用 iterator 迭代器遍历比 for 循环遍历快。所以说,当我们在做项目时,应该考虑到 List 集合的不同子类采用不同的遍历方式,能够提高性能。

    ArrayList 的扩容机制?

         当使用 add 方法的时候首先调用 ensureCapacityInternal 方法,传入 size+1 进去,检查是否需要扩充 elementData 数组的大小;

           newCapacity = 扩充数组为原来的 1.5 倍(不能自定义),如果还不够,就使用它指定要扩充的大小 minCapacity ,然后判断 minCapacity 是否大于 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8) ,如果大于,就取 Integer.MAX_VALUE;

           扩容的主要方法:grow;

         ArrayList 中 copy 数组的核心就是 System.arraycopy 方法,将 original 数组的所有数据复制到 copy 数组中,这是一个本地方法。

    Array 和 ArrayList 有何区别?什么时候更适合用 Array?

    • Array 可以容纳基本类型和对象,而 ArrayList 只能容纳对象;

    • Array 是指定大小的,而 ArrayList 大小是固定的。

    什么时候更适合使用 Array:

    • 如果列表的大小已经指定,大部分情况下是存储和遍历它们;

    • 对于遍历基本数据类型,尽管 Collections 使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢;

    • 如果你要使用多维数组,使用 [ ][ ] 比 List> 更容易。

    HashMap 的实现原理/底层数据结构?JDK1.7 和 JDK1.8

    JDK1.7:Entry数组 + 链表
    JDK1.8:Node 数组 + 链表/红黑树,当链表上的元素个数超过 8 个并且数组长度 >= 64 时自动转化成红黑树,节点变成树节点,以提高搜索效率和插入效率到 O(logN)。Entry 和 Node 都包含 key、value、hash、next 属性。

    HashMap 的 put 方法的执行过程?

         当我们想往一个 HashMap 中添加一对 key-value 时,系统首先会计算 key 的 hash 值,然后根据 hash 值确认在 table 中存储的位置。若该位置没有元素,则直接插入。否则迭代该处元素链表并依次比较其 key 的 hash 值。如果两个 hash 值相等且 key 值相等(e.hash == hash && ((k = e.key) == key || key.equals(k))),则用新的 Entry 的 value 覆盖原来节点的 value。如果两个 hash 值相等但 key 值不等 ,则将该节点插入该链表的链头。

    HashMap 的 get 方法的执行过程?

            通过 key 的 hash 值找到在 table 数组中的索引处的 Entry,然后返回该 key 对应的 value 即可。

    在这里能够根据 key 快速的取到 value 除了和 HashMap 的数据结构密不可分外,还和 Entry 有莫大的关系。HashMap 在存储过程中并没有将 key,value 分开来存储,而是当做一个整体 key-value 来处理的,这个整体就是Entry 对象。同时 value 也只相当于 key 的附属而已。在存储的过程中,系统根据 key 的 HashCode 来决定 Entry 在 table 数组中的存储位置,在取的过程中同样根据 key 的 HashCode 取出相对应的 Entry 对象(value 就包含在里面)。

    HashMap 的 resize 方法的执行过程?

    有两种情况会调用 resize 方法:

          第一次调用 HashMap 的 put 方法时,会调用 resize 方法对 table 数组进行初始化,如果不传入指定值,默认大小为 16。

          扩容时会调用 resize,即 size > threshold 时,table 数组大小翻倍。每次扩容之后容量都是翻倍扩容后要将原数组中的所有元素找到在新数组中合适的位置。

          当我们把 table[i] 位置的所有 Node 迁移到 newtab 中去的时候:这里面的 node 要么在 newtab 的 i 位置(不变),要么在 newtab 的 i + n 位置。也就是我们可以这样处理:把 table[i] 这个桶中的 node 拆分为两个链表 l1 和 l2:如果 hash & n == 0,那么当前这个 node 被连接到 l1 链表;否则连接到 l2 链表。这样下来,当遍历完 table[i] 处的所有 node 的时候,我们得到两个链表 l1 和 l2,这时我们令 newtab[i] = l1,newtab[i + n] = l2,这就完成了 table[i] 位置所有 node 的迁移(rehash),这也是 HashMap 中容量一定的是 2 的整数次幂带来的方便之处。

    HashMap 的 size 为什么必须是 2 的整数次方?

          这样做总是能够保证 HashMap 的底层数组长度为 2 的 n 次方。当 length 为 2 的 n 次方时,h & (length - 1) 就相当于对 length 取模,而且速度比直接取模快得多,这是 HashMap 在速度上的一个优化。而且每次扩容时都是翻倍。

          如果 length 为 2 的次幂,则 length - 1 转化为二进制必定是 11111……的形式,在与 h 的二进制进行与操作时效率会非常的快,而且空间不浪费。但是,如果 length 不是 2 的次幂,比如:length 为 15,则 length - 1 为 14,对应的二进制为 1110,在于 h 与操作,最后一位都为 0 ,而 0001,0011,0101,1001,1011,0111,1101 这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率,这样就会造成空间的浪费。

    HashMap 的 get 方法能否判断某个元素是否在 map中?

          HashMap 的 get 函数的返回值不能判断一个 key 是否包含在 map 中,因为 get 返回 null 有可能是不包含该 key,也有可能该 key 对应的 value 为 null。因为 HashMap 中允许 key 为 null,也允许 value 为 null。

    HashMap 与 HashTable 的区别是什么?

    • HashTable 基于 Dictionary 类,而 HashMap 是基于 AbstractMap。Dictionary 是任何可将键映射到相应值的类的抽象父类,而 AbstractMap 是基于 Map 接口的实现,它以最大限度地减少实现此接口所需的工作。

    • HashMap 的 key 和 value 都允许为 null,而 Hashtable 的 key 和 value 都不允许为 null。HashMap 遇到 key 为 null 的时候,调用 putForNullKey 方法进行处理,而对 value 没有处理;Hashtable 遇到 null,直接返回 NullPointerException。

    • Hashtable 是线程安全的,而 HashMap 不是线程安全的,但是我们也可以通过 Collections.synchronizedMap(hashMap),使其实现同步。

    HashTable的补充:
    HashTable 和 HashMap 的实现原理几乎一样,差别无非是
    1、HashTable 不允许 key 和 value 为 null;
    2、HashTable 是线程安全的。但是 HashTable 线程安全的策略实现代价却太大了,简单粗暴,get/put 所有相关操作都是 synchronized 的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差。

    HashMap 与 ConcurrentHashMap 的区别是什么?

        HashMap 不是线程安全的,而 ConcurrentHashMap 是线程安全的。

    ConcurrentHashMap 采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段 segment,而且每个小的片段 segment 上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段 segment,然后再在这个片段上面进行插入,而且这里还需要获取 segment 锁,这样做明显减小了锁的粒度。

    HashTable 和 ConcurrentHashMap 的区别?

           HashTable 和 ConcurrentHashMap 相比,效率低。Hashtable 之所以效率低主要是使用了 synchronized 关键字对 put 等操作进行加锁,而 synchronized 关键字加锁是对整张 Hash 表的,即每次锁住整张表让线程独占,致使效率低下,而 ConcurrentHashMap 在对象中保存了一个 Segment 数组,即将整个 Hash 表划分为多个分段;而每个Segment元素,即每个分段则类似于一个Hashtable;这样,在执行 put 操作时首先根据 hash 算法定位到元素属于哪个 Segment,然后对该 Segment 加锁即可,因此, ConcurrentHashMap 在多线程并发编程中可是实现多线程 put操作。

    ConcurrentHashMap 的实现原理是什么?

    数据结构
    JDK 7:中 ConcurrentHashMap 采用了数组 + Segment + 分段锁的方式实现。
    JDK 8:中 ConcurrentHashMap 参考了 JDK 8 HashMap 的实现,采用了数组 + 链表 + 红黑树的实现方式来设计,内部大量采用 CAS 操作。

    ConcurrentHashMap 采用了非常精妙的"分段锁"策略,ConcurrentHashMap 的主干是个 Segment 数组。

    final  Segment[]  segments;
    • 1

    Segment 继承了 ReentrantLock,所以它就是一种可重入锁(ReentrantLock)。在 ConcurrentHashMap,一个 Segment 就是一个子哈希表,Segment 里维护了一个 HashEntry 数组,并发环境下,对于不同 Segment 的数据进行操作是不用考虑锁竞争的。就按默认的 ConcurrentLevel 为 16 来讲,理论上就允许 16 个线程并发执行。

    所以,对于同一个 Segment 的操作才需考虑线程同步,不同的 Segment 则无需考虑。Segment 类似于 HashMap,一个 Segment 维护着一个HashEntry 数组:

    transient  volatile  HashEntry[]  table;
    • 1

    HashEntry 是目前我们提到的最小的逻辑处理单元了。一个 ConcurrentHashMap 维护一个 Segment 数组,一个 Segment 维护一个 HashEntry 数组。因此,ConcurrentHashMap 定位一个元素的过程需要进行两次 Hash 操作。第一次 Hash 定位到 Segment,第二次 Hash 定位到元素所在的链表的头部。

    HashSet 的实现原理?

           HashSet 的实现是依赖于 HashMap 的,HashSet 的值都是存储在 HashMap 中的。在 HashSet 的构造法中会初始化一个 HashMap 对象,HashSet 不允许值重复。因此,HashSet 的值是作为 HashMap 的 key 存储在 HashMap 中的,当存储的值已经存在时返回 false。

    HashSet 怎么保证元素不重复的?

    public boolean add(E e) { 
    return map.put(e, PRESENT)==null;
    }
    • 1

    • 2

    • 3

    元素值作为的是 map 的 key,map 的 value 则是 PRESENT 变量,这个变量只作为放入 map 时的一个占位符而存在,所以没什么实际用处。其实,这时候答案已经出来了:HashMap 的 key 是不能重复的,而这里HashSet 的元素又是作为了 map 的 key,当然也不能重复了。

    LinkedHashMap 的实现原理?

            LinkedHashMap 也是基于 HashMap 实现的,不同的是它定义了一个 Entry header,这个 header 不是放在 Table 里,它是额外独立出来的。

           LinkedHashMap 通过继承 hashMap 中的 Entry,并添加两个属性 Entry before,after 和 header 结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。

           LinkedHashMap 定义了排序模式 accessOrder,该属性为 boolean 型变量,对于访问顺序,为 true;对于插入顺序,则为 false。一般情况下,不必指定排序模式,其迭代顺序即为默认为插入顺序。

    Iterator 怎么使用?有什么特点?

    迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。Java 中的 Iterator 功能比较简单,并且只能单向移动:

    • 使用方法 iterator() 要求容器返回一个 Iterator。第一次调用 Iterator 的 next() 方法时,它返回序列的第一个元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 继承。

    • 使用 next() 获得序列中的下一个元素。

    • 使用 hasNext() 检查序列中是否还有元素。

    • 使用 remove() 将迭代器新返回的元素删除。

     Iterator 和 ListIterator 有什么区别?

    Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List。Iterator 对集合只能是前向遍历,ListIterator 既可以前向也可以后向。ListIterator 实现了 Iterator 接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引等等。

    Iterator 和 Enumeration 接口的区别?

    与 Enumeration 相比,Iterator 更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。否则会抛出 ConcurrentModificationException 异常。这其实就是 fail-fast 机制。具体区别有三点:

    • Iterator 的方法名比 Enumeration 更科学;

    • Iterator 有 fail-fast 机制,比 Enumeration 更安全;

    • Iterator 能够删除元素,Enumeration 并不能删除元素。

    fail-fast 与 fail-safe 有什么区别?

           Iterator 的 fail-fast 属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util 包中的所有集合类都被设计为 fail-fast 的,而 java.util.concurrent 中的集合类都为 fail-safe 的。当检测到正在遍历的集合的结构被改变时,fail-fast 迭代器抛出ConcurrentModificationException,而 fail-safe 迭代器从不抛出 ConcurrentModificationException。

    Collection 和 Collections 有什么区别?

          Collection:是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素。它的直接继承接口有 List,Set 和 Queue。

          Collections:是不属于 Java 的集合框架的,它是集合类的一个工具类/帮助类。此类不能被实例化, 服务于 Java 的 Collection 框架。它包含有关集合操作的静态多态方法,实现对各种集合的搜索、排序、线程安全等操作。

    1c32f5454f08fd5f102aabc89985a2b0.png

    展开全文
  • 很遗憾的是到目前为止,没有找到一个具体的例子能回答这个问题。这篇文章中我们讨论不用任何循环如何处理 JavaScript 数组,最终得出的效果是可以降低代码复杂性。循环是一种很重要的控制结构,它很难被重用,也很难...

    之前有讨论过,缩进(非常粗鲁地)增加了代码复杂性。我们的目标是写出复杂度低的 JavaScript 代码。通过选择一种合适的抽象来解决这个问题,可是你怎么能知道选择哪一种抽象呢?很遗憾的是到目前为止,没有找到一个具体的例子能回答这个问题。这篇文章中我们讨论不用任何循环如何处理 JavaScript 数组,最终得出的效果是可以降低代码复杂性。

    循环是一种很重要的控制结构,它很难被重用,也很难插入到其他操作之中。另外,它意味着随着每次迭代,代码也在不断的变化之中。——Luis Atencio

    我们先前说过,像循环这样的控制结构引入了复杂性。但是也没有给出确切的证据证明这一点,我们先看看 JavaScript 中循环的工作原理。

    循环

    在 JavaScript 中,至少有四、五种实现循环的方法,最基础的是 while 循环。我们首先先创建一个示例函数和数组:

    // oodlify :: String -> String

    function oodlify(s) {

    return s.replace(/[aeiou]/g, 'oodle');

    }

    const input = [

    'John',

    'Paul',

    'George',

    'Ringo',

    ];

    现在有了一个数组,我们想要用 oodlify 函数处理每一个元素。如果用 while 循环,就类似于这样:

    let i = 0;

    const len = input.length;

    let output = [];

    while (i < len) {

    let item = input[i];

    let newItem = oodlify(item);

    output.push(newItem);

    i = i + 1;

    }

    注意这里发生的事情,我们用了一个初始值为 0 的计数器 i,每次循环都会自增。而且每次循环中都和 len 进行比较以保证循环特定次数以后终止循环。这种利用计数器进行循环控制的模式太常用了,所以 JavaScript 提供了一种更加简洁的写法: for 循环,写起来如下:

    const len = input.length;

    let output = [];

    for (let i = 0; i < len; i = i + 1) {

    let item = input[i];

    let newItem = oodlify(item);

    output.push(newItem);

    }

    这一结构非常有用,while循环非常容易把自增的 i 给忘掉,进而引起无限循环;而for循环把和计数器相关的代码都放到了上面,这样你就不会忘掉自增 i,这确实是一个很好的改进。现在回到原来的问题,我们目标是在数组的每个元素上运行 oodlify() 函数,并且将结果放到一个新的数组中。

    对一个数组中每个元素都进行操作的这种模式也是非常普遍的。因此在 ES2015 中,引入了一种新的循环结构可以把计数器也简化掉: for...of 循环。每一次返回数组的下一个元素给你,代码如下:

    let output = [];

    for (let item of input) {

    let newItem = oodlify(item);

    output.push(newItem);

    }

    这样就清晰很多了,注意这里计数器和比较都不用了,你甚至都不用把元素从数组里面取出来。for...of 帮我们做了里面的脏活累活。如果现在用 for...of 来代替所有的 for 循环,其实就可以很大程度上降低复杂性。但是,我们还可以做进一步的优化。

    mapping

    for...of 循环比 for 循环更清晰,但是依然需要一些配置性的代码。如不得不初始化一个 output 数组并且每次循环都要调用 push() 函数。但有办法可以让代码更加简洁有力,我们先扩展一下问题。

    如果有两个数组需要调用 oodlify 函数会怎么样?

    const fellowship = [

    'frodo',

    'sam',

    'gandalf',

    'aragorn',

    'boromir',

    'legolas',

    'gimli',

    ];

    const band = [

    'John',

    'Paul',

    'George',

    'Ringo',

    ];

    很容易想到的方法是对每个数组都做循环:

    let bandoodle = [];

    for (let item of band) {

    let newItem = oodlify(item);

    bandoodle.push(newItem);

    }

    let floodleship = [];

    for (let item of fellowship) {

    let newItem = oodlify(item);

    floodleship.push(newItem);

    }

    这确实ok,有能正确执行的代码,就比没有好。但是重复的代码太多了——不够“DRY”。我们来重构它以降低重复性,创建一个函数:

    function oodlifyArray(input) {

    let output = [];

    for (let item of input) {

    let newItem = oodlify(item);

    output.push(newItem);

    }

    return output;

    }

    let bandoodle = oodlifyArray(band);

    let floodleship = oodlifyArray(fellowship);

    这看起来好多了,可是如果我们想使用另外一个函数该怎么办?

    function izzlify(s) {

    return s.replace(/[aeiou]+/g, 'izzle');

    }

    上面的 oodlifyArray() 一点用都没有了。但如果再创建一个 izzlifyArray() 函数的话,代码又重复了。不管那么多,先写出来看看什么效果:

    function oodlifyArray(input) {

    let output = [];

    for (let item of input) {

    let newItem = oodlify(item);

    output.push(newItem);

    }

    return output;

    }

    function izzlifyArray(input) {

    let output = [];

    for (let item of input) {

    let newItem = izzlify(item);

    output.push(newItem);

    }

    return output;

    }

    这两个函数惊人的相似。那么是不是可以把它们抽象成一个通用的模式呢?我们想要的是:给定一个函数和一个数组,通过这个函数,把数组中的每一个元素做操作后放到新的数组中。我们把这个模式叫做 map 。一个数组的 map 函数如下:

    function map(f, a) {

    let output = [];

    for (let item of a) {

    output.push(f(item));

    }

    return output;

    }

    这里还是用了循环结构,如果想要完全摆脱循环的话,可以做一个递归的版本出来:

    function map(f, a) {

    if (a.length === 0) { return []; }

    return [f(a[0])].concat(map(f, a.slice(1)));

    }

    递归解决方法非常优雅,仅仅用了两行代码,几乎没有缩进。但是通常并不提倡于在这里使用递归,因为在较老的浏览器中的递归性能非常差。实际上,map 完全不需要你自己去手动实现(除非你自己想写)。map 模式很常用,因此 JavaScript 提供了一个内置 map 方法。使用这个 map 方法,上面的代码变成了这样:

    let bandoodle = band.map(oodlify);

    let floodleship = fellowship.map(oodlify);

    let bandizzle = band.map(izzlify);

    let fellowshizzle = fellowship.map(izzlify);

    可以注意到,缩进消失,循环消失。当然循环可能转移到了其他地方,但是我们已经不需要去关心它们了。现在的代码简洁有力,完美。

    为什么这个代码这么简单呢?这可能是个很傻的问题,不过也请思考一下。是因为短吗?不是,简洁并不代表不复杂。它的简单是因为我们把问题分离了。有两个处理字符串的函数: oodlify 和 izzlify,这些函数并不需要知道关于数组或者循环的任何事情。同时,有另外一个函数:map ,它来处理数组,它不需要知道数组中元素是什么类型的,甚至你想对数组做什么也不用关心。它只需要执行我们所传递的函数就可以了。把对数组的处理中和对字符串的处理分离开来,而不是把它们都混在一起。这就是为什么说上面的代码很简单。

    reducing

    现在,map 已经得心应手了,但是这并没有覆盖到每一种可能需要用到的循环。只有当你想创建一个和输入数组同样长度的数组时才有用。但是如果你想要向数组中增加几个元素呢?或者想找一个列表中的最短字符串是哪个?其实有时我们对数组进行处理,最终只想得到一个值而已。

    来看一个例子,现在一个数组里面存放了一堆超级英雄:

    const heroes = [

    {name: 'Hulk', strength: 90000},

    {name: 'Spider-Man', strength: 25000},

    {name: 'Hawk Eye', strength: 136},

    {name: 'Thor', strength: 100000},

    {name: 'Black Widow', strength: 136},

    {name: 'Vision', strength: 5000},

    {name: 'Scarlet Witch', strength: 60},

    {name: 'Mystique', strength: 120},

    {name: 'Namora', strength: 75000},

    ];

    现在想找最强壮的超级英雄。使用 for...of 循环,像这样:

    let strongest = {strength: 0};

    for (hero of heroes) {

    if (hero.strength > strongest.strength) {

    strongest = hero;

    }

    }

    虽然这个代码可以正确运行,可是实在太烂了。看这个循环,每次都保存到目前为止最强的英雄。继续提需求,接下来我们想要所有超级英雄的总强度:

    let combinedStrength = 0;

    for (hero of heroes) {

    combinedStrength += hero.strength;

    }

    在这两个例子中,都在循环开始之前初始化了一个变量。然后在每一次的循环中,处理一个数组元素并且更新这个变量。为了使这种循环套路变得更加明显一点,现在把数组中间的部分抽离到一个函数当中。并且重命名这些变量,以进一步突出相似性。

    function greaterStrength(champion, contender) {

    return (contender.strength > champion.strength) ? contender : champion;

    }

    function addStrength(tally, hero) {

    return tally + hero.strength;

    }

    const initialStrongest = {strength: 0};

    let working = initialStrongest;

    for (hero of heroes) {

    working = greaterStrength(working, hero);

    }

    const strongest = working;

    const initialCombinedStrength = 0;

    working = initialCombinedStrength;

    for (hero of heroes) {

    working = addStrength(working, hero);

    }

    const combinedStrength = working;

    用这种方式来写,两个循环变得非常相似了。它们两个之间唯一的区别是调用的函数和初始值不同。两个的功能都是对数组进行处理,最终得到一个值。所以,我们创建一个 reduce 函数来封装这个模式。

    function reduce(f, initialVal, a) {

    let working = initialVal;

    for (item of a) {

    working = f(working, item);

    }

    return working;

    }

    reduce 模式在 JavaScript 中也是很常用的,因此 JavaScript 为数组提供了内置的方法,不需要自己来写。通过内置方法,代码就变成了:

    const strongestHero = heroes.reduce(greaterStrength, {strength: 0});

    const combinedStrength = heroes.reduce(addStrength, 0);

    ok,如果足够细心的话,你会注意到上面的代码其实并没有短很多。不过也确实比自己手写的 reduce 代码少写了几行。但是我们的目标并不是使代码变短或者少写,而是降低代码复杂度。现在的复杂度降低了吗?我会说是的。把处理每个元素的代码和处理循环代码分离开来了,这样代码就不会互相纠缠在一起了,降低了复杂度。

    reduce 方法乍一看可能觉得非常基础。我们举的 reduce 大部分也比如做加法这样的简单例子。但是没有人说 reduce 方法只能返回基本类型,它可以是一个 object 类型,甚至可以是另一个数组。当我第一次意识到这个问题的时候,自己也是豁然开朗。所以其实可以用 reduce 方法来实现 map 或者 filter,这个留给读者自己做练习。

    filtering

    现在我们有了 map 处理数组中的每个元素,有了 reduce 可以处理数组最终得到一个值。但是如果想获取数组中的某些元素该怎么办?我们来进一步探索,现在增加一些属性到上面的超级英雄数组中:

    const heroes = [

    {name: 'Hulk', strength: 90000, sex: 'm'},

    {name: 'Spider-Man', strength: 25000, sex: 'm'},

    {name: 'Hawk Eye', strength: 136, sex: 'm'},

    {name: 'Thor', strength: 100000, sex: 'm'},

    {name: 'Black Widow', strength: 136, sex: 'f'},

    {name: 'Vision', strength: 5000, sex: 'm'},

    {name: 'Scarlet Witch', strength: 60, sex: 'f'},

    {name: 'Mystique', strength: 120, sex: 'f'},

    {name: 'Namora', strength: 75000, sex: 'f'},

    ];

    ok,现在有两个问题,我们想要:

    找到所有的女性英雄;

    找到所有能量值大于500的英雄。

    使用普通的 for...of 循环,会得到如下代码:

    let femaleHeroes = [];

    for (let hero of heroes) {

    if (hero.sex === 'f') {

    femaleHeroes.push(hero);

    }

    }

    let superhumans = [];

    for (let hero of heroes) {

    if (hero.strength >= 500) {

    superhumans.push(hero);

    }

    }

    逻辑严密,看起来还不错?但是里面又出现了重复的情况。实际上,区别在于 if 的判断语句,那么能不能把 if 语句重构到一个函数中呢?

    function isFemaleHero(hero) {

    return (hero.sex === 'f');

    }

    function isSuperhuman(hero) {

    return (hero.strength >= 500);

    }

    let femaleHeroes = [];

    for (let hero of heroes) {

    if (isFemaleHero(hero)) {

    femaleHeroes.push(hero);

    }

    }

    let superhumans = [];

    for (let hero of heroes) {

    if (isSuperhuman(hero)) {

    superhumans.push(hero);

    }

    }

    这种只返回 true 或者 false 的函数,我们一般把它称作断言(predicate)函数。这里用了断言(predicate)函数来判断是否需要保留当前的英雄。

    上面代码的写法会看起来比较长,但是把断言函数抽离出来,可以让重复的循环代码更加明显。现在把种循环抽离到一个函数当中。

    function filter(predicate, arr) {

    let working = [];

    for (let item of arr) {

    if (predicate(item)) {

    working = working.concat(item);

    }

    }

    }

    const femaleHeroes = filter(isFemaleHero, heroes);

    const superhumans = filter(isSuperhuman, heroes);

    同 map 和 reduce 一样,JavaScript 提供了一个内置数组方法,没必要自己来实现(除非你自己想写)。用内置数组方法,上面的代码就变成了:

    const femaleHeroes = heroes.filter(isFemaleHero);

    const superhumans = heroes.filter(isSuperhuman);

    为什么这段代码比 for...of 循环好呢?回想一下整个过程,我们要解决一个“找到满足某一条件的所有英雄”。使用 filter 使得问题变得简单化了。我们需要做的就是通过写一个简单函数来告诉 filter 哪一个数组元素要保留。不需要考虑数组是什么样的,以及繁琐的中间变量。取而代之的是一个简单的断言函数,仅此而已。

    与其他的迭代函数相比,使用 filter 是一个四两拨千斤的过程。我们不需要通读循环代码来理解到底要过滤什么,要过滤的东西就在传递给它的那个函数里面。

    finding

    filter 已经信手拈来了吧。这时如果只想找一个英雄该怎么办?比如找 “Black Widow”。使用 filter 会这样写:

    function isBlackWidow(hero) {

    return (hero.name === 'Black Widow');

    }

    const blackWidow = heroes.filter(isBlackWidow)[0];

    这段代码的问题是效率不够高。filter 会检查数组中的每一个元素,而我们知道这里面只有一个 “Black Widow”,当找到她的时候就可以停住,不用再看后面的元素了。那么,依旧利用断言函数,我们写一个 find 函数来返回第一次匹配上的元素。

    function find(predicate, arr) {

    for (let item of arr) {

    if (predicate(item)) {

    return item;

    }

    }

    }

    const blackWidow = find(isBlackWidow, heroes);

    同样地,JavaScript 已经提供了这样的方法:

    const blackWidow = heroes.find(isBlackWidow);

    find 再次体现了四两拨千斤的特点。通过 find 方法,把问题简化为:你只要关注如何判断你要找的东西就可以了,不必关心迭代到底怎么实现等细节问题。

    总结

    这些迭代函数的例子很好地诠释“抽象”的作用和优雅。回想一下我们所讲的内置方法,每个例子中我们都做了三件事:

    消除了循环结构,使得代码变的简洁易读;

    通过适当的方法名称来描述我们使用的模式,也就是:map,reduce,filter 和 find;

    把问题从处理整个数组简化到处理每个元素。

    注意在每一种情况下,我们都用几个纯函数来分解问题和解决问题。真正令人兴奋的是通过仅仅这么四种模式模式(当然还有其他的模式,也建议大家去学习一下),在 JS 代码中你就可以消除几乎所有的循环了。这是因为 JS 中几乎每个循环都是用来处理数组,或者生成数组的。通过消除循环,降低了复杂性,也使得代码的可维护性更强。

    作者:James Sinclair

    编译:胡子大哈

    展开全文
  • <div><p>我想在地图上添加多个marker,对应多个infoWindow;...但是窗体才出现最后一个,这个怎么解决,万分感谢该提问来源于开源项目:Dafrok/vue-baidu-map</p></div>
  • 另外服务端需要实时查询数据库,如果有数据就发送给对应客户端,5秒循环一次,我把这两个业务写在同一个channelRead中,结果后者经常出现多次发送数据的情况,好像是多个线程都在执行这个循环,执行的周期不是5秒,5...
  • 以前只做过LIST的循环打印,昨天被问到MAP循环怎么弄,一下给人问蒙了,最后查API,发现Map类中有一个方法values() ,可以返回一个Collection集合容器,然后可以循环打印。而且发现实现了Iterable的类都可以用...
  • //结合起来就是获取从第一个字节开始到第一个:符号的这部分子字符串。 //atoi是字符转int函数。c_str是char*转string的函数。 //这行代码总体的意思就是将文件分成两段,:前面的作为map的键,;后面的字节...
  • 点击“机器学习算法与Python实战”,“置顶”公众号重磅干货,第一时间送达Python中zip()函数的解释和可视化Python中enumerate函数的解释和可视化先重温一下迭代(Iteration)、迭代器对象...一个很好的例子是循环 - ...
  • 如下图,在packages数组中,每个packages都有一个entry数组,entry数组中可能包含一个对象,也可能包含多个,怎么在li列表中循环输出每一个entry中的每条数据 ![图片说明]...
  • 今年因为忙(其实是懒),没怎么写博客,加上工作内容比较low 遇到的问题也low,实在没啥好写的,这次终于碰上了有趣的问题 赶紧来写波 问题 因为协议经常不知道为什么不给定批量删除的接口,只能自己循环单个...
  • 最近弄刷新数据功能 存储方式Map,List<Model>> 然后我就想着更新条数据发条广播 在循环里面弄 如果不能连续发广播,那还有什么好的办法把数据一起传过去吗?没有经过页面的跳转之类的 只是点击按钮刷新数据在...
  • " % (map.key,map.value,id)这不就是一个循环迭代吗?所以我开始在网上找资料,看python有没有像mybatis中< foreach>的用法,但是浏览了一圈好像没有这个方法。最后询问了同事,他们给出了两个方法:1.将map的...
  • private void parseDictionary(JsonObject dics) throws JSONException { for (Map.Entry , JsonElement> entryBlock : ...//请问怎么将每一个entryValue.getValue().getAsString() 的值添加到下拉框spinner里 ```
  • React for循环渲染组件

    千次阅读 2019-11-26 16:34:40
    通常你需要在一个组件中渲染列表。或者循环遍历渲染相同的多个组件,下面看看怎么实现: 先来个有 If 判断的字组件循环渲染: render() { // 聊天列表组件 function MsgList(props){ const list = props....
  • 分享给大家供大家参考,具体如下:有一个数字字符的列表:numbers = ['1', '5', '10', '8']想要把每个元素转换为数字:numbers = [1, 5, 10, 8]用一个循环来解决:new_numbers = [];for n in numbers:new_numbers....
  • 文章首发本文已经默认你已经知道generator是什么以及for...of和数据类型... 所有同学的名字const 对于第一个问题来说,我们可以使用各种循环语句: for/whilefor for...offor 那么对于第二个问题来说,因为for/whil...
  • java map 添加数据

    2016-08-29 03:31:08
    java map 添加key 和 value, 动态添加key和value,key和value是分别每个数组中某一个值,key是a数组中的值a[i],而value是... //或者放进一个map中,再放进list中也可以。 看到的不用回答了,我知道答案了。 。。
  • map()方法总结

    2019-08-13 20:22:53
    对数组内元素操作,不要只会想到循环...map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。 并举了个例子: var array1 = [1,4,9,16]; const map1 = array1.map(x => x ...
  • kotlin map.plus 后值不会变化

    千次阅读 2017-06-30 10:43:49
    实际运用中遇到的第一个坑循环一个Mapmap.foreach{ //... if(xx){ val m = xxRepository.findOne() m.plus(it[x]) } }结果发现这个 m 的值怎么都不会变更.. 这也是java转kotlin的水土不服这里m 需要定义成var ...
  • 多重嵌套 for 循环太丑,运行效率又低,有没有什么办法可以降低它的嵌套层数?游戏内怎么简洁优雅的...先看一个简单的例子,数组去重:let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) {let newArra...
  • map/fileter

    2019-09-26 16:03:28
    每次循环的时候,就按照这个规则(自己定义的逻辑)去生成一个数据。 res = ['a','1' ] 只有在每次循环的时候,才按照这个规则去生成数据。 all = (i for i in range(5)) print(all)# generator生成器 附:...
  • 多重嵌套for循环太丑,运行效率又低,有没有什么办法可以降低它的嵌套层数?游戏内怎么简洁优雅的...先看一个简单的例子,数组去重:let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) { let newArray...
  • 为什么想这么做? ​ 在用传统Servlet做WEB项目的时候,涉及到构造一个field极多的Bean的业务,并且各个field类型不同,...​ 首先想到的是用一个Map将各个属性put进去,然后遍历这个Map利用反射构造属性。 代码展...
  • 多重嵌套for循环太丑,运行效率又低,有没有什么办法可以降低它的嵌套层数?游戏内怎么简洁优雅的...先看一个简单的例子,数组去重:let myArray = [1, 2, 3, 4, 5, 2, 1];function getArray(array) { let newArray...

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 170
精华内容 68
关键字:

怎么循环一个map