java容器学习笔记
容器
容器的组成
容器有两个接口Map和Collection。
collection接口有List类和set类。
List类可以分为:Vector、LinkedList、ArrayList、CopyOnWriteArrayList
Set类可以分为:HashSet、LinkedHashSet、TreeSet
Map接口拥有:HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap
结论:
- 如果是集合类型,有List和Set供我们选择。List的特点是插入有序的,元素是可重复的。Set的特点是插入无序的,元素不可重复的。
- 如果是key-value型,就可以选择Map。如果要保持插入顺序,则可以选择LinkedHashMap,如果不需要则选择HashMap,如果要排序则选择TreeMap。
选择什么样的容器来存储对象,关键在于了解每一个常用集合类的数据结构!
容器的初步了解
List
List集合基础
- 实现了Collection接口
- 特性:有序的,元素可重复的
- 允许元素为null
List常用的子类
- Vector
底层结构是数组,初始容量是10,每次增长2倍。
它是线程同步的,已被ArrayList代替。
- LinkedList
底层结构是双向链表
实现了Deque接口,因此可以向操作栈和队列一样操作它。
线程非同步。
- ArrayList
底层结构是数组,初始容量为10,每次增长1.5倍。
在增删的时候,需要数组的拷贝复制。
线程非同步。
- CopyOnWriteArrayList
原理:在修改时,复制出一个新数组,修改的操作在新数组中完成,最后将新数组交由array变量指向。
写加锁,读不加锁。
缺点: CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。
适合在读多写少的场景中使用。
Set
Set集合基础
- 实现了Collection接口
- 特性:无序的,元素不可重复
- 底层大多数是Map结构的实现
- 常用的三个子类都是非同步的
Set常用的子类
- HashSet
底层数据结构是哈希表(是一个元素为链表的数组) + 红黑树。
实际上就是封装了HashMap。
元素无序,可以为null。
- LinkedHashSet
底层数据结构由哈希表和双向链表组成。
父类是HashSet。
实际上就是LinkHashMap。
元素可以为null。
- TreeSet
底层实际上是一个TreeMap实例(红黑树)。
可以实现排序的功能。
元素不能为null。
Map
Map基础知识
- 存储的结构是key-value键值对,不像Collection是单列集合
- 需要先了解一下散列表和红黑树
Map常用的子类
- HashMap
底层是散列表 + 红黑树。
初始容量为16, 装载因子为0.75,每次扩容2倍。
允许为null,存储无序。
非同步。
散列表容量大于64且链表大于8时,转为红黑树。
Key的哈希值会与该值的高16位做异或操作,进一步增加随机性。
当散列表的元素大于容量 * 装载因子时,会再散列,每次扩容2倍。
如果hashCode相同,key不同则替换元素,否则就是散列冲突。
- LinkedHashMap
底层是散列表 + 红黑树 + 双向链表,父类是HashMap。
允许为null,插入有序。
非同步。
提供插入顺序和访问顺序两种,访问顺序是符合LRU算法,一般用于扩展(默认是插入排序)
迭代与初始容量无关(迭代的是维护的双向链表)
大多使用HashMap的API,只不过在内部重写了某些方法,维护了双向链表。
- TreeMap
底层是红黑树,保证了时间复杂度为log(n)。
可以对其进行排序,使用Comparator或者Comparable。
只要compare或者CompareTo认定该元素相等,那就相等。
非同步。
自然排序(手动排序),元素不能为null。
- ConcurrentHashMap
底层是散列表 + 红黑树,支持高并发操作。
key和value都不能为空。
线程是安全的,利用CAS算法和部分操作上锁实现。
get方法是非阻塞,无锁的。重写Node类,通过volatile修饰next来实现每次获取都是最新设置的值。
在高并发环境下,统计数据(如计算size等)其实是无意义的,因为在下一个时刻size值就变化了。
Collection的功能
1.添加功能
boolean add(Object obj):添加一个元素
boolean addAll(Collection c): 添加一个集合的元素
2.删除功能:
void clear(): 移除所有元素
boolean remove(Object obj): 移除一个元素
boolean removeAll(Collection c): 移除一个集合的元素,只要一个元素被移除了就返回true。
3.判断功能
boolean contain(Object obj): 判断集合是否包含该元素
boolean containsAll(Collection c): 判断集合中是否包含指定的集合元素,只有包含所有元素才叫包含。
boolean isEmpty(): 判断集合是否为空
4.获取功能
Iterator<E> iterator(): 迭代器
5.长度功能
int size(): 元素的个数
6.交集功能
boolean retainAll(Collection c): 移除次collection中未包含在指定collection中的所有元素。集合A和集合B做交集,最终的结果保存在集合A,返回值表示的是A是否发生过变化。
迭代器Iterator
Iterator实际上就是在遍历集合。
遍历集合的步骤:
- 通过结合对象获取迭代器对象
- 通过迭代器对象的hasNext()方法判断是否有元素
- 通过迭代器对象的next()方法获取元素并移动到下一个位置
我们有一个集合:Collection c = new ArrayList();
给集合添加元素:c.add("hello"); c.add("world"); c.add("java");
通过集合获取迭代器对象:Iterator it = c.iterator();
while(it.hashNext()){
String s = (String)it.next();
System.out.println(s);
}
ArrayList解析
图
add方法
1.add(E e)
首先去检查一下数组的容量是否足够:
- 足够:直接添加
- 不足够:扩容:
- 扩容到原来的1.5倍
- 第一次扩容后,如果容量还是小于minCapacity,就将容量扩充为minCapacity。
2.add(int index, E e)
- 检查角标
- 空间检查,如果有需要进行扩容
- 插入元素
get方法
get(int index)
- 检查角标
- 返回元素
set方法
set(int index, E e)
- 检查角标
- 替代元素
- 返回旧值
remove方法
remove(int index)
- 检查角标
- 删除元素
- 计算出需要移动的个数,并移动
- 设置为null,让Gc回收
总结:
- ArrayList是基于动态数组实现的,在增删时候,需要数组的拷贝复制。
- ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
- 删除元素时不会减少容量,若希望减少容量则调用trimToSize()
- 它不是线程安全的。它能存放null值。
Vector与ArrayList
- Vector底层也是数组,与ArrayList最大的区别就是:同步(线程安全)
Vector。 - 在要求非同步的情况下,我们一般都是使用ArrayList来替代Vector的了。
- 如果想要ArrayList实现同步,可以使用Collections的方法:
List list = Collections.synchronizedList(new ArrayList(...))
; - Vector扩展2倍
LinkedList解析
底层是双向链表
方法的一些细节
- add方法实际上就是往链表最后添加元素
- remove方法实际上就是用equals看看这两个元素是否在里面
图
- get方法查看下标,如果下标小于长度的一半就从头遍历,否则从尾遍历
- set方法和get方法其实差不多,根据下标来判断是从头遍历还是从尾遍历
List总结:
- ArrayList:
底层实现是数组
ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
在增删时候,需要数组的拷贝复制(navite 方法由C/C++实现)
- LinkedList:
底层实现是双向链表[双向链表方便实现往前遍历]
- Vector:
底层是数组,现在已少用,被ArrayList替代,原因有两个:
Vector所有方法都是同步,有性能损失。
Vector初始length是10,超过length时,以100%比率增长,相比于ArrayList更多消耗内存。
总的来说:查询多用ArrayList,增删多用LinkedList。
Map的功能
1.添加功能
v put(K key, V value):添加元素
- 如果键是第一次存储,就直接存储,返回null
- 如果键不是第一次存储,就用值把它以前的值替换掉,返回以前的值
2.删除功能
void clear(): 移除所有的键值对元素
v remove(Object key): 根据键删除值,并把值返回
3.判断功能
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty(): 判断集合是否为空
4.获取功能
Set<Map.Empty<K key, V value>> entrySet():返回的是键值对对象的集合
v get(Object key): 根据键获取值
Set<K> keySet():获取集合中所有的键的集合
Collection<V> values(): 获取集合汇总所有的值的集合
5.长度功能
int size(): 返回集合汇总键值对的对数
散列表
首先我们回顾下数据和链表:
链表和数组都可以按照人们的意愿来排列元素的次序,他们可以说是有序的(存储的顺序和取出的顺序是一致的)。但同时,这会带来缺点:想要获取某个元素,就要访问所有的元素,直到找到为止。这会让我们消耗很多的时间在里边,遍历访问元素。
而还有另外的一些存储结构:不在意元素的顺序,能够快速的查找元素的数据
其中就有一种非常常见的:散列表
散列表为每个对象计算出一个整数,称为散列码。根据这些计算出来的整数(散列码)保存在对应的位置上!
在Java中,散列表用的是链表数组实现的,每个列表称之为桶。一个桶上可能会遇到被占⽤的情况(hashCode散列码相同,就存储在同一个位置上),这种情况是无法避免的,这种现象称之为:散列冲突。
- 此时需要用该对象与桶上的对象进行比较,看看该对象是否存在桶上了——如果存在,就不添加了,如果不存在则添加到桶上
- 当然了,如果hashcode函数设计得足够好,桶的数目也足够,这种比较是很少的
- 在JDK1.8中,桶满时会从链表变成平衡二叉树
如果散列表太满,是需要对散列表再散列,创建一个桶数更多的散列表,并将原有的元素插入到新表中,丢弃原来的表:
- 装填因子(load factor)决定了何时对散列表再散列
- 装填因子默认为0.75,如果表中超过了75%的位置已经填入了元素,那么这个表就会用双倍的桶数自动进行再散列
HashMap
图
总结:
- 在JDK8中HashMap的底层是:数组+链表(散列表)+红黑树
- 在散列表中有装载因子这么一个属性,当装载因子*初始容量小于散列表元素时,该散列表会再散列,扩容2倍!
- 装载因子的默认值是0.75,无论是初始大了还是初始小了对我们HashMap的性能都不好
- 装载因子初始值大了,可以减少散列表再散列(扩容的次数),但同时会导致散列冲突的可能性变大(散列冲突也是耗性能的操作,得操作链表(红黑树)!
- 装载因子初始值小了,可以减小散列冲突的可能性,但同时扩容的次数可能就会变多!
- 初始容量的默认值是16,它也一样,无论初始大了还是小了,对我们的HashMap都是有影响的:
- 初始容量过大,那么遍历时我们的速度就会受影响
- 初始容量过小,散列表再散列(扩容的次数)可能就变得多,扩容也是一件非常耗费性能的事
- 从源码上我们可以发现:HashMap并不是直接拿key的哈希值来用的,它会将key的哈希值的高16位进行异或操作,使得我们将元素放入哈希表的时候增加一定的随机性。
- 还要值得注意的是:并不是桶上有8位元素的时候它就能变成红黑树,它得同时满足我们的散列表容量大于64才行。
TreeMap
- TreeMap实现了NavigableMap接口,而NavigableMap接口继承着SortedMap接口,致使我们的TreeMap是有序的!
- TreeMap底层是红黑树,它方法的时间复杂度都不会太高:log(n)
- 非同步
- 使用Comparator或者Comparable来比较key是否相等与排序的问题
注意:
- TreeMap有序是通过Comparator来进行比较的,如果comparator为null,那么就使用自然顺序。
- key值不能为null。
总结:
TreeMap底层是红黑树,能够实现该Map集合有序。如果在构造方法中传递了Comparator对象,那么就会以Comparator对象的方法进行比较。否则,则使
用Comparable的compareTo(T o)
方法来比较。值得说明的是:
- 如果使用的是
compareTo(T o)
方法来比较,key一定是不能为null,并且得实现了Comparable接口。 - 即使是传入了Comparator对象,不用
compareTo(T o)
方法来比较,key也是不能为null的。
要点:
- 由于底层是红黑树,那么时间复杂度可以保证为log(n)
- key不能为null,为null为抛出NullPointException的
- 想要自定义比较,在构造方法中传入Comparator对象,否则使用key的自然排序来进行比较
- TreeMap非同步,想要同步可以使用Collections来封装
Set集合总结
- HashSet:
无序,允许为null,底层是HashMap(散列表+红黑树),非线程同步 - TreeSet:
有序,不允许为null,底层是TreeMap(红黑树),非线程同步 - LinkedHashSet:
迭代有序,允许为null,底层是HashMap+双向链表,非线程同步
java容器学习笔记的更多相关文章
- java—容器学习笔记
一:迭代器 刚开始学容器,做了个简单的练习题.. import java.util.ArrayList; import java.util.Collection; import java.util.I ...
- 尚学堂JAVA基础学习笔记
目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...
- Java Web学习笔记之---JSP
Java Web学习笔记之---JSP (一)JSP常用语法 (1)HTML注释 <!--所要注释的内容 --> 在客户端显示一个注释. (2)隐藏注释 <%--所要注释的内容--% ...
- 20145213《Java程序设计学习笔记》第六周学习总结
20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...
- [原创]java WEB学习笔记95:Hibernate 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- java JDK8 学习笔记——助教学习博客汇总
java JDK8 学习笔记——助教学习博客汇总 1-6章 (by肖昱) Java学习笔记第一章——Java平台概论 Java学习笔记第二章——从JDK到IDEJava学习笔记第三章——基础语法Jav ...
- java JDK8 学习笔记——第16章 整合数据库
第十六章 整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API中的接口会有数据库厂商操作,称为JDBC驱动程 ...
- [原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
随机推荐
- Web漏洞小结
本文是对Web中最常见漏洞的一个小结: 注入类漏洞: SQL注入:SQL注入漏洞详解 XML注入:XXE(XML外部实体注入) 代码注入:代码注入漏洞 CRLF注入:CRLF注入 注入类漏洞是应用违背 ...
- (1) arm 指令格式
arm 指令的基本格式如下: <opcode>{<cond>}{S}{.W\.N} <Rd>,<Rn>{,<operand2>} opcod ...
- 检查dtd和Xschema文件限制下的xml文件是否符合的Java文件
先来xml文件: 1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE orders SY ...
- 在C++中调用Python
技术背景 虽然现在Python编程语言十分的火爆,但是实际上非要用一门语言去完成所有的任务,并不是说不可以,而是不合适.在一些特定的.对于性能要求比较高的场景,还是需要用到传统的C++来进行编程的.但 ...
- [笔记] c和指针
1.简介 2.基本概念 3.指针 计算机内存的最小单位是位(bit),每个位可以容纳值0或1,单独的位用处不大,通常许多位合成一组作为一个单位,以存储较大范围的值 每个字节包含8位,可以存储无符号值0 ...
- x小结:certutil -hashfile D:\1.exe MD5
在Win7上,MD5不要使用小写,在Win10上没有这个问题 x小结:certutil -hashfile D:\1.exe MD5certutil -hashfile D:\1.exe SHA1ce ...
- 041.Python守护进程,锁信号量和事件
一 守护进程 1.1 基本概念 守护进程 正常情况下,主进程默认等待子进程调用结束之后结束 守护进程在主进程执行代码结束后,自动终止 守护进程语法: 进程对象.daemon = True ,设置该进程 ...
- Python 简单的龟鱼游戏
游戏编程:按一下要求定义一个乌龟类和鱼类并尝试编程 假设游戏场景为范围(x,y)为 0<=x<=10,0<=y<=10 游戏生成1只乌龟和10条鱼 他们的移动方向均随机 乌龟的 ...
- mysql基础之mysql主从架构半同步复制
一.概念 1.异步复制(Asynchronous replication) MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样 ...
- CoSky 高性能 服务注册/发现 & 配置中心
CoSky 基于 Redis 的服务治理平台(服务注册/发现 & 配置中心) Consul + Sky = CoSky CoSky 是一个轻量级.低成本的服务注册.服务发现. 配置服务 SDK ...