前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >java集合中list、set、map接口间的区别

java集合中list、set、map接口间的区别

作者头像
用户7886150
修改2021-04-20 10:14:26
5940
修改2021-04-20 10:14:26
举报
文章被收录于专栏:bit哲学院

参考链接: Java集合接口

集合框架 

集合是数据的容器,可以保存大量数据,集合的长度可以自动扩展。 

collection 接口list接口元素是有顺序的,元素可以重复因为每个元素有自己的角标(索引)set接口元素是无序的,且不可以重复(存入和取出的顺序不一定一致),线程不同步,数据不能单独访问。 

map接口这个集合是存储键值对的,一对一对往里存,而且要确保键的唯一性(键不能重复)

List接口 

LinkedList类: 底层使用的是链表数据结构,特点是:增 删很快,查询慢(LinkedList中的get方法是要依照顺序从列表的一端開始检查,直到另一端)。 

 不要使用常规的for循环遍历linkedList 

ArrayList类: 底层的数据结构是数组结构,特点是:查询很快,增 删 稍微慢点(由于ArrayList要移动数据),线程不同步,在 Java1.2 版本引入的。 

 数据的扩容:ArrayList默认添加原来的0.5倍,ArrayList没有提供相关的方法来让我们自己设置增长的大小。 

Vector类: 底层是数组数据结构,线程同步,被ArrayList代替了,现在用的只有他的枚举,在 Java1.0 版本引入的。 

 数据的扩容:Vector默认添加原来的一倍,能够由我们自己来设置增长的大小。 

。 

Set接口 

HashSet类:底层是哈希表数据结构。根据hashCode和equals方法来确定元素的唯一性。TreeSet类:可以对Set集合中的元素进行排序(自然循序),底层的数据结构是二叉树,也可以自己写个类实现Comparable 或者 Comparator 接口,定义自己的比较器,将其作为参数传递给TreeSet的构造函数。LinkedHashSet类:能保留数据的原始添加顺序。 

map接口 

HashTable类:底层是哈希表数据结构,不可以存入null键和null值,该集合线程是同步的,效率比较低。出现于JDK1.0。 

 从Hashtable示例的源码可以看出,Hashtable是用synchronized关键字来保证线程安全的,由于synchronized的机制是在同一时刻只能有一个线程操作,其他的线程阻塞或者轮询等待,在线程竞争激烈的情况下,这种方式的效率会非常的低下。 Hashtable的扩容 :newSize = 2 * oldSize + 1 

HashMap类:底层是哈希表数据结构,可以存入null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2。 

 我想线程安全,但是我又想效率高怎么办? 

 jdk1.5之后,推出了ConcurrentHashMap,通过把整个Map分为N个Segment(类似于HashTable),可以提供相同的线程安全,但是效率提高N倍,默认提升16倍。 

HashMap线程不安全,它的线程不安全主要发生在put等对HashEntry有直接写操作的地方:

从put操作的源码不难看出,线程不安全主要可能发生在这两个地方:

key已经存在,需要修改HashEntry对应的value; key不存在,在HashEntry中做插入。

TreeMap类:底层是二叉树数据结构,线程不同步,可以用于个map集合中的键进行排序。LinkHashMap类:能保留键的原始添加顺序。 

HashMap是如何添加\查找数据的: 

添加: 1、通过键的hashCode计算出数组的下标 2、通过下标找到该位置,如果该位置上数据为null,就把数据存入该位置的节点 3、如果该位置数据不为null,调用键的equals方法和该节点的键比较 4、如果equals返回为true,将新的数据覆盖原有数据 5、如果equals返回为false,就和下一个节点比较,最后把数据放到链表末尾。 查找: 1、通过键的hashCode计算出数组的下标 2、如果该位置有值,就调用equals进行比较 3、equals为true,返回数据的值 4、如果为false,就在链表依次往后查找,直到找到为止 

HashTable和ConcurrentHashMap的区别; 

HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。 因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。 如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低 

ConcurrentHashMap使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问 

HashMap的底层实现,以及ConcurrentHashMap的底层实现; 

在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。 HashMap实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。HashMap底层就是一个数组,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组 ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。 一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组, 每个HashEntry是一个链表结构的元素, 每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。 

如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现; 

方法一: 维护一张表,存储数据插入的顺序,可以使用vector。但是如果删除数据呢,首先得在vector里面找到那个数据,再删除,而删除又要移动大量数据。性能效率很低。 使用list,移动问题可以解决,但是查找数据的O(n)时间消耗,如果删除m次,那查找数据的性能就是0(n*m),那总体性能也是 O(n2)。性能还是没法接受。 

方法二: 可以在hashmap里面维护插入顺序的id, 在value建一个字段存储id值,再维护一张表vector,并且id对应vector里面的值。 插入的时候,id+=1, hashmap.insert,vector.push_back. 删除的时候,先hashmap.find(key), 得到value, 并从value中得到id, 通过id把对应vector值置为无效。 更新:删除+插入。 维护工作OK了,输出的时候直接输出vector里面的值就可以了, 无效的就continue。 算法复杂度为O(n) 

方法三: Java里面有个容器LinkedHashMap, 它能实现按照插入的顺序输出结果。 它的原理也是维护一张表,但它是链表,并且hashmap中维护指向链表的指针,这样可以快速定位链表中的元素进行删除。 它的时间复杂度也是O(n), 空间上要比上面少些。 

 加波关注,不迷路!!感谢各位老铁们的帮助。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档