如何线程安全地遍历List

遍历List的多种方式在讲如何线程安全地遍历 List 之前,先看看遍历一个 List 通常会采用哪些方式。 方式一:for(int i = 0; i < list.size(); i++) { System.out.println(list.get(i));}方式二:Iterator iterator = list.iterator();while(iterator.hasNext()) { System.out.println(iterator.next());}方式三:for(Object item : list) { System.out.println(item);}方式四(Java 8):list.forEach(new Consumer<Object>() { @Override public void accept(Object item) { System.out.println(item); }});方式五(Java 8 Lambda):list.forEach(item -> { System.out.println(item);});方式一的遍历方法对于 RandomAccess 接口的实现类(例如 ArrayList)来说是一种性能很好的遍历方式。但是对于 LinkedList 这样的基于链表实现的 List,通过 list.get(i) 获取元素的性能差。 方式二和方式三两种方式的本质是一样的,都是通过 Iterator 迭代器来实现的遍历,方式三是增强版的 for 循环,可以看作是方式二的简化形式。 方式四和方式五本质也是一样的,都是使用Java 8新增的 forEach 方法来遍历。方式五是方式四的一种简化形式,使用了Lambda表达式。 遍历List的同时操作List会发生什么?先用非线程安全的 ArrayList 做个试验,用一个线程通过增强的 for 循环遍历 List,遍历的同时另一个线程删除 List 中的一个元素,代码如下: ...

November 3, 2019 · 3 min · jiezi

全栈之路JAVA基础课程六集合20190615v10

欢迎进入JAVA基础课程 博客地址:https://blog.csdn.net/houjiyu...本系列文章将主要针对JAVA一些基础知识点进行讲解,为平时归纳所总结,不管是刚接触JAVA开发菜鸟还是业界资深人士,都希望对广大同行带来一些帮助。若有问题请及时留言或加QQ:243042162。 寄语:再走长征路,回顾过往峥嵘岁月,重温重要历史事件,砥砺前行,用脚步丈量新时代的长征路。工作道路上,我们也要弘扬这种长征精神,坚持不懈,一步一个脚印,脚踏实地,朝着自己的目标前行。集合1. 集合框架图(1)缩略版 (2)详细版 2.集合和数组区别 3.Collection接口 List接口:元素按进入先后有序保存,可重复 (1)LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素(2) ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素 (3) Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素 Set 接口: 仅接收一次,不可重复,并做内部排序 HashSet 使用hash表(数组)存储元素 LinkedHashSet 链表维护元素的插入次序TreeSet 底层实现为二叉树,元素排好序HashSet和TreeSet区别: (1)Treeset 中的数据是自动排好序的,不允许放入 null 值。 (2)HashSet 中的数据是无序的,可以放入 null,但只能放入一个 null,两者中的值都不能重复,就如数据库中唯一约束。 (3)HashSet 要求放入的对象必须实现 HashCode()方法,放入的对象,是以 hashcode 码作为标识的,而具有相同内容的 String 对象,hashcode 是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 4.Map HashMap和HashTableHashMap 和 Hashtable 都实现了 Map 接口,因此很多特性非常相似。但是,他们有以下不同点:(1)HashMap 允许键和值是 null,而 Hashtable 不允许键或者值是 null。(2)Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线程环境,而 Hashtable 适合于多线程环境。 TreeMap 5.集合遍历(1)list遍历 public class CollectionMain { public static void main(String[] args) { List<String> testList = new ArrayList<>(); testList.add("1"); testList.add("2"); testList.add("3"); System.out.println("使用Iterator迭代....."); Iterator<String> iterator = testList.iterator(); while (iterator.hasNext()){ String value = iterator.next(); System.out.printf("%s ",value); } //在使用ListIterator迭代时,开始也需要正向迭代,然后在倒序迭代 System.out.println("\n\n使用ListIterator迭代....."); System.out.println("正向遍历....."); ListIterator<String> listIterator = testList.listIterator(); while (listIterator.hasNext()){ String value = listIterator.next(); System.out.printf("%s ",value); } System.out.println("\n反向遍历....."); while (listIterator.hasPrevious()){ String value = listIterator.previous(); System.out.printf("%s ",value); } }}输出结果 ...

June 16, 2019 · 1 min · jiezi

多叉树全路径遍历

多叉树全路径遍历本文为原创作品,首发于微信公众号:【坂本先生】,如需转载请在文首明显位置标明“转载于微信公众号:【坂本先生】”,否则追究其法律责任。 前言本文研究的是如何对一个多叉树进行全路径的遍历,并输出全路径结果。该问题的研究可以用在:Trie树中查看所有字典值这个问题上。本文将对该问题进行详细的模拟及进行代码实现,讨论了递归和非递归两种方法优劣并分别进行实现,如果读者对这两种方法的优劣不感兴趣可直接跳到问题构建章节进行阅读。文章较长,推荐大家先收藏再进行阅读。 文章目录多叉树全路径遍历 前言文章目录递归和迭代比较 递归迭代递归的劣势和优势 递归的劣势递归的优势问题构建问题解决 递归方法迭代方法结论参考资料递归和非递归比较这个问题知乎上已经有了很多答案(https://www.zhihu.com/questio...),在其基础上我进行了一波总结: 递归将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。 非递归执行效率高,运行时间只因循环次数增加而增加,没什么额外开销。空间上没有什么增加 递归的劣势和优势递归的劣势递归容易产生"栈溢出"错误(stack overflow)。因为需要同时保存成千上百个调用记录,所以递归非常耗费内存。效率方面,递归可能存在冗余计算。使用递归的方式会有冗余计算(比如最典型的是斐波那契数列,计算第6个需要计算第4个和第5个,而计算第5个还需要计算第4个,所处会重复)。迭代在这方面有绝对优势。递归的优势递归拥有较好的代码可读性,对于数据量不算太大的运算,使用递归算法绰绰有余。 问题构建现在存在一个多叉树,其结点情况如下图,需要给出方法将叶子节点的所有路径进行输出。 最终输出结果应该有5个,即[rad,rac,rbe,rbf,rg] 问题解决首先我们对结点进行分析,构建一个结点类(TreeNode),然后我们需要有一个树类(MultiTree),包含了全路径打印的方法。最后我们需要建立一个Main方法进行测试。最终的项目结构如下: 注意:本文使用了lombok注解,省去了get,set及相关方法的实现。如果读者没有使用过lombok也可以自己编写对应的get,set方法,后文会对每个类进行说明需要进行实现的方法,对核心代码没有影响。 TreeNode类 节点类,主要包含两个字段: content:用于存储当前节点存储的内容childs:用于存储子节点信息,HashMap的string存储的是子节点内容,childs采用HashMap实现有利于实现子节点快速查找该类中包含了必要的get,set方法,一个无参构造器,一个全参构造器 @Data@RequiredArgsConstructor@AllArgsConstructorpublic class TreeNode { private String content; private HashMap<String,TreeNode> childs;}MultiTree类 包含的字段只有两个: root:根节点pathList:用于存储遍历过程中得到的路径 该类中的构造函数中我手动创建问题构建中的树,相关代码如下: public MultiTree(){ //创建根节点 HashMap rootChilds = new HashMap(); this.root = new TreeNode("r",rootChilds); //第一层子节点 HashMap aChilds = new HashMap(); TreeNode aNode = new TreeNode("a",aChilds); HashMap bChilds = new HashMap(); TreeNode bNode = new TreeNode("b",bChilds); HashMap gChilds = new HashMap(); TreeNode gNode = new TreeNode("g",gChilds); //第二层结点 HashMap dChilds = new HashMap(); TreeNode dNode = new TreeNode("d",dChilds); HashMap cChilds = new HashMap(); TreeNode cNode = new TreeNode("c",cChilds); HashMap eChilds = new HashMap(); TreeNode eNode = new TreeNode("e",eChilds); HashMap fChilds = new HashMap(); TreeNode fNode = new TreeNode("f",fChilds); //建立结点联系 rootChilds.put("a",aNode); rootChilds.put("b",bNode); rootChilds.put("g",gNode); aChilds.put("d",dNode); aChilds.put("c",cNode); bChilds.put("e",eNode); bChilds.put("f",fNode); }在这个树中,每个节点都有childs,如果是叶子节点,则childs中的size为0,这是下面判断一个节点是否为叶子节点的重要依据接下来我们会对核心算法代码进行实现。 ...

May 28, 2019 · 2 min · jiezi

这破旧的脑子——二叉树

为什么会写这篇文章学习的时间越来越长总会忘掉一些东西,就比如向量,矩阵,二叉树,邻接表,太多太多东西,不用就都给忘了,今天看了这样一道面试题:总结下来就是根据二叉树的前中序遍历,然后写出后序遍历,清晰的记得当时学习二叉树的时候做这种题是很快的,可是我还真就卡住了,不是说需要做一会儿,是做不出来,看过好多遍使用程序实现DFS(深度优先)BFS(广度优先)的例子,可是让我用笔推断,我还真就脑子瓦特了,所以也记录一下,顺便帮一下也忘记了手工推断的你们回忆一下,你们一定都比我优秀,perfect。题目:前序遍历A D C E F G H B中序遍历C D F E G H A B后序遍历?这些遍历就是根据遍历根节点的顺序而定义的,前序遍历就是优先遍历根节点然后遍历左右子节点,当然左右子节点也是根据这个原则遍历的,那么中后序遍历也一样。那么我们应该怎么去做呢?其实就是根据前中遍历的结果推断出这颗树。。。第一步根据前序遍历原则找出根节点:A 因为优先遍历根节点根据根节点A和中序遍历划分前中序遍历的左右子树,以中左表示,前序遍历的左右子树,以前左表示:中左:C D F E G H中右:B前左:D C E F G H前右:B第二步根据上面的中左,前左继续划分根节点:D 由于右子树就一个节点,所以就结束了根据根节点D和中序遍历划分前中序遍历的左右子树中左:C中右:F E G H前左:C前右:E F G H第三步根据上面的中右,前右继续划分根节点:E 由于左子树就一个节点,所以就结束了根据根节点E和中序遍历划分前中序遍历的左右子树中左:F中右:G H前左:F前右:G H第四步根据上面的中右,前右继续划分根节点:G 由于左子树就一个节点,所以就结束了根据根节点G和中序遍历划分前中序遍历的左右子树中左:中右:H前左:前右:H终于构建出来这颗树了,接下来根据后序遍历原则去写:后序遍历结果亮相:C F H G E D B A只有多学习才能变得更强,还是那句话:坚持一件事,对自己。

March 28, 2019 · 1 min · jiezi

JavaScript数组遍历:for、foreach、for in、for of、$.each、$().each的区别

一、forJavascript中的for循环,它用来遍历数组var arr = [1,2,3,4]for(var i = 0 ; i< arr.length ; i++){ console.log(arr[i])}//1,2,3,4九九乘法表:for ( var x = 1; x <= 9; x++) { var str=""; for ( var y = 1; y <= x; y++) { str+=x + “” + y + " = " + (x * y)+" “; } console.log(str);}二、foreachforEach循环我们可以直接取到元素,同时也可以取到index值。但是forEach也有一些局限,不能continue跳过或者break终止循环let arr = [‘a’, ‘b’, ‘c’, ’d’]arr.forEach(function (val, index, arr) { console.log(‘index:’+index+’,’+‘val:’+val) // val是当前元素,index当前元素索引,arr数组 console.log(arr)})//index:0,val:a//[“a”, “b”, “c”, “d”]0: “a"1: “b"2: “c"3: “d”//index:1,val:b//[“a”, “b”, “c”, “d”]//index:2,val:c//[“a”, “b”, “c”, “d”]//index:3,val:d//[“a”, “b”, “c”, “d”]三、for infor(var item in arr|obj){} 可以用于遍历数组和对象遍历数组时,item表示索引值, arr表示当前索引值对应的元素 arr[item]遍历对象时,item表示key值,arr表示key值对应的value值 obj[item]for in一般循环遍历的都是对象的属性,遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性var obj = {a:1, b:2, c:3}; for (let item in obj) { console.log(“obj.” + item + " = " + obj[item]);}// obj.a = 1// obj.b = 2// obj.c = 3var arr = [‘a’,‘b’,‘c’];for (var item in arr) { console.log(item) //0 1 2 console.log(arr[item]) //a b c}四、for ofES6中新增加的语法 for of 语句创建一个循环来迭代可迭代的对象。在 ES6 中引入的 for of 循环,以替代 for in 和 forEach() ,并支持新的迭代协议。for of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。循环一个数组:let arr = [‘A’, ‘B’, ‘C’]for (let val of arr) { console.log(val) }// A B C循环一个字符串:let iterable = “abc”;for (let value of iterable) { console.log(value);}// “a”// “b”// “c"循环一个Map:let iterable = new Map([[“a”, 1], [“b”, 2], [“c”, 3]]); for (let [key, value] of iterable) { console.log(value);}// 1// 2// 3for (let entry of iterable) { console.log(entry);}// [a, 1]// [b, 2]// [c, 3]循环一个 Set:let iterable = new Set([1, 1, 2, 2, 3, 3]); for (let value of iterable) { console.log(value);}// 1// 2// 3循环一个拥有enumerable属性的对象for of循环并不能直接使用在普通的对象上,但如果我们按对象所拥有的属性进行循环,可使用内置的Object.keys()方法:for (var key of Object.keys(someObject)) { console.log(key + “: " + someObject[key]);}循环一个生成器(generators):function fibonacci() { // a generator function let [prev, curr] = [0, 1]; while (true) { [prev, curr] = [curr, prev + curr]; yield curr; }}for (let n of fibonacci()) { console.log(n); // truncate the sequence at 1000 if (n >= 1000) { break; }}五、jQuery里面的$.each$.each(arr|obj, function(k, v))可以用来遍历数组和对象,其中k表示索引值或者key值,v表示value值var arr = [‘a’,‘b’,‘c’]$.each(arr, function(key, val) { console.log(key, val);})//0 a//1 b//2 c六、jQuery里面的$().each()$().each()在dom处理上面用的较多,主要是用来遍历DOMList。如果页面有多个input标签类型为checkbox,对于这时用$().each()来处理多个checkbox,例如:$(“input[name=’checkbox’]”).each(function(i){if($(this).attr(‘checked’)==true){//操作代码}结论:推荐在循环对象属性的时候使用for in,在遍历数组的时候的时候使用for of;for in循环出的是key,for of循环出的是value;for of是ES6新引入的特性。修复了ES5的for in的不足;for of不能循环普通的对象,需要通过和Object.keys()搭配使用。跳出循环的方式有如下几种:return 函数执行被终止;break 循环被终止;continue 循环被跳过。 ...

December 17, 2018 · 2 min · jiezi