Java集合详解

Java集合类

Java集合类是java.util包下的重要知识,集合类都实现了两个接口,一个是Collection接口,这是一个基础的集合接口,Collection接口继承了Iterable接口,Iterable接口是可迭代,可遍历的;

Collection接口

Collection接口有多个实现子类,实现子类中可以存放多个元素,元素的类型可以是Object,元素存储有的子类可以重复有的不可以,有的是有序存放的,有的不支持有序,在Collection之下有几个重要的实现接口,Set接口,List接口,Queue接口,AbstractCollection抽象类等;Collection接口的这几个实现接口实现了多个重要的集合类;

常用方法:

  • size():
  • isEmpty()
  • contains(),containsAll():如果列表中包含指定的元素,返回true。
  • iterator():返回一个迭代器,以正确的顺序遍历列表中的元素。
  • toArray():返回包含列表中所有元素的数组
  • add(),addAll()
  • remove(),removeAll()
  • retainAll():只保留列表中包含在指定集合中的元素
List集合

List集合是一种允许元素重复,有序的集合,可以通过下标索引对其中的元素进行查询和操作;在List接口之下,有Vecor类,ArrayList类,LinkedList类;在List接口中,存在多种方法;

ArrayList集合

该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。

  • ArrayList继承了 AbstractList 类。
  • ArrayList实现了 List 接口,可进行列表的相关操作。
  • ArrayList实现了 Cloneable 接口,可实现克隆。
  • ArrayList实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

ArrayList集合的底层实现:

  • 底层的数据结构是数组类型,源码:transient Object[] elementData;
  • 当创建时没有指定集合的大小时,会有默认的初始容量为0,但第一次加入一个元素后,容量为10,源码:private static final int DEFAULT_CAPACITY = 10;,大小超过10后,增大elementData1.5倍;一开始指定大小也是会自动扩容1.5倍;
Vector集合

Vector集合与ArraryList集合相似,也是一个可变大小的数组,其底层数据结构也是数组,但它是线程安全的,Vector每一个操作都用synchronized修饰了,所以线程安全,但同时也导致了Vector性能较差;在初始容量上,Vector的初始容量为10,需要扩容时,直接增大2倍,和ArraryList存在着差别;

  • ArrayList继承了 AbstractList 类。
  • ArrayList实现了 List 接口,可进行列表的相关操作。
  • ArrayList实现了 Cloneable 接口,可实现克隆。
  • ArrayList实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输
LinkedList集合

该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步;可以通过下标对其进行访问;

  • LinkedList 继承了 AbstractSequentialList 类。
  • LinkedList 实现了 Queue 接口,可作为队列使用。
  • LinkedList 实现了 List 接口,可进行列表的相关操作。
  • LinkedList 实现了 Deque 接口,可作为队列使用。
  • LinkedList 实现了 Cloneable 接口,可实现克隆。
  • LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

LinkedList集合底层实现:

  • 底层的数据结构是双向链表,维护了first和last两个属性,指向首位节点。
  • LinkedList的元素添加和删除,不是通过数组实现的,相对来说效率较高
Set集合

Set集合 不保存重复的元素;Set 接口存储一组唯一,无序的对象。

set集合和list集合的区别:

  • Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
  • Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
  • list查找元素效率高,插入删除效率低,因为会引起其他元素位置改变
HashSet集合

该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。HashSet集合不是线程安全的;

底层实现:

HashSet 的内部采用了HashMap作为数据存储,HashSet其实就是在操作HashMap的key

map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));

map.put(e, PRESENT)==null;

源码说明:hashset初始化时,默认为容量为16,负载因子为0.75的HashMap;对其元素的操作实质上是对HashMap的key的操作,value都为一个Object对象;

LinkedHashSet集合

LinkedHashSet集合是HashSet集合的子类,实现了Set接口,Cloneable接口, java.io.Serializable接口;

底层实现:

LinkedHashSet集合底层是基于HashSet实现的,HashSet中有一个构造函数存在一个参数作为标志,专门用来构造LinkedHashSet集合,dummy = true时为LinkedHashSet集合;这是HashSet的底层实现是LinkedHashMap,它维护了 一个数组和一个双向链表;

LinkedHashSet集合维护了一个hash表和一个双向链表,LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,于是LinkedHashSet集合可以看起来该集合的元素是存在顺序的,而Hashset集合的元素的存储是没有顺序的;

TreeSet集合
Map集合

Map 接口存储一组键值对象,提供key(键)到value(值)的映射。Map不属于集合而是属于一种映射关系,它的实现类有多种,AbstractMap抽象类,SortedMap接口,又间接实现了HashMap,TreeMap等。

HashMap集合

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。HashMap 是无序的,即不会记录插入的顺序。HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。HashMap集合的默认初始化容量是16,默认加载因子是0.75 ,要求线程同步,可以使用ConcurrentHashMap集合

static final int TREEIFY_THRESHOLD = 8;

static final int UNTREEIFY_THRESHOLD = 6;

static final int MIN_TREEIFY_CAPACITY = 64;

底层实现:

HashMap的底层实现是数组+链表+红黑树,当底层数组的某一个索引位置的链表长度大于8,同时数组的长度大于64时,此时这个索引位置上的数据改为使用红黑树存储;当红黑树的长度小于6时,又会转化回链表存储;

具体的存储结构为:维护了一个数组来存储Key-value的节点,transient Node<K,V>[] table;,数组元素为每一条链表的头节点,当出现新的k-v映射时,会添加在数组索引位置的链表的尾部;k-v节点的数据结构为

final int hash;
final K key;
V value;
Node<K,V> next;

即除了记录k-v之外,还会存放当前数组索引位置的hash值,以及指向下一个节点的指针;数组的索引位置通过Key的hashCode方法得到,通过equals方法在链表上判断Key是否存在;

HashMap中put()方法的实现原理:

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

当put一个映射时,会通过hashCode方法得到Key的hash值,得到数组的下标,定位到数组的对应位置,如果该位置上没有K-V,则会新创建一个链表节点,存放该位置的hash值,K-V;如果该位置存在节点,则会遍历该位置的整个链表,通过equals方法判断是否存在与当前key相同的key值,如果存在则返回false,如果不存在则在链表尾端插入一个节点并存放hash值,K-V值,返回true;

HashMap中get()方法的实现原理:

当get一个key时,会通过hashCode方法得到Key的hash值,得到数组的下标,定位到数组的对应位置,如果该位置上没有K-V,则直接返回null,如果存在链表,则会遍历一整个链表,通过equals方法查找是否存在当前的key值,如果不存在则返回null,如果存在,则返回节点上的value值;

HashTable集合

HashTable集合类实现了 Map、Cloneable、java.io.Serializable 接口,继承了Dictionary抽象类,Dictionary与Map相似,同样是K-V的映射;HashTable是线程安全的,每一个对HashTable集合的操作都用了synchronized修饰,所以是同步的;与HashMap不同,HashTable不支持存放null值。int hash = key.hashCode();

HashTable与HashMap的区别:

HashTable是线程安全的,HashMap是线程不安全的;

HashTable不允许null值,但HashMap可以存在null值;

HashMap的初始容量为16,HashTable的初始容量为11;

HashMap的扩容机制为扩容两倍,而HashTable的扩容机制为两倍-1;

HashTable不会转化为红黑树;

底层实现:

HashTable集合的底层数据存储是通过Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。 其数据结构的组织形式与HashMap相似,不同点在于单向链表的结构不同,同时计算索引的方式不同,HashMap计算索引的方式是i = (n - 1) & hash,而Hashtable用的是模运算int index = (hash & 0x7FFFFFFF) % tab.length;

遍历Hashtable的键值对(获取键值集)
第一步:根据entrySet()获取Hashtable的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

ConcurrentHashMap集合

ConcurrentHashMap最大的特点是线程安全的,而且性能较好,因此适合用于高并发的环境;线程安全的Map还可以使用HashTable或者Collections.synchronizedMap()方法实现线程同步,但两者都存在着性能不好的问题,频繁的使用synchronized锁,所以性能不好;

ConcurrentHashMap的优势在于兼顾性能和线程安全,一个线程进行写操作时,它会锁住一小部分,其他部分的读写不受影响,其他线程访问没上锁的地方不会被阻塞;

底层实现:

ConcurrentHashMap底层是由多个Segment 组成的,Segment 本身相当于一个HashMap,Segment 是一个数组结构,所以ConcurrentHashMap相当于两个嵌套的HashMap;

put操作:jdk1.7之前使用ReentrantLock加锁,在jdk1.8之后使用synchronized加锁;自旋锁,对某个Segment 加锁;首先是通过 key 定位到 Segment,之后在对应的 Segment 中进行具体的 put;

get操作:仅使用volatile关键字,保证变量的可见性,不适用其他加锁操作;

LinkedHashMap集合

LinkedHashMap继承了HashMap类,以及实现了Map接口,所以LinkedHashMap集合是基于HashMap的实现的,其不同是,LinkedHashMap是有序的,而HashMap是无序的,因为LinkedHashMap的底层实现还维护了一个双向链表用于记录元素的插入顺序;Entry<K,V> before, after; LinkedHashMap包含了afterNodeRemoval方法,afterNodeInsertion方法,afterNodeAccess方法,分别是删除,插入,访问;

底层实现:

LinkedHashMap是再HashMap的基础上实现的,额外还维护了一条双向链表,用来记录元素的插入位置具体实现是:在newNode方法中,插入一个新的元素时,除了在单向链表上插入一个节点之外,还会向双向链表的尾节点插入这个元素,put一个节点的实现时直接继承了HashMap的put方法,原因是在HashMap中就已经维护了链表;

TreeMap集合

TreeMap继承了AbstractMap,实现了NavigableMap意味着它支持一系列的导航方法,所以TreeMap是有序的; Cloneable, java.io.Serializable接口,TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值