集合中的线程初体验

java零基础入门-高级特性篇(四) HashSet 和 Collections

本章继续讲集合,先来看看Set集合。Set集合的特点,1:无序,2:无重复。上一章讲了HashMap,最后提到HashSet的底层实现其实就是HashMap。那么为什么用HashMap就可以实现无序和不重复,下面看看具体如何使用HashMap实现HashSet。

hashset和hashmap的区别

hashset底层用hashmap实现,那为什么不直接用hashmap就完了,非要整个hashset出来?

集合

如果有这个问题,可以回头看看前面讲的集合框架的设计。设计hashset是用来保存那种不需要使用下标操作元素,并且不能重复的集合。set集合的元素和List集合的元素一样,都是一个对象。而hashmap的元素是key-value键值对,因为数据存储类型不同,所以需要将set和map区分开来。看一下图,帮助回忆一下。

hashmap如何实现hashset

Set集合最重要的一个方法就是add(E e),如何往一个Set集合添加元素,了解了添加元素的原理,查找元素理解起来就简单许多。hashset的add方法,用的就是是hashmap的put方法。下面是源代码:

map.put(e,PRESENT) == null

这里需要重点理解的是,一个对象如何存入一个key-value。从上面这句代码中,可以发现,在往set集合添加元素的时候,这个元素e被用来当做map的key,而value是一个常量。

为什么直接将对象作为key呢?因为hashmap使用哈希算法对key进行计算,计算后的结果就是底层数组的位置,所以当使用hashset的时候,需要对放进set的对象进行哈希计算,至于value,hashset不关心。

hashmap的key的特性就是不会重复,后添加相同的数据会将前一个数据覆盖掉。正好满足了set集合不重复的特性,所以直接用hashmap即可以满足hashset集合的要求。

关系

其实这里容易绕晕的是几个底层实现的结构,这里用一个图来说明一下。HashSet利用HashMap的Key的特性来实现,而HashMap是利用数组和链表的特性来实现,这样应该明白这几个结构之间的关系了吧。

这次hashset真的讲完了。

Collections工具

使用集合存放数据的时候,会有很多情况要对集合进行操作。特别是List集合,因为List集合的有序性,会需要按照特定的顺序操作集合,而java也专门提供了Collections工具来对集合进行操作。下面来看看几个例子

List list = new ArrayList();

list.add("one");//此处省略 ,一共添加五个元素one,two,three,fore,five

Collections方法

Collection.reverse(list);//反转集合元素的顺序

Collection.shuffle(list);//随机顺序,多次随机结果可能不一样

Collection.sort(list);//升序排序,先数字后字母,数字0-9,字母A-Z a-z的顺序,逐位比较

Collection.swap(list,0,3);//交换第一个和第四个元素位置

看一个swap的源码实现

Object tmp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

嗯,完了,是不是有一种这玩意我都会写,干嘛要用这个的感觉?其实确实可以自己写,但是一般提供工具给别人用,肯定要提供全套,再就是合理的使用工具会减少不必要的代码,提升代码的可读性。

Collection 和 Collections

这两个长得很像,但是作用差别很大,初学者切勿将两者概念混淆。Collection是集合体系中的上层接口,而Collections是操作集合的工具。何谓工具?还记不记得我们讲的静态方法?不记得的快去复习类和对象的文章。

Collections作为一个工具类,里面提供的方法都是静态方法,所以在上面的例子中,都是直接使用类Collections来调用方法,Collections提供了大量的静态方法来操作集合,有没有加深对静态成员这个概念的理解?

用Collections工具类创建线程安全的集合

上次讲vector的时候,说了他是线程安全的集合,而List是线程不安全的。但是可以通过一些方法让List变成线程安全的,所以vector目前已经没有使用的必要了。那么如何让List变成线程安全的集合呢?答案就是使用Collections工具可以将List变为线程安全。

Collections有一系列的synchronized方法来使集合变为线程安全的,一系列是指不仅仅可以将List变为线程安全的,Set,Map也有方法变成线程安全的。

List list = Collections.synchronizedList(new ArrayList());

Set set = Collections.synchronizedSet(new HashSet());

Map map = Collections.synchronizedMap(new HashMap());

使用以上三个工具方法就可以将普通集合变为线程安全的集合。这些方法有什么魔法么,为什么外面包一层就线程安全了?下面来简单介绍一下多线程以及使用多线程会遇到的问题。

多线程是什么?

多线程就是并行处理问题。比如去银行取钱,如果只有一个取款机队伍就会排很长,但是如果有多个取款机同时办理业务,速度就会快很多。这就是多线程的思路,多个线程(取款机)处理一个问题(取钱)。

多个ATM

为什么多线程有安全问题?

假设现在有2个人要取款,如果2个人同时操作一个取款机会发生什么?第一个人密码输了3位数,第二个人跑来按3位数,第一个人删了准备重新输,又被第二个人按了3下,这样下去两个人都别想取钱。多个线程抢同一个资源就会产生线程安全问题,实际开发中遇到的线程安全问题会比这种情况还要复杂。

怎么解决?

带锁的ATM

新款取款机,带门带锁的!一旦一个人进去了,先锁门,然后就可以放心大胆的取钱了,这时候没有人会来跟你抢着取钱了。代码里面也是可以上锁的,所以线程安全的集合都是带有锁机制的。

高效并发容器

这里是补充知识,了解即可。其实上面的这几个方法确实可以将普通集合转为线程安全的集合,但是实现很粗糙,导致效率不是很高。所以就有了专门为并发情况设计的更加高效的并发容器,比如CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap。

上锁也是有很多种上锁的方法,继续取钱的例子。

最极端粗暴的上锁方式就是,银行每次只准进一个人,这样绝对不会有任何问题,绝对安全,银行所有保安都盯着你,你还能玩出花来?但是这样做效率极端底下。比如老版本的vector和hashtable就是用类似这种粗暴的方法来上锁。

再来看稍微先进点的上锁方式。就是银行可以进很多人,但是办理不同业务的分开来,取钱的一个队,存钱的一个队,办理财换密码再来一个队,每个队同时只能一个人办,办的人进小房间,上锁。这种上锁的粒度比上面那种要小,效率要快很多。

最后就是最先进的锁了。也就是类似并发容器这种,升级版的取款机不仅可以取钱还可以存钱,还能办其他业务~也就是说不管办什么业务,找个人最少的队排着就行了,所有机器可以同时办相同或者不同的业务。这种锁的粒度最小,只对操作对象的数据进行上锁,效率最高。

本文分享自微信公众号 - 码思客(javamsk)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员小明

快速优雅的在linux服务器上安装jdk8

要在linux下安装jdk,首先你得先有一台linux服务器,作为小白,手头的机器肯定都是windows的,搞个虚拟机安装对我这种小白简直是折磨人;这里使用最简...

66920
来自专栏Crossin的编程教室

len(x) 击败 x.len(),从内置函数看 Python 的设计思想

它们预先定义在内置命名空间中,开箱即用,所见即所得。Python 被公认是一种新手友好型的语言,这种说法能够成立,内置函数在其中起到了极关键的作用。

9620
来自专栏业余草

灭霸所有单例模式,克隆、序列化、反射机制破坏7种单例模式

单例模式实际上也不止 7 种。但是,每一种都并非安全的。今天我给大家讲一讲如何利用克隆、序列化、反射机制破坏单例模式。

16730
来自专栏码匠的流水账

聊聊Elasticsearch的ExponentiallyWeightedMovingAverage

本文主要研究一下Elasticsearch的ExponentiallyWeightedMovingAverage

13850
来自专栏码匠的流水账

聊聊Elasticsearch的LazyInitializable

elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/common/util/LazyIniti...

12550
来自专栏码匠的流水账

聊聊Elasticsearch的Releasables

elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/common/lease/Releasab...

11120
来自专栏秃头哥编程

Java开发神器Lombok的安装与使用

项目中经常使用bean,entity等类,绝大部分数据类类中都需要get、set、toString、equals和hashCode方法,虽然eclipse和id...

10930
来自专栏我是攻城师

如何通过Java反射获取泛型类型信息

关于Java泛型,很多人都有一个误解,认为Java代码在编译时会擦除泛型的类型,从而在运行时导致没法访问其类型,这其实并不完全正确,因为有一部分泛型信息是可以在...

2.2K20
来自专栏码匠的流水账

聊聊dubbo的Invoker select

dubbo-2.7.1-sources.jar!/org/apache/dubbo/rpc/proxy/InvokerInvocationHandler.jav...

14630
来自专栏前沿技墅

响应式微服务架构设计

使用微服务架构最关键的一个原则就是将系统划分成一个个相互隔离、无依赖的微服务,这些微服务通过定义良好的协议进行通信。而响应式微服务架构,又有其独特的设计原则和理...

19930

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励