什么是集合类 

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

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

Java集合关系 java集合类_集合

集合类有哪些

Set

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

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

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

Java集合关系 java集合类_java_02

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/文件系统都有用到。

Java集合关系 java集合类_Java集合关系_03

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对应的值给更新了

Java集合关系 java集合类_java_04

名称

类型

线程同步

描述

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:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序