String,StringBuilder,StringBuffer:
String StringBuffer StringBuilder
执行速度 最差 其次 最高
线程安全 线程安全 线程安全 线程不安全(没使用线程同步操作)
使用场景 少量字符串操作 多线程环境下的大量操作 单线程环境下的大量操作
String:
对于String来说,是把数据存放在了常量池中,因为所有的String,默认都是以常量形式保存,且由final修饰,因此在线程池中它是线程安全的。因为每一个String当被创建好了以后,他就不再发生任何变化,但是它的执行速度是最差的。我们要创建String的时候,他在常量池中对这些信息进行处理,如果在程序中出现了大量字符串拼接的工作,效率是非常底下的。因此使用场景是在少量字符串操作的时候才建议直接使用String来操作。
StirngBuffer:(效率不如StringBuilder,但远比String要高):
StringBuffer相对于StringBuilder效率要相对低一点,但也远比String要高的多。效率低的原因:对于StringBuffer来说更多的考虑到了多线程的情况,在进行字符串操作的时候,它使用了synchronize关键字,对方法进行了同步处理。
因此StringBuffer适用于多线程环境下的大量操作。
StringBuilder:(没有考虑线程安全问题):
线程安全与线程不安全:
在进行多线程处理的时候,如果多个线程对于这一个对象同时产生操作,会产生预期之外的结果。对于StringBuilder来说,执行效率虽然高,但是因为线程不安全,所以不建议在多线程的环境下对同一个StringBuilder对象进行操作。
因此StringBuilder适用于单线程环境下的大量字符串操作。
java集合:
List:
ArrayList
:Object[]
数组。
Vector
:Object[]
数组。
LinkedList
:双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
Arraylist扩容机制:
第一种情况,当ArrayList的容量为0时,此时添加元素的话,需要扩容,三种构造方法创建的ArrayList在扩容时略有不同:
1.无参构造,创建ArrayList后容量为0,添加第一个元素后,容量变为10,此后若需要扩容,则正常扩容。
2.传容量构造,当参数为0时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。
3.传列表构造,当列表为空时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。
第二种情况,当ArrayList的容量大于0,并且ArrayList是满的时,此时添加元素的话,进行正常扩容,每次扩容到原来的1.5倍。
Set:
HashSet
(无序,唯一): 基于 HashMap
实现的,底层采用 HashMap
来保存元素。
LinkedHashSet
: LinkedHashSet
是 HashSet
的子类,并且其内部是通过 LinkedHashMap
来实现的。
TreeSet
(有序,唯一): 红黑树(自平衡的排序二叉树)。
比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
1. HashSet
、LinkedHashSet
和 TreeSet
都是 Set
接口的实现类,都能保证元素唯一,并且都不是线程安全的。
2. HashSet
、LinkedHashSet
和 TreeSet
的主要区别在于底层数据结构不同。HashSet
的底层数据结构是哈希表(基于 HashMap
实现)。LinkedHashSet
的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。TreeSet
底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。
3. 底层数据结构不同又导致这三者的应用场景不同。HashSet
用于不需要保证元素插入和取出顺序的场景,LinkedHashSet
用于保证元素的插入和取出顺序满足 FIFO 的场景,TreeSet
用于支持对元素自定义排序规则的场景。
Queue:
PriorityQueue
: Object[]
数组来实现小顶堆。
DelayQueue
:PriorityQueue
。
ArrayDeque
: 可扩容动态双向数组。
Map:
HashMap
:JDK1.8 之前 HashMap
由数组+链表组成的,数组是 HashMap
的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
LinkedHashMap
:LinkedHashMap
继承自 HashMap
,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap
在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
Hashtable
:数组+链表组成的,数组是 Hashtable
的主体,链表则是主要为了解决哈希冲突而存在的。
TreeMap
:红黑树(自平衡的排序二叉树)。
HashMap和HashTable的区别:
1. 线程是否安全: HashMap
是非线程安全的,Hashtable
是线程安全的,因为 Hashtable
内部的方法基本都经过synchronized
修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap
吧!);
2. 效率: 因为线程安全的问题,HashMap
要比 Hashtable
效率高一点。另外,Hashtable
基本被淘汰,不要在代码中使用它;
3. 对 Null key 和 Null value 的支持: HashMap
可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
。
4. 初始容量大小和每次扩充容量大小的不同: ① 创建时如果不指定容量初始值,Hashtable
默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap
默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable
会直接使用你给定的大小,而 HashMap
会将其扩充为 2 的幂次方大小(HashMap
中的tableSizeFor()
方法保证,下面给出了源代码)。也就是说 HashMap
总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
5. 底层数据结构: JDK1.8 以后的 HashMap
在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树),以减少搜索时间(后文中我会结合源码对这一过程进行分析)。Hashtable
没有这样的机制。
HashMap的特性
1.HashMap存储键值对实现快速存取,允许为null。key值不可重复,若key值重复则覆盖。
2.非同步,线程不安全。
3.底层是hash表,不保证有序
HashMap的底层结构
在jdk1.8之前使用的是数组加链表
在jdk1.8以后使用的是数组加链表加红黑表
链表的作用是什么?为什么JDK8引入了红黑树?
链表主要是为了解决数组中的key发生hash冲突时,将发生碰撞的key存到链表中
红黑树主要是为了解决链表过长,的查询速度太慢问题,链表查询时间复杂度为O(n)
当链表长度大于等于8时,就会转变成红黑树,时间复杂度为O(logn)
当链表长度小于等于6时,由红黑树转变回链表,因为链表过短时引入红黑树反而会降低查询速度
HashMap扩容原理
当元素个数大于额定元素个数时,就会触发扩容。
额定元素=数组长度*负载因子(默认是0.75)
扩容后的长度为原来长度的2倍。