Java常用集合类详解

  • 什么是集合类 

     简单来讲:集合就是一个放数据的容器,准确的说是放数据对象引用的容器

  1. 集合类存放于java.util包中。
  2. 集合类型主要有3种:set(集)、list(列表)和map(映射)。
  3. 集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。

  • 集合类有哪些

    • Set

List和Set都是集合,一般来说:如果我们需要保证集合的元素是唯一的,就应该想到用Set集合,比如说:现在要发送一批消息给用户,我们为了减少「一次发送重复的内容给用户」这样的错误,我们就用Set集合来保存用户的信息

一般我们在开发中最多用到的也就是HashSet。TreeSet是可以排序的Set,一般我们需要有序,从数据库拉出来的数据就是有序的,可能往往写order by id desc比较多。而在开发中也很少管元素插入有序的问题,所以LinkedHashSet一般也用不上。

如果考虑线程安全的问题,可以考虑CopyOnWriteArraySet,用得就更少了(这是一个线程安全的Set,底层实际上就是CopyWriteOnArrayList);TreeSet和LinkedHashSet更多的可能用在刷算法的时候。

1) Set 集合属于单列集合,不允许包含重复元素;

2) 判断元素是否重复的标准为对象的 equals 方法,存在时返回 false,不存在返回 true;

3) 元素的排序规则,由相应的实现类决定,分为无序、元素大小排序、写入顺序排序;

4) 初始化大小,扩容参考 HashMap。

名称类型线程同步描述
Set接口 继承了Collection接口
SortedSet接口 继承了Set接口
HashSet实现类不同步继承了AbstractSet类,实现了Set、Cloneable、Serializable接口
TreeSet实现类不同步继承了AbstractSet类,实现了NavigableSet(继承了SortedSet)、Cloneable、Serializable接口
LinkedHashSet实现类不同步继承了HashSet类,实现了Set、Cloneable、Serializable接口
  • HsahSet

1) HashSet 实现了 Set 接口,继承了 AbstractSet 类,由哈希表支持,看源码可以发现是一个 HashMap 实例。

2) HashSet 不保证集合内元素的迭代顺序,特别是不保证迭代顺序永久不变,该集合运行 null 元素存在。

3) HashSet 中的元素,作为 HashMap 键值对的 Key 存储,而 Value 由一个统一的值保存。

4) HashSet 默认初始化大小为 16,扩容加载因子为 0.75,扩容大小为原来的一倍。即一个初始化size为16的 HashSet,元素添加到12个的时候会进行扩容,扩容后的大小为32。 

  • TreeSet

1) TreeSet 实现了 NavigableSet 接口,继承了AbstractSet类,由哈希表支持,看源码可以发现是一个 TreeMap 实例。

2) TreeSet 中的元素有序的,排序规则遵照元素本身的大小进行排序,元素不能重复。

3) TreeSet 中的元素,作为 TreeMap 键值对的 Key 存储,而 Value 由一个统一的值保存。

  • LinkedHashSet

1) LinkedHashSet 实现了 Set 接口,继承了HashSet类,由哈希表支持,看源码可以发现是一个 LinkedHashMap 实例。 2) LinkedHashSet 中的元素有序的,排序规则遵照元素写入顺序进行排序,元素不能重复。 3) LinkedHashSet 中的元素,作为 LinkedHashMap 键值对的 Key 存储,而 Value 由一个统一的值保存。

  • List

List集合下最常见的集合类有两个:ArrayList和LinkedList。在工作中,我都是无脑用ArrayList。我问了两个同事:“你们在项目中用过LinkedList吗?”他们都表示没有。

众所周知,ArrayList底层是数组,LinkedList底层是链表。数组遍历速度快,LinkedList增删元素快。

为什么在工作中一般就用ArrayList,而不用LinkedList呢?原因也很简单:

  • 在工作中,遍历的需求比增删多,即便是增加元素往往也只是从尾部插入元素,而ArrayList在尾部插入元素也是O(1)
  • ArrayList增删没有想象中慢,ArrayList的增删底层调用的copyOf()被优化过,加上现代CPU对内存可以块操作,普通大小的ArrayList增删比LinkedList更快。

所以,在开发中,想到要用集合来装载元素,第一个想到的就是ArrayList。LinkedList用在什么地方呢?我们一般用在刷算法题上。把LinkedList当做一个先进先出的队列,LinkedList本身就实现了Queue接口,如果考虑线程安全的问题,可以看看CopyWriteOnArrayList,实际开发用得不多,但我觉得可以了解一下它的思想(CopyWriteOn),这个思想在Linux/文件系统都有用到。

1) List 集合属于单列、有序的、允许元素重复、可以为 null 的集合;

 2) List 接口的实现类主要有三种:ArrayList、LinkedList、Vector。

名称类型线程同步描述
List接口 继承了Collection接口
ArrayList实现类不同步继承了AbstractList类,实现了List、RandomAccess、Cloneable、Serializable接口
LinkedList实现类不同步继承了AbstractSequentialList类,实现了List、Deque、Cloneable、Serializable接口
Vector实现类同步继承了AbstractList类,实现了List、RandomAccess、Cloneable、Serializable接口

 

  • Arraylist

1) ArrayList 实现了 List 接口,继承了 AbstractList 类,由一个 Object[] 实例实现,即底层为数组结构;

2) 默认初始化长度为 10,扩容规则为 0.5倍的原容量加1,即一次扩容后的长度为 16;

3) 特点:查询速度快,添加、删除相对于LinkedList较慢、线程不同步(不安全)。

  • LinkedList

1) LinkedList 实现了 List 接口,继承了 AbstractSequentialList 类,由一个 Node 节点链表实现,即底层为链表结构;

2) 由于LinkedList 数据结构为链表,无预扩容机制;

3) 特点:添加、删除速度快,查询相对于ArrayList较慢、线程不同步(不安全)。

  • Vector

1) Vector实现了 List 接口,继承了 AbstractList 类,由一个 Object[] 实例实现,即底层为数组结构;

2) 默认初始化长度为 10,扩容加载因子为 1,当元素长度大于原容量时进行扩容,默认为 10,一次扩容后容量为 20;

3) 特点:线程安全,但是速度慢;在实现的方法上,用 synchronized 关键字进行了修饰,即在方法上实现了同步锁。

  • Map

Map集合最常见的子类也有三个:HashMap、LinkedHashMap、TreeMap

如果考虑线程安全问题,应该想到的是ConcurrentHashMap,当然了Hashtable也要有一定的了解,因为面试实在是问得太多太多了。

HashMap在实际开发中用得也非常多,只要是key-value结构的,一般我们就用HashMap。LinkedHashMap和TreeMap用的不多,原因跟HashSet和TreeSet一样。

ConcurrentHashMap在实际开发中也用得挺多,我们很多时候把ConcurrentHashMap用于本地缓存,不想每次都网络请求数据,在本地做本地缓存。监听数据的变化,如果数据有变动了,就把ConcurrentHashMap对应的值给更新了

名称类型线程同步描述
Map接口  
HashMap实现类不同步继承了AbstractMap类,实现了Map、Cloneable、Serializable接口
LinkedHashMap实现类不同步继承了HashMap类,实现了Map接口
TreeMap实现类不同步继承了AbstractMap类,实现了NavigableMap(继承了SortedMap)、Cloneable、
Serializable接口
Hashtable实现类同步继承了Dictionary类,实现了Map、Cloneable、Serializable接口
ConcurrentHashMap实现类同步继承了AbstractMap类,实现了ConcurrentMap(继承了Map)、Serializable接口
  • HashMap

1) HashMap实现了 Map接口,继承了 AbstractMap类,数据结构采用的位桶数组,底层采用链表或红黑树进行存储;

2) 默认初始化长度为 16,扩容加载因子为 0.75,一旦大于 0.75*16之后,就会调用resize()进行扩容,扩容2倍,即32;

3) JDK1.7中,数据结构采用:位桶数组+链表结构; JDK1.8中,数据结构采用:位桶数组+(链表/红黑树);

4) 支持克隆,无序,线程不同步,非安全。

  • LinkedHashMap

1) LinkedHashMap 实现了 Map 接口,继承了 HashMap 类; 引用实现;

2) 迭代顺序由 accessOrder 属性的决定,默认为 false,以插入顺序访问;设置为 true 则按上次读取顺序访问(每次访问 元素时会把元素移动到链表末尾方便下次访问,结构会时刻变化)。

3) 默认初始化长度为 16,扩容加载因子为 0.75,一旦>0.75*16之后,就会调用resize()进行扩容,与HashMap一致;

4) 支持克隆,有序,线程不同步,非安全。

  • TreeMap

1) TreeMap实现了 NavigableMap接口,继承了 AbstractMap 类;

2) 数据结构基于红黑树实现;

3) 该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法;

4) 无初始化长度;

5) 支持克隆,有序,线程不同步,非安全。

  • HashTable

1) Hashtable实现了 Map 接口,继承了 Dictionary类;

2) 数据结构:也是一个散列表,数据结构与HashMap相同,key、value都不可以为 null;

3) 该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法;

4) 默认初始化长度为 11,扩容加载因子为 0.75,一旦>0.75*11之后,就会进行扩容,扩容到2n+1,即23;

5) 支持克隆,无序,线程同步,安全。在实现的方法上,用 synchronized 关键字进行了修饰,即在方法上实现了同步锁。

6) 支持 Enumeration 遍历方式。

  • ConcurrentHashMap

1) ConcurrentHashMap实现了 ConcurrentMap接口,继承了 AbstractMap类;

2) 默认初始化长度为 16,扩容加载因子为 0.75,一旦大于 0.75*16之后,就会调用resize()进行扩容,扩容2倍,即32;

3) 数据结构:由Segment数组结构和HashEntry数组结构组成,一个ConcurrentHashMap中包含一个Segment数组, Segment的结构和HashMap类似,是一种数组和链表结构。

4) 使用了锁分段技术,Segment是一种可重入锁ReentrantLock,每个Segment拥有一个锁,当对HashEntry数组的 数据进行修改时,必须先获得对应的Segment锁

5) 不支持克隆,无序,线程同步,安全。

  • 什么时候考虑线程安全

由于 HashSet、TreeSet、LinkedHashSet的底层实现为HashMap、TreeMap、LinkedHashMap,所以Set集合是非线程安全的。 如果要实现 Set 集合的线程安全,可以使用 ConcurrentHashMap 实现一个Set集合。

什么时候考虑线程安全的集合类,那当然是线程不安全的时候咯。那什么时候线程不安全?最常见的是:操作的对象是有状态的

虽然说,我们经常会听到线程不安全,但在业务开发中要我们程序员处理线程不安全的地方少之又少。比如说:你在写Servlet的时候,加过syn/lock锁吗?应该没有吧?

因为我们的操作的对象往往是无状态的。没有共享变量被多个线程访问,自然就没有线程安全问题了

一句话总结:只要涉及到多个线程操作一个共享变量的时候,就要考虑是不是要用线程安全的集合类

总结

List和Set总结:

(1)List,Set都是继承自Collection接口,Map则不是
(2)List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
(3)Set和List对比:Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
ArrayList与LinkedList的区别和适用场景

Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。

LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析
当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。

ArrayList与Vector的区别和适用场景
ArrayList有三个构造方法:

public ArrayList(int initialCapacity)//构造一个具有指定初始容量的空列表。    
public ArrayList()      //默认构造一个初始容量为10的空列表。    
public ArrayList(Collection<? extends E> c)//构造一个包含指定 collection 的元素的列表

Vector有四个构造方法:

public Vector()//使用指定的初始容量和等于0的容量增量构造一个空向量。    
public Vector(int initialCapacity)//构造一个空向量,使其内部数据数组的大小,其标准容量增量为零。    
public Vector(Collection<? extends E> c)//构造一个包含指定 collection 中的元素的向量    
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量构造一个空的向量    

ArrayList和Vector都是用数组实现的,主要有这么三个区别:
(1).Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
(2)两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
*(3)*Vector可以设置增长因子,而ArrayList不可以。
*(4)*Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
适用场景分析
1.Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
2.如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。

Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。请注意!!!, Map 没有继承 Collection 接口, Map 提供 key 到 value 的映射,你可以通过“键”查找“值”。一个 Map 中不能包含相同的 key ,每个 key 只能映射一个 value 。 Map 接口提供 3 种集合的视图, Map 的内容可以被当作一组 key 集合,一组 value 集合,或者一组 key-value 映射。

HashMap 非线程安全
HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。

TreeMap:非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。

适用场景分析:
HashMap和HashTable:HashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。HashMap允许空键值,而HashTable不允许。

HashMap:适用于Map中插入、删除和定位元素。
Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

线程安全集合类与非线程安全集合类
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;
HashMap是非线程安全的,HashTable是线程安全的;
StringBuilder是非线程安全的,StringBuffer是线程安全的。

数据结构
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值