《Java核心技术卷I》阅读笔记 ——泛型、集合
泛型
泛型类:具有一个或多个类型变量的类
- 多个类型变量:
public class Pair<T, U>{}
- 用具体的类型替换类型变量就可以实例化泛型类型
- 多个类型变量:
泛型方法:
- 调用一个泛型方法时,在方法名前的尖括号中放人具体的类型:
String middle = ArrayAlg.<String>getMiddle("]ohn", "Q.", " "Public");
- 调用一个泛型方法时,在方法名前的尖括号中放人具体的类型:
限定类型变量:
<T extends Int & String>
,限定T为int或者String的子类类型擦除:
泛型类会自动生成一个原始类型(raw type),擦除(erased)类型变量,替换为限定类型
程序调用泛型方法时,如果擦除返回类型,编译器会插入强制类型转换,,如下所示,此时编译器会将该调用翻译为:调用原始方法Pair.getFirst,将返回的Object类型强制转换为类型A
1
2Pair<A> buddies = ...;
A buddy = buddies.getFirst();
泛型的约束:
- 不能用类型参数代替基本类型,即,没有
Pair<double>
,只有Pair<Double>
- 运行时类型查询只适用于原始类型,即,
if (a instanceof Pair<String>)
会报错 - 不能创建参数化类型的数组,即
Pair<String>[] table = new Pair<String>[10]
报错,但是声明类型为Pair<String>[]
的变量仍是合法的 - 不能实例化类型变量
- 不能构造泛型数组
- 不能在静态域或方法中引用类型变量
- 不能抛出或捕获泛型类的实例
- 不能用类型参数代替基本类型,即,没有
集合
利用 Java 类库实现传统的数据结构
Java 集合类库将接口( interface)与实现(implementation)分离
链表队列:LinkedList类、循环数组队列:ArrayDeque类
Collection接口:
1
2
3
4
5
6public interface Collection<E>
{
boolean add(E element);
Iterator<E> iterator();
...
}add:向集合添加元素,成功返回True,如果集合中有该元素则返回False
iterator:迭代器,依次访问集合中的元素
包含四个方法:
for each循环:
Collection<String> c = ...; for(String element: c){}
元素被访问的顺序取决于集合类型。对 ArrayList 的迭代,从索引0开始,对 HashSet 迭代,元素出现次序随机
remove 方法会删除上次调用 next 方法时返回的元素,因此调用 remove 之前没有调用 next 是不合法的
Collection泛型常用的方法:
int size()
boolean isEmpty()
boolean contains(Object obj)
boolean allAll(Collection<? extends E> other)
void clear()
Object[] toArray()
集合框架中的接口:
具体的集合
import java.util.*
以 Map 结尾的类实现 Map 接口,其他类实现 Collection 接口


链表
Java 中的链表实际是双向链接(
LinkedList
)添加三个元素。将第二个元素删除
将元素添加到链表中间,则用
iterator
接口中的add方法(第二个元素之间添加juliet)用一个由 Iterator 方法返回、指向链表表头的迭代器调用 add 操作时, 新添加的元素将变成新表头
可以根据需要给容器附加许多迭代器,但只有一个既能读又能写
LinkedList 类提供了一个用来访问某个特定元素的 get 方法,但效率低
数组列表
ArrayList 封装了一个动态再分配的对象数组
Vector 类的所有方法都是同步的,两个线程可以安全地访问一个 Vector 对象
用 get 和 set 方法访问元素
散列集
- 无法控制元素出现的次序
- Java 中散列表用链表数组实现,每个列表称为桶
- 可以指定一个初始的桶数
- Java 集合类库提供一个 HashSet 类,实现了基于散列表的集,用 add 方法添加元素,用 contains 方法快速查看是否某个元素已经出现在集中

树集
- 树集是一个有序集合,以任意顺序将元素插入到集合,遍历时值自动按照排序后的顺序呈现
- 默认使用红黑树

队列与双端队列
- 队列:在尾部添加元素,在头部删除元素
- 双端队列:在头部和尾部同时添加或删除元素
- 不支持在队列中间添加元素
- Deque 接口由 ArrayDeque 和 LinkedList 类实现,两个类提供双端队列,必要时可以增加队列的长度
java.util.Queue<E>
、java.util.Deque<E>
、java.util.ArrayDeque<E>
优先级队列
- 元素任意的顺序插人,按照排序顺序检索——无论何时调用 remove 方法,总获得当前优先级队列中最小的元素(使用堆作为数据结构)
- 优先级队列可以保存实现了 Comparable 接口的类对象, 也可以保存在构造器中提供的 Comparator 对象
- 迭代不是按元素的排列顺序访问
java.util.PriorityQueue<E>
映射
- 集是一个集合,快速查找现有的元素,而映射用来存放键 / 值对
基本的映射操作
- HashMap 和 TreeMap 实现了Map接口——散列映射对键进行散列,树映射用键的整体顺序对元素排序并组织成搜索树
- 散列或比较函数只能作用于键
put
、get
- remove 方法用于从映射中删除给定键对应的元素。size 方法用于返回映射中的元素数
更新映射项
getOrDefault 方法应对第一次插入键值对
merge 方法:
映射视图
键集、 值集合、键 / 值对集(实现了Map.Entry接口的类的对象)
枚举一个映射的所有键
枚举entry
弱散列映射
略
链接散列集与映射
LinkedHashSet 和 LinkedHashMap类用来记住插人元素项的顺序
链接散列映射将用访问顺序,而不是插入顺序,对映射条目进行迭代
标识散列映射
- IdentityHashMap 中, 键的散列值不用 hashCode 函数计算的, 用 System.identityHashCode 方法计算
- 根据对象的内存地址计算散列码
- 比较两个对象时,使用 == ——不同的键对象即使内容相同,也被视为不同的对象
视图与包装器
略
算法
排序与混合
- 使用 List 接口的 sort 方法并传入一个 Comparator 对象
- Collections 类的 shuffle 用于混合
二分查找
- Collections 类的 binarySearch 方法
- 必须提供实现 List 接口的集合
其他算法
集合与数组转换
数组到集合
集合到数组
其他集合
并发访问使用 ConcurrentHashMap
属性映射:
- 键与值都是字符串
- 表可以保存到一个文件中,也可以从文件中加载
- 使用一个默认的辅助表
栈:扩展为Vector类,但原始的栈为
java.util.Stack<E>
BitSet:高效地存储位序列
并发
java.lang.Thread
static void sleep(long millis)
中断线程
当对一个线程调用 interrupt 方法时,线程的中断状态将被置位
线程状态
新建线程:new 操作符创建一个新线程时,线程状态为new
可运行线程:调用 start 方法,线程处于 runnable 状态
被阻塞线程、等待线程
- 当一个线程试图获取一个内部的对象锁,该锁被其他线程持有,线程阻塞
- 当线程等待另一个线程时,进入等待状态——在调用 Object.wait 方法、 Thread.join 方法、等待 java.util.concurrent 库中的 Lock 或 Condition
- 有几个方法有超时参数,调用它们导致线程进入计时等待状态,一直保持到超时期满或者接收到适当的通知——Thread.sleep 和 Object.wait、Thread.join、 Lock,tryLock
被终止线程
同步
锁
用 ReentrantLock 保护代码块的基本结构如下
- 构确保任何时刻只有一个线程进入临界区
- 一旦一个线程封锁了锁对象,其他线程都无法通过 lock 语句
条件对象:条件变量用来自动阻塞一个线程,直到某特殊情况发生唤醒该线程为止
- synchronized 关键字自动提供一个锁以及相关的“条件”