容器集合(区别)

本文详细介绍了Java集合框架中的ArrayList、Vector、LinkedList、HashSet、LinkedHashSet、TreeSet以及HashMap和HashTable的特性、底层实现与区别。ArrayList基于动态数组,Vector线程安全但效率较低,LinkedList适合频繁插入删除。HashSet无序不重复,LinkedHashSet保持插入顺序,TreeSet按排序顺序存储。HashMap非线程安全,HashTable线程安全但性能较低。
摘要由CSDN通过智能技术生成

前言

本文主要对集合一些重要细节进行补充说明!
详细学习,请前往:集合学习


提示:以下是本篇文章正文内容,下面案例可供参考

一、Collection接口

1.list集合

1.1 ArrayList

ArrayList的底层原理是由动态数组实现的。其数组的长度是随着元素的增多而变长的;当实例化ArrayList时(List array = new ArrayList();)它的长度是默认为10。但当初始化一个ArrayList后,发现数组的容量为0,并不是默认容量10啊。添加一个元素之后,容量变为了10。而且,ArrayList集合是容量满,它会创建一个新的数组(相当于扩容因子为1),其新数组的长度为当前数组的1.5倍,然后将当前数组的元素复制到新数组中,当前数组的内存被释放。(线程不安全,效率高)

1.2 Vector

Vector和ArrayList的底层实现相似,通过源码可以看出,每个方法都添加了synchronized关键字来保证同步,所以它是线程安全的,但是因为这些方法的同步,让其效率大大的降低了,比ArrayList的效率要慢。
当Vector容量默认值为10,这个集合会优先保证容量,即增加一个或多个元素时,会先判断集合的容量是否为0,不为0就扩容,设置的新容量是旧容量的2倍。注意的是,在Vector集合中不能有null值,一旦有,会报空指针。
Vector容量默认值根据构造方法不同,有些细微差别:如果为无参构造,初始化容量为10,扩容为原容量的2倍; 如果为单参数构造,初始化容量为指定容量,扩容为原容量的2倍; 如果为两个参数的构造,初始化容量为指定容量,扩容为原容量加上扩容长度

1.3 LinkedList

LinkedList底层实现为链表,准确一点为双向链表,这也说明是没有初始容量说法的,因为对链表而言,存储的位置是可以不连续的,他可以直接进行操作。

2.Queue

队列是一种特殊的数据结构,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
对于Queue 是个接口,它提供的 add, offer 方法初衷是希望子类能够禁止添加元素为 null,(除LinkedList这一特殊实现类没有特别指明,但是在使用时应该尽量避免。)是因为避免 poll(), peek() 方法在异常的时候会返回 null,当添加了 null 以后,再获取时不好分辨究竟是否正确返回还是添加值。

3.Set集合

3.1 HashSet

HashSet 的内部也是采用了HashMap作为数据存储,HashSet其实就是在操作HashMap的key(不能保证迭代顺序也不能有重复元素),
没有重复元素的关键是重写hashCode() 和 equals() 两个方法;
因为HashMap是无序的,因此HashSet也不能保证元素的顺序;
因为HashSet中没有对应同步的操作,因此是线程不安全的;
支持null元素(因为hashMap也支持null键和null值);
可以看出HashSet的操作都是基于HashMap实现的,默认构造函数是构建一个初始容量为16,扩容因子为0.75 的HashMap

3.2 LinkedHashSet

LinkedHashSet底层为数组+双向链表
LinkedHashSet() :使用默认的初始容量(16)和负载因子(0.75)创建一个空的链接哈希集。
LinkedHashSet(int initialCapacity) :创建一个具有指定初始容量和默认负载因子(0.75)的新的空链接哈希集。(初始容量>=0)
LinkedHashSet(int initialCapacity,float loadFactor) :使用给定的初始容量和负载因子创建一个空的LinkedHashSet。
LinkedHashSet(Collection <?扩展E> c) :创建一个新的链接的哈希集,其元素与指定的集合相同。
我认为比起HashSet, LinkedHashSet中链表的添加是为了保证输出的顺序的:
在LinkedHastSet中维护了一个hash表和双向链表,使每一个节点有pre和next属性,这样可以形成双向链表,这样在添加一个元素时,先求hash值,在求索引.确定该元素在hashtable的位置,然后将添加的元素加入到双向链表(如果已经存在,则不添加),那么,我们遍历LinkedHashSet 也能确保插入顺序和遍历顺序一致。

3.3 TreeSet

TreeSet集合底层是红黑树结构,集合元素有序且唯一,自然排序(无参构造方法时)为从小到大排序。无下标·,没有直接修改集合元素的方法。遍历有两种方法实现,即增强for和迭代器。
自然排序就是说,没有修改和指定的话,在输入时,就直接按照顺序排列,又因为结构原因(Set)不能有重复的元素。

二、Map集合

1.HashMap

HashMap底层实现为数组,链表和红黑树
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
整个流程是这样的:
我们输入数据,在存储数据时,计算HashCode,计算出来的HashCode再通过散列函数计算出其在数组中的索引位置。其中,HashCode是一个虚拟地址(真正的地址通过计算得到),两个不相同的元素值计算出来的Hash值一般不同,但是通过散列函数计算之后,不同HashCode可能映射为同一个数组的索引位置,这就需要再通过equals方法来比较两者是否真的相同了,相同的话,代表的是同一个数据,不存储;如果不相同,也就是,两者不一样,却计算出来了相同的值,映射了相同的数组索引位置,这个时候就产生了冲突,这时候我们引入链表来解决冲突,也就是说,链表是为了解决hash冲突的。
Hash冲突 : 不同的对象算出来数组下标是相同的
单向链表 : 用于解决Hash冲突的方案,加入一个next记录下一个节点
在这里插入图片描述
hashmap默认数组长度是16(一般),那么达到出发条件,数组存储比例达到了75% ,也就是16*0.75=12的时候就会发生扩容(0.75为扩容因子),其新长度是原来的2倍。
最后,在1.8之后引入红黑树,他是一种二叉树,高效的检索效率
这个是用来处理过长的链表,当链表长度大于8,就会将之后的数据以红黑树的方式存储(根节点是黑色)链表太长,查询速度慢。
(红黑树中元素小于6时,红黑树转链表)
在这里插入图片描述

2.HashTable

Hashtable同样是基于哈希表实现的,主体还是Entry数组,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了11,扩容因子0.75)时,同样会自动增长。
Hashtable也是JDK1.0引入的类,是线程安全的,会使用synchronized关键字,相当于是锁,只能一个线程操作,所以线程是安全的。(这也说明了效率慢)
Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。

3.HashMap和HashTable的区别

HashMap是非线程安全,在多线程环境下,HashMap会产生线程安全问题;而HashTable中的大部分方法都使用synchronized关键字来确保线程同步(只能单线程操作),因此HashTable是线程安全的,不过性能要比HashMap低一些;
HashMap的key可以使用null(但只能有一个,不重复),value可以为null(可重复),而HashTable都不允许存储key和value值为空的元素;
HashMap继承了AbstractMap,HashTable继承了Dictionary抽象类,两者都实现了Map接口;
HashMap的初始容量为16,HashTable的初始容量为11;
HashMap的扩容机制为扩容两倍,而HashTable的扩容机制为两倍-1;
HashTable不会转换为红黑树,仅用链式存储,查询效率低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值