Java 集合与数据结构

1. Java数据结构

在这里插入图片描述

1.1 数组(Array)

 1. 需要连续的内存空间。
 2. 按照索引查询速度快。复杂度O(1)
 3. 随机添加删除元素慢。需要移动元素。
 4. 无法扩容。创建的时候就确定。
 5. 数组只能存储一种类型的数据。

1.2 链表(Linked List)

分为:单向链表和双向链表
在这里插入图片描述

 1. 一种递归的数据结构,分为数据部分和节点引用。
 2. 因为存储了节点的引用,故需要更多的存储空间。
 3. 无需连续的内存空间
 4. 不需要初始化容量且链表长度可扩展。
 5. 插入删除速度快,只需要更新引用,无需移动数据。
 6. 查询速度慢,需要遍历链表。复杂度O(n)

栈(Stack)

在这里插入图片描述

1. 按照“后进先出”、“先进后出”的原则来存储数据。

1.3 队列(Queue)

在这里插入图片描述

1. 按照““先进先出”的原则来存储数据。	

1.4 树(Tree)

在这里插入图片描述

1. 非线性结构,它是由 n(n>0)个有限节点组成的一个具有层次关系的集合。
2. 二叉树:每个节点最多包含两个子树。
3. 平衡二叉树:当且仅当任何节点的两棵子树的高度差不大于 1 的二叉树。
4. 红黑树:平衡二叉树的一种,通过颜色的约束来维持着二叉树的平衡。

1.5 堆(Heap)

在这里插入图片描述

1. 堆可以被看做是一棵树的数组对象。
2. 堆中某个节点的值总是不大于或不小于其父节点的值。
3. 堆总是一棵完全二叉树。
4. 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

1.6 图(Graph)

在这里插入图片描述

1. 复杂的非线性结构,由顶点的又穷非空集合和顶点之间边的集合组成。
2. 通常表示为GVE),G表示图,V表示顶点集合,E表示边的集合。
3. 图的节点之间的关系是任意的。

1.7 哈希表(Hash)

1. 是一种可以通过关键码值(key-value)直接访问的数据结构。
2. 快速实现查找、插入和删除。

2. 集合类图

在这里插入图片描述

3. List接口常用的实现类

主要是为顺序存储诞生的,List接口是为了存储一组不唯一的(允许重复)有序的对象。

3.1 ArrayList

1. 底层采用了数组这种数据结构;
2. 非线程安全;
3. 集合类型是Object类型;
4. 初始容量是10,扩容到原容量的1.5倍;

扩容

  1. 数组的扩容是新建一个大容量(原始数组大小+扩充容量)的数组;
  2. 然后将原始数组数据拷贝到新数组(Arrays.copyOf浅复制);
  3. 然后将新数组作为扩容之后的数组。

序列化

  • ArrayList 底层是一个Object[]对象(elementData);
  • elementData 被 transient,表示不能被序列化;
  • 不直接序列化这个对象,是因为这个对象绝大多数情况下会有没有存储任何元素的容量空间。这样将会是一个很大的空间浪费。
  • 而ArrayList 的序列化是对ArrayList 里面每个元素进行序列化的反序列化的。

3.2 LinkedList

1. 底层采用了双向链表的数据结构;
2. 非线程安全;

3.3 Vector

1. 底层采用了数组这种数据结构;
2. 线程安全的,所有的方法都有synchronized关键字修饰,效率低;

3.4 CopyOnWriteArrayList

1. 底层数据结构和和ArrayList一样,不过是线程安全的;

4. Set接口常用的实现类

主要特性是不允许重复的集合。对象存储不可重复性,且无序。

4.1 HashSet

1. HashSet集合在new的时候,底层实际上new了一个HashMap集合。
2.HashSet集合中存储元素,实际上是存储到HashMap集合中的key位置;
3. HashMap集合是一个哈希表数据结构。

4.2 TreeSet

1. TreeSet集合实际是TreeMapnew TreeSet集合的时候,底层实际上new了一个TreeMap集合。
2.TreeSet集合中存储元素,实际上是存储到TreeMap集合中的key位置;
3. TreeSet集合采用了二叉树数据结构。

5. Map接口常用的实现类

主要特征是Key-value。Map会维护与Key对应的值。
在这里插入图片描述

5.1 HashMap

1. 底层是哈希表数据结构。
2. 非线程安全;
3. 初始化容量是16,扩容是之前的2;
4. key和Value都允许为null.

在这里插入图片描述
JDK1.8之前是 数组 + 链表,之后是数组 + 链表 + 红黑树 实现的。

5.1.1 红黑树和链表转换

链表转红黑树的条件

  • 链表长度超过8个;
  • 数据长度大于等于64;如果小于64则会进行扩容。

红黑树转链表的条件

  • 同一个索引位置的节点在移除后数量少于6个;
  • 该索引位置的节点为红黑树节点。

5.1.2 HashMap 重要属性

  • size:已经存储的节点个数;
  • threshold:扩容阈值,当HashMap的个数达到该值,触发扩容。初始化时的容量。
  • loadFactor:负载因子,扩容阈值 = 容量 * 负载因子。负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,所以此时索引效率就会降低。反之浪费空间,但索引效率会提高。

5.1.3 HashMap 插入流程

在这里插入图片描述
其中hash计算方式 = key.hashCode() ^ key >>> 16;
n 为数组长度。

5.1.4 HashMap 扩容流程

在这里插入图片描述

5.1.5 e.hash & oldCap == 0

扩容后,新表的n-1 只比老表的的 n-1 在高位多了一个1。
而这一位的值刚好等于 oldCap。
所以e.hash & oldCap == 0 则新索引位置等于原索引位置;e.hash & oldCap != 0 则新索引位置等于原索引位置 + oldCap 位置。

5.1.6 JDK1.8 做了哪些优化

  1. 数组 + 链表 改成 数组 + 链表 + 红黑树
  2. 头插法改成尾插法
  3. 扩容计算新节点的位置,h & (length -1) 改成 h & oldCap。
  4. hash计算优化,优化成 让高16位参与了运算。

5.1.6 为什么用String类型当HashMap的key

  • String 复写了hashCode方法,且是根据内容计算的hash值,而不是通过地址计算(Object的hashCode)。
  • String 是final 修饰的不可变的,故它的hashCode被缓存下来,不需要再次计算。
  • equals 方法 String自己就有实现。

5.2 HashTable

1. 底层是哈希表数据结构。
2. 线程安全,所有的方法都有synchronized关键字,效率比较低;
3. 初始化容量是11,扩容是:原容量*2+1;
4. key和Value都不允许为null.

5.3 SortedMap

1. 本身不可重复无序,但是此处有自动排序,按照大小进行排序。
2. 此接口的实现类有TreeMap(二叉树结构)。

5.4 ConcurrentHashMap

JDK1.7版本
在这里插入图片描述

1. 线程安全的,采用分段锁实现,默认16Segment(也即并发数)。
2. 由一个个Segment组成,通过继承ReenTrantLock来进行加锁。

JDK1.8版本

1. ConcurrentHashMap结构基本上和Java8HashMap一样。采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作,这里我简要介绍下CAS2. CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。
3. 其中value和next都用volatile修饰,保证并发的可见性。

版本差异对比

1. 数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
2. 保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLockJDK1.8采用CAS+Synchronized保证线程安全。
3. 锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
4. 链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。

5.5 LinkedHashMap

1. LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
2. LinkedHashMap有序,可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)3. LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
4. LinkedHashMap是线程不安全的。

在这里插入图片描述

LinkedHashMap底层是数组+单向链表+双向链表。数组+链表就是HashMap的结构,双向链表维持插入顺序用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值