JAVA数据结构篇--14集合汇总篇

前言:在编程中我们经常使用List,Map,Set 集合进行元素的存放,它们之间的数据结构是怎样的,它们的使用场景又是怎样的;

1 数据结构:
1.1 数组:内存空间连续,存储同一种数据类型;
ArrayList<>: 底层使用数组结构实现;增删的涉及到数组的扩容和缩容,效率较低,查询效率高;内存空间连续,存储同一种数据类型,可以自动扩容的数组,存放元素有序且元素可以以重复;

1.2 链表:内存空间非连续,存储同一种数据类型;
LinkedList: 底层使用双向链表实现;增删的只涉及指针的指向效率较高,查询效率低因为需要进行链表的节点遍历,存放元素有序且元素可以以重复;

ArrayList和LinkedList 都是非线程安全的,线程安全的集合:
(1)Vector:底层使用数组实现;其中add ,get,remove方法会被synchronized 关键字进行修饰;所以他是线程安全的;
使用:Vector lists= new Vector<>();
(2)Collections.synchronizedList():使用:将原有的集合进行包装
Collections.synchronizedList(new ArrayList<>());替换 List lists= new ArrayList<>();
其中add ,get,remove方法体会被synchronized 关键字进行修饰;所以他是线程安全的;
(3)CopyOnWriteArrayList:
底层通过数组进行实现;add 方法时通过 ReentrantLock 进行加锁,确保数据安全,get方法没有加锁,所以并发效率较高;但是基于数组实现比较占用内存;

1.3 Map:key-value 键值对存储数据::
HashMap: 使用数组+链表+红黑树的结构;通过对key计算其hash 的散列值,在对散列值进行高低位一定计算后,和数组取模 得到数组的下标,在改index 下标出存储元素;如果改下标数组已经有元素占用,则扩展为链表,将改元素刚入到之前元素之后;当链表超过一定长度(说明 key 的hash 值存在大量相同,导致链表长度太长影响效率),则将链表转换为红黑树的结构;

LinkedHashMap:有序的键值对;底层使用数组,双向链表和红黑树存放数据的实现;在进行put,get 时可以根据key 的hash 值在散列表中直接找到改下标元素然后通过key进行equals 比较获取元素;在进行遍历的时候可以使用双向链表的结果进行顺序遍历;

TreeMap :可以进行排序的k-v结构:
底层使用红黑树进行实现,通过对key进行Comparator 比较器来决定元素的前后顺序;如果比较的结果是小于0则放到左边,如果是大于0则放到右边,如果等于0则进行数据的覆盖;通过实现
Comparator 接口重写compare 比较的方法,需要注意,如果比较的时候两个对象是想想等的,则需要保证两个对象进行equals 是返回true的;

HashMap,LinkedHashMap,TreeMap都是非线程安全的,线程安全的集合:
(1)Hashtable:底层使用数组和链表进行实现;所以Hashtable 中存放的元素是无序的,是线程安全的put和get 方法会被synchronized 关键字进行修饰;

(2) Collections.synchronizedMap:
将原有的map 集合进行封装,在get ,put ,remove 方法会被synchronized 关键字进行修饰;所以他是线程安全的;
使用: Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());

(3)ConcurrentHashMap:
jdk1.7 底层使用Hashtable 来实现所以底层的结构是数组+链表;将Hashtable 分成16段,每段都可以有一把锁,以此增加并发效率;
jdk1.8:底层使用HashMap来实现,所以底层数据结构是数组+链表+红黑树的结构;加锁则采用CAS和synchronized实现。
使用: Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

1.4 Set: 存储不重复的元素:
HashSet : 底层使用HashMap;add元素时,将该元素当做map 的key ,value为一个静态的常量,作为k-v 结果存储;当遍历的时候只遍历map 的key 即可;因为HashMap 是无序的,所以HashSet 存放的数据元素也是无序的;

TreeSet: 存放有序的不重复元素:
底层 使用TreeMap 由红黑树数据结果进行实现,其中元素的顺序通过Comparator 比较器来决定元素的前后顺序,通过 equals 方法来对比元素是否重复;所以在用TreeSet 存储对象时,可以通过实现
Comparator 接口重写compare 比较的方法,需要注意,如果比较的时候两个对象是想想等的,则需要保证两个对象进行equals 是返回true的;

LinkedHashSet: 存放有序(存储元素的顺序和取出一致)的不重复元素:
使用LinkedHashMap来进行实现;底层使用数组,双向链表的实现;add和元素遍历方法使用HashSet 父类方法进行添加;

HashSet,TreeSet,LinkedHashSet都是非线程安全的,线程安全的集合:
(1)Collections.synchronizedSet()
使用 :HashSet hashSet = new HashSet<>();
Set synchronizedSet = Collections.synchronizedSet(hashSet);
其中add ,get,remove方法体会被synchronized 关键字进行修饰;所以他是线程安全的;

(2)CopyOnWriteArraySet:
使用: CopyOnWriteArraySet copyOnWriteArraySet = new CopyOnWriteArraySet<>();
底层通过CopyOnWriteArrayList实现,数组进行实现;add 方法时通过 ReentrantLock 进行加锁,确保数据安全,get方法没有加锁,所以并发效率较高;但是基于数组实现比较占用内存;

(3)Collections.newSetFromMap()
Set setFromMap = Collections.newSetFromMap(new ConcurrentHashMap<>());
底层通过ConcurrentHashMap实现,所有数据结构是(jdk1.8及之后)数组+链表+红黑树实现;

1.5 Tree:树形结构:
二叉树:只用一个根节点,然后分为左分支和右分支;
二叉查找树(BST):只用一个根节点,然后分为左分支和右分支,左分支的数据比父节点 的都要小,右分支的数据都比父节点大;解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表
平衡二叉树(AVⅥL):通过旋转解决了平衡的问题,但是旋转操作效率太低;
红黑树:通过舍弃严格的平衡和引入红黑节点,解决了 AⅥ旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO 次数太多;
B树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;B树通常可以用来索引文件;
B+树:在 B 树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。B+树叶节点之间只是逻辑相邻,而不是物理相邻,甚至在物理位置相邻很远的情况下,依然会产生很多的随机IO。B+树减少随机IO的关键在于,利用叶节点逻辑相邻的特性,尽可能地做到物理相邻(数据被分配到连续的页中),使得在读取叶节点中的大量记录时可以使用顺序IO。这点很重要;
在mysql 中使用的索引选择了B+树;这是一种综合考虑的结果,如果使用平衡二叉树或者红黑树,依然会造成数据的高度很高,从而影响查询效率;之所以不用B树,B+树只在叶子节点存储数据,是在同一个层次深度,避免了B树的跨层查询;而且B+树的叶子节点使用链表,可以进一步减少io 的次数(B+ 树遍历数据时可以直接在多个子节点之间进行跳转,这样能够节省大量的磁盘 I/O 时间,也不需要在不同层级的节点之间对数据进行拼接和排序);
优化方向:索引可以使用hash数组+B+数的形式,这样在插入,更新,删除的使用直接进行hash 提供 O(1) 的单数据行操作性能;而在范围查询的时候使用B+树结构;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值