Java容器
Java容器分类
-
Java容器主要分成两大类:Collection和Map,区别就是Collection是一个槽的,Map是两个槽的(键值对)
-
Collection划分为:List,Set,Queue
- List接口常见有:ArrayList,LinkedList,AbstractList, CopyOnWriteArrayList, Vector(已过时),Stack
- Set接口常见有:HashSet,TreeSet,LinkedHashSet,AbstractSet,CopyOnWriteArraySet,EnumSet
- Queue接口常见有:(阻塞队列)ArrayBlockQueue、PriorityBlockingQueue、LinkedBlockingQueue(双端队列)ArrayDeque、LinkedBlockingDeque、LinkedList
-
Map一般使用:Hashmap、Treemap、Hashtable、AbstractMap、ConcurrentHashMap、 LinkedHashMap、WeakHashMap
-
容器类图
List集合
ArrayList
- ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素(类似于STL vector)
- ArrayList 是一个数组队列,提供了相关的添加、删除、修改、遍历等功能
- 基于动态数组实现
- 适合于顺序添加、随机访问 (RandomAccess 接口标识该类支持快速随机访问) 的场景
- 允许插入为空,允许插入重复数据
- 线程不安全
数组的默认大小为 10;添加元素时候,如果容量不足,需要使用 grow() 方法进行扩容,新容量是旧容量的 1.5
倍,扩容操作还需要将旧数组中的元素 copy 到新数组中,该过程比较耗费性能。 添加一个元素,同样需要 copy
一次,因此该过程是比较耗费性能的。
删除中间某个元素时候,后面元素需要整体向前移动,因此该过程也是耗费性能,而对于删除最后一个元素,则可以直接设为 null,让 gc
垃圾回收机制去回收。
- 常用方法列表
LinkedList
-
Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器
-
与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低
-
以下情况使用 ArrayList :
- 频繁访问列表中的某一个元素
- 只需要在列表末尾进行添加和删除元素操作
-
以下情况使用 LinkedList :
- 你需要通过循环迭代来访问列表中的某些元素
- 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作
-
基于双向链表实现
-
允许插入为空,允许插入重复数据
-
线程不安全
-
LinkedList类图
- LinkedList 继承了 AbstractSequentialList 类
- LinkedList 实现了 Queue 接口,可作为队列使用
- LinkedList 实现了 List 接口,可进行列表的相关操作
- LinkedList 实现了 Deque 接口,可作为双端队列使用,也作为栈使用
- LinkedList 实现了 Cloneable 接口,可实现克隆
- LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输
-
LinkedList常用方法列表
Vector
- 原理与 ArrayList 相同
- 允许插入为空,允许插入重复数据
- 线程安全 (采用 synchronized 同步实现)
- Vector 与 ArrayList 类似,扩容是原大小的 2 倍,采用 synchronized 同步实现,因此开销就比 ArrayList 大
- 可以使用 Collections.synchronizedList(); 得到一个线程安全的 ArrayList。也可以使用 concurrent 并发包下的 CopyOnWriteArrayList 类
- 已过时
Set集合
TreeSet
- 底层基于红黑树实现
- 支持有序性操作,查找,删除,添加等操作都是基于红黑树
- 不允许插入为空
- 线程不安全
- 查找时间复杂度为 O(logN)
HashSet
- 底层基于哈希表实现
- 不支持有序性操作,并且失去元素插入顺序信息
- 允许插入为空,但是最多一个
- 线程不安全
- 查找时间复杂度:O(1)
LinkedHashSet
- 继承自 HashSet,同时使用双向链表维护元素的插入顺序
- 此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代
- LinkedHashSet是迭代有序的HashSet
Queue集合
LinkedList
- 基于双向链表,可以用于实现双向队列
PriorityQueue
- 基于堆结构实现,可以用于实现优先队列
Map集合
Hashtable
- 基于哈希表实现
- key、value 均不能为 null,且 key 不能重复,value 允许重复
- 失去元素插入顺序信息
- 线程安全 (采用 synchronized 同步实现)
- 已过时
HashMap
- 相当于STL的unordered_map
- 基于哈希表实现
- key、value 允许为 null,重复性:key 重复会覆盖,value 允许重复
- 失去元素插入顺序信息
- 线程不安全
- capacity:默认 table 容量为 16,扩容时候保证为 2 的 n 次方
- loadFactor:装载因子,默认是 0.75
- Java8 之前:桶存储的是用链表。而 Java8 之后:链表长度大于 8时会将链表转换为红黑树
- 常用方法列表
LinkedHashMap
- 基于双向链表实现
- key、value 都允许为空,重复性:key 重复会覆盖, value 允许重复
- 此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入键,则插入顺序不受影响
- LinkedHashMap是迭代有序的HashMap
- 线程不安全
TreeMap
- 相当于STL的map
- 基于红黑树实现
- key 不能为 null,value 允许为空,重复性:key 重复会覆盖,value 允许重复
- 根据 key 值进行排序
- 线程不安全
Java多线程编程
Java线程属性
线程状态
- 新建状态:
- 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:
- 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:
- 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:
- 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 死亡状态:
- 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程优先级
- 每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
- Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
- 默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
- 具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
Java Thread类重要方法
Thread对象调用方法
Thread静态方法
线程创建
- Java 提供了三种创建线程的方法:
- 通过实现 Runnable 接口
- 通过继承 Thread 类本身
- 通过 Callable 和 Future 创建线程
下面依次用代码实现这些创建方法
通过实现Runnable接口
- 创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下
public void run()
- 通过重写该方法可以设置线程任务,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。Thread 定义了几个构造方法:
Thread(); Thread(Runnable target); Thread(ThreadGroup group, Runnable target); Thread(String name); Thread(ThreadGroup group, String name); Thread(Runnable target, String name); Thread(ThreadGroup group, Runnable target, String name); Thread(ThreadGroup group, Runnable target, String name, long stackSize); // 实际上面8个构造函数都是调用init方法进行线程初始化 private