[笔记]Java基本数据集合概略

目录

Collection、List、Set、Map概述
Collection
Map
HashMap的实现原理
TreeMap的红黑树
Collections和Arrays辅助类
Iterator、ListIterator和Foreach三种遍历
Concurrent包

Collection、List、Set、Map概述

Collection
........|--------List
........|..........|----------ArrayList
........|..........|----------Vector
........|..........|.............|-----Stack
........|..........|----------LinkedList
........|--------Set
...................|----------HashSet .
...................|.............|-----LinkedHashSet
...................|----------SortedSet
.................................|-----TreeSet
 
Iterator
.....|-------ListIterator
 
Map
.....|------Hashtable
.....|..........|------Properties
.....|------HashMap
.....|..........|------LinkedHashMap
.....|------WeakHashMap
.....|------SortedMap
................|------TreeMap

Collection .
●..实现该接口及其子接口的所有类都可应用clone()方法,并是序列化类.

.....List.
.....●..可随机访问包含的元素
.....●..元素是有序的
.....●..可在任意位置增、删元素
.....●..不管访问多少次,元素位置不变
.....●..允许重复元素
.....●..用Iterator实现单向遍历,也可用ListIterator实现双向遍历

..........ArrayList
..........●..用数组作为根本的数据结构来实现List
..........●..元素顺序存储
..........●..新增元素改变List大小时,内部会新建一个数组,在将添加元素前将所有数据拷贝到新数组中
..........●..随机访问很快,删除非头尾元素慢,新增元素慢而且费资源
..........●..较适用于无频繁增删的情况
..........●..比数组效率低,如果不是需要可变数组,可考虑使用数组
..........●..非线程安全
.
..........Vector .
..........●..另一种ArrayList,具备ArrayList的特性
..........●..所有方法都是线程安全的(双刃剑,和ArrayList的主要区别)
..........●..比ArrayList效率低

...............Stack
...............●..LIFO的数据结构

..........LinkedList.
..........●..链接对象数据结构(类似链表)
..........●..随机访问很慢,增删操作很快,不耗费多余资源
..........●..非线程安全

.....Set .
.....●..不允许重复元素,可以有一个空元素
.....●..不可随机访问包含的元素
.....●..只能用Iterator实现单向遍历

..........HashSet
..........●..用HashMap作为根本数据结构来实现Set
..........●..元素是无序的
..........●..迭代访问元素的顺序和加入的顺序不同
..........●..多次迭代访问,元素的顺序可能不同
..........●..非线程安全

...............LinkedHashSet
...............●..基于HashMap和链表的Set实现
...............●..迭代访问元素的顺序和加入的顺序相同
...............●..多次迭代访问,元素的顺序不便
...............●..因此可说这是一种有序的数据结构
...............●..性能比HashSet差
...............●..非线程安全

..........SortedSet
..........●..加入SortedSet的所有元素必须实现Comparable接口
..........●..元素是有序的

...............TreeSet .
...............●..基于TreeMap实现的SortedSet
...............●..排序后按升序排列元素
...............●..非线程安全

-----------------------------------
 
Iterator ..
●..对Set、List进行单向遍历的迭代器

..........ListIterator.
..........●..对List进行双向遍历的迭代器

-----------------------------------

Map
●..键值对,键和值一一对应
●..不允许重复的键.

.....Hashtable.
.....●..用作键的对象必须实现了hashcode()、equals()方法,也就是说只有Object及其子类可用作键
.....●..键、值都不能是空对象
.....●..多次访问,映射元素的顺序相同
.....●..线程安全的

..........Properties
..........●..键和值都是字符串

.....HashMap
.....●..键和值都可以是空对象
.....●..不保证映射的顺序
.....●..多次访问,映射元素的顺序可能不同
.....●..非线程安全

...............LinkedHashMap
...............●..多次访问,映射元素的顺序是相同的
...............●..性能比HashMap差

.....WeakHashMap ..
.....●..当某个键不再正常使用时,垃圾收集器会移除它,即便有映射关系存在
.....●..非线程安全

.....SortedMap.
.....●..键按升序排列
.....●..所有键都必须实现.Comparable.接口.

...............TreeMap .
...............●..基于红黑树的SortedMap实现
...............●..非线程安全

Collection

Collection主要有List和Set两种,主要成员结构如下:

Collection
|-List-
| |-LinkedList
| |-ArrayList(:>AbstractList:>AbstractCollection:>Collection implements List)
| |-Vector-
| | |-Stack
|-Set-
| |-HashSet-
| | |-LinkedHashSet
| |-TreeSet

遍历
Collection继承Iterable,所以要通过iterator来遍历数据,这其实是一个迭代器模式。
Collection只能iterate,不能get
Collections
java.util.Collections是一个帮助类,可以帮助各种collection对象sort、max等函数操作。
List
有序数据集合,重要的是次序,元素可以重复出现,可以通过下标直接找到元素,List可以get。
其中:
LinkedList是链表,插入删除更快。另外,linkedlist可以对链表的最前面和最后面进行处理,这样就可以把它用作栈、队列、双向队列。
ArrayList、Vector是数组,数组的随机访问效率更高(直接下标找到元素)。
ArrayList是可变数组,capacity容量可以自动增加,增加方式是((旧容量 * 3) / 2) + 1,如果需要插入大量元素,可以用ensureCapacity来自主增加容量,也可以在初始化时指定初始容量List lst=new ArrayList<>(500)。
ArrayList里专门用一个size变量管理元素的真正个数,而不是容器的容量。
Vector在操作方法上加了Synchronized同步,实现线程安全(并不是绝对安全,因为在锁方法之外的配合会出问题)。
Vector增加了同步机制,所以使用迭代器时可能与修改操作冲突,此时会抛出异常ConcurrentModificationException,必须捕获。
Vector实现了Cloneable,在clone中调的是系统的System.arraycopy函数,这是个native函数,不是在java层做的,而是交给了更底层,所以效率更高。
Vector扩容时,增加方式是原容量+增长系数capacityIncrement,如果该系数<=0,就直接原容量*2。
Vector不支持序列化,而ArrayList支持序列化。
Stack在Vector的基础上实现了先进后出,增加了push、pop、peek等方法。
LinkedList其实同时实现了List接口和Deque双链表接口,这两个接口又都是Collection的子接口:

[笔记]Java基本数据集合概略_第1张图片
LinkedList的继承关系

LinkedList和Stack的区别,Stack加了同步锁和栈操作函数,LinkedList只是有做栈的潜力。
和数组对比,所有的List性能都不如数组(因为List需要管理内部的可变数组,扩容还需要拷贝),但是数组要求元素类型固定、数组长度固定
Set
无序数据集合,重要的是元素,元素不能重复出现,只能迭代查询元素
List里可以有多个null,但是Set里只能有1个null(元素不能重复出现)
Set和Collection是一样的接口,只是行为不同,是典型的多态。
Set实现了Clonable,但是与List利用native实现arraycopy不同,Set最终是native的internalClone。
Set有HashSet和TreeSet两种。
HashSet其实是用HashMap实现的,实际上是把元素作为key存入HashMap的,至于value值,统一使用了一个空的Object对象。
HashSet有一个子类LinkedHashSet,查询速度与HashSet一致,但是额外维护了一个链表,保存元素的插入次序。
LinkedHashSet底层是通过LinkedHashMap来保存数据的(通过HashSet创建LinkedHashMap),操作都是通过调用父类实现的。
concurrent
java.util.concurrent包里有CopyOnWriteArraySet和CopyOnWriteArrayList,简称COW,核心思想是读写分离,平时共享一个容器,当需要修改时,先复制一份出来,在副本中修改,修改完后,再把原容器指向副本,这样不需要加锁同步,如果主要业务是读取数据集,而不是修改数据集,这种操作就很有优势。

Map

Map中的key是一个set,value是一个collection,这样key不可重复,value可以重复。
Collection和Map
Collection是对象的集合,Collection接口继承了Iterable
Map是键值对的集合,Map接口没有父接口。
Collection和Map没有直接关系。
Map
Java中用Map存储健值对,键不可以重复,值可以重复。
java.util.Map有四个实现类:

Map
|-HashTable
|-HashMap-
| |-LinkedHashMap
| |-WeakHashMap
|-TreeMap
|-IdentityHashMap

Map中的元素有内置的顺序,如果要保持添加顺序,可以使用LinkedHashMap,如果要按照大小排序,可以使用TreeMap。
Map通过Hash散列存储数据,速度比ArrayList更快。

HashTable有内置同步,是线程安全的,不能存储null值。
HashMap没有做同步,但是可以存储1个null值。

HashMap的实现原理

HashMap链表散列,是数组+链表的结合,默认使用一个长度16的数组,数组中每个元素都是一个链表的表头。
Entry,数组和链表中,每个节点都是一个HashMapEntry,HashMapEntry包括key、value、hash(int值),next(指向下一个HashMapEntry)。
hash值和分配,对于每个数据,都会计算一个int型的hash值(key和value对象各自作为object的hashcode,做异或操作),hashCode是基类Object定义的方法(具体算法在c++实现,与地址、偏向锁等有关),这个hash值对数组的长度(默认16)取余,根据得到的余数,分配到数组中的对应链表的最后面(解决hash冲突)(Android是放到最后面)。
hashcode和equal
equal就是利用hashcode,如果覆写了其中一个,就要一起配套的覆写另一个。
内存使用,占用比较大,原因包括:
1.数组本身默认长度16,会占用内存
2.容量扩充时,每次2,判断是否需要扩容的公式是:数据量>容量加载因子(默认0.75),扩容时是移位运算+或运算,最后加1,可以快速得到一个2的N次幂作为目标容量值。
3.每次扩容时新建一个Entry数组,在旧数组中遍历链表,重新分配位置(如果不在原位置,就向后移动2次幂的位置)
4.扩容后的最大值不超过231-1(如果当期值是230,则直接赋值为2^31)
遍历,删改查元素时,先计算hash值定位到数组位置,然后遍历查找链表。
key重复,因为key根据hash值判断重复,所以如果key是个对象,加入HashMap后,又修改了key,hash值变化,就会重复插入,且旧的key就无法再查询到。
HashTable
HashTable和HashMap的结构和操作基本一致,但是HashTable增加了线程安全。
HashMap的key和value都可以为null。
但是因为同步的原因,HashTable的key/value值不允许为空,concurrent包里的ConcurrentHashMap的value值也不允许为空。从源码上,遇到null的value值会抛出nullpointexception异常;从设计上,因为是并发操作可能删除键值对,这样在取出key的value时,无法判断是key没有对应的value还是对应的value为null。
ConcurrentHashMap
ConcurrentHashMap和HashMap的结构和操作也基本一致,也是实现了线程安全,但是HashTable是锁住整个表,ConcurrentHashMap只会锁住要操作的节点,只在处理size时锁整个表
WeakHashMap
WeakHashMap是对key做了弱引用,这样不影响回收。
LinkedHashMap的实现原理
LinkedHashMap是HashMap的子类,但他输出和顺序和输入的顺序相同,因而适合LRU等操作。
LinkedHashMapEntry在HashMap中Entry的基础上,增加了两个指针before和after,这样既是Hash表,又是双向链表,链表顺序就是读写顺序。
LinkedHashMap有一个固定位置的header,如果在LRU模式下,读过的Entry会插到header前面,所以header前一定是最近访问过的,header后一定是最久没访问过的。
LinkedHashMap遍历速度一般比HashMap慢,因为链表比数组慢,速度只和数据量有关,而HashMap和容量有关,如果容量巨大数据量很小,HashMap反而不占优势。
TreeMap的实现原理
TreeMap是数据是排序过的,实现了SortMap接口,保存的数据是按键值排序的(comparator),它是个红黑树数据结构,是唯一有subMap()方法的Map。
IdentifyHashMap
用==代替equals对键值做比较。
SparseArray和ArrayMap
SparseArray稀疏数组,是两个数组的结合,是Android特有的api,优点是特别节省内存,效率上在小数据量时占优,大数据量不占优。
SparseArray中的key是int值,LongSparseArray中的key是long值。
ArrayMap中的key是object值。
SparseArray和ArrayMap的实现原理类似,都是用两个数组结合,一个存放key,一个存放value,对key使用二分法从小到大排序,查找/添加/删除/都需要先使用二分法查找,找到key所在的index后,根据index进行操作(keyAt(index),valueAt(index))。
SparseArray和ArrayMap都适用于千级以下的数据,数据量过大时,二分查找性能退化严重。

TreeMap的红黑树

TreeMap其实就是维持了一个红黑树,红黑树实现非常复杂,但它性能很好,在最坏情况下也能保持很好的性能,查找/插入/删除的事件复杂度为O(log n),因为它的平衡性非常好,从根到叶子的最长可能路径不超过最短可能路径的两倍,红黑树有4个特定:
1.节点是红或黑
2.根是黑的
3.叶子是黑
4.每个红节点的两个子节点都是黑(从根到叶没有连续的红)
5.任一节点到每个叶子,黑节点数量相同
4+5合起来,就是最长不超过最短的两倍。
插入操作
插入时总是插入红节点,这样可以避免对红黑树做调整,而且只可能破坏性质2或者性质4,这可以分情况递归调整。
删除操作
普通二叉树的删除中,叶子节点和独生子节点的删除相对简单,有两个子节点的时候删除就比较麻烦,需要使用左子树的最大元素(所有右子树中没有子树的那个节点就是最大元素)或右子树的最小元素,并调整二叉树。
红黑树的删除增加了删除后空节点的黑色属性,新的节点是原色+黑色,如果是红+黑就不用处理,如果是黑+黑就要分情况讨论,递归处理。
经典数据结构-红黑树详解

Collections和Arrays

Collections是个数据集合的辅助类,提供了各种查找和排序方法,如折半查找、逆序、交换等;Collections还能封装,把集合转换成特殊集合,如同步集合、只读集合等。
Arrays是个数组的辅助类,可以对数组中的值进行比较、查找、删改、排序。

Iterator、ListIterator和Foreach

Iterator是对列表单向遍历,不需要知道集合及其元素的类型(所以可以抽象出迭代器设计模式),并可以直接在迭代中增删改数据。
ListIterator可以对列表双向遍历。
Foreach是基于Iterator实现的,但是它需要知道集合中元素的类型。
For是按照数组下标查询,所以在数组中效率最高,在链表中效率最低

Cuncurrent包

Java从5.0提供了java.util.cuncurrent.*并发包,主要是基于volatile变量和AbstractQueuedSynchronizer(AQS同步器)
volatile易变变量一方面在写内存时有内存屏障锁,避免指令重排序,另一方面在读之前会立即从主内存同步数据,原子性、一致性和有序性,他能做到后两者。
AQS同步器是通过1个整型的volatile变量维持同步状态,
并发容器,并发容器比较注重并发性,尽量不用锁,比如CopyOnWrite侧重于多读少写的场景,写的时候重建容器,确保正在读的线程不受影响;Cuncurrent也是多用volatile实现读不加锁,仅在put等修改操作上加锁。
cuncurrent包中具体提供了如下结构:
CunCurrentHashMap
ReentrantLock
Condition
CopyOnWriteArrayList
CopyOnWriteArraySet
ArrayBlockingQueue
ThreadPoolExecutor
Future、FutureTask

引用

Java集合及concurrent并发包总结(转)
Android HashMap源码详解
Android内存优化(使用SparseArray和ArrayMap代替HashMap)
设计模式汇总:结构型模型(上)
JAVA LinkedList和ArrayList的使用及性能分析
Collection,List,Set和Map用法和区别
Android HashMap源码详解
HashMap的扩容机制---resize()
经典数据结构-红黑树详解
HashSet的存储方式是把HashMap中的Key作为Set的对应存储项

你可能感兴趣的:([笔记]Java基本数据集合概略)