Java核心技术卷I (3)泛型、集合、并发

《Java核心技术卷I》阅读笔记 ——泛型、集合

泛型

  • 泛型类:具有一个或多个类型变量的类

    image-20220709140209792
    • 多个类型变量:public class Pair<T, U>{}
    • 用具体的类型替换类型变量就可以实例化泛型类型
  • 泛型方法:

    image-20220709140419487
    • 调用一个泛型方法时,在方法名前的尖括号中放人具体的类型:String middle = ArrayAlg.<String>getMiddle("]ohn", "Q.", " "Public");
  • 限定类型变量:<T extends Int & String>,限定T为int或者String的子类

  • 类型擦除:

    • 泛型类会自动生成一个原始类型(raw type),擦除(erased)类型变量,替换为限定类型

      image-20220709142433459
    • 程序调用泛型方法时,如果擦除返回类型,编译器会插入强制类型转换,,如下所示,此时编译器会将该调用翻译为:调用原始方法Pair.getFirst,将返回的Object类型强制转换为类型A

      1
      2
      Pair<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
    6
    public interface Collection<E>
    {
    boolean add(E element);
    Iterator<E> iterator();
    ...
    }
    • add:向集合添加元素,成功返回True,如果集合中有该元素则返回False

    • iterator:迭代器,依次访问集合中的元素

      • 包含四个方法:

        image-20220710144832008
      • 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()
  • 集合框架中的接口:

    image-20220710145502384

具体的集合

  • import java.util.*

  • 以 Map 结尾的类实现 Map 接口,其他类实现 Collection 接口

    image-20220711210702521
image-20220711210726552 image-20220720151551768

链表

  • Java 中的链表实际是双向链接(LinkedList

  • 添加三个元素。将第二个元素删除

    image-20220711210929535
  • 将元素添加到链表中间,则用iterator接口中的add方法(第二个元素之间添加juliet)

    image-20220711211146153
  • 用一个由 Iterator 方法返回、指向链表表头的迭代器调用 add 操作时, 新添加的元素将变成新表头

  • 可以根据需要给容器附加许多迭代器,但只有一个既能读又能写

  • LinkedList 类提供了一个用来访问某个特定元素的 get 方法,但效率低

数组列表

  • ArrayList 封装了一个动态再分配的对象数组

  • Vector 类的所有方法都是同步的,两个线程可以安全地访问一个 Vector 对象

  • 用 get 和 set 方法访问元素

散列集

  • 无法控制元素出现的次序
  • Java 中散列表用链表数组实现,每个列表称为桶
  • 可以指定一个初始的桶数
  • Java 集合类库提供一个 HashSet 类,实现了基于散列表的集,用 add 方法添加元素,用 contains 方法快速查看是否某个元素已经出现在集中
image-20220712125500854

树集

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

队列与双端队列

  • 队列:在尾部添加元素,在头部删除元素
  • 双端队列:在头部和尾部同时添加或删除元素
  • 不支持在队列中间添加元素
  • 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接口——散列映射对键进行散列,树映射用键的整体顺序对元素排序并组织成搜索树
  • 散列或比较函数只能作用于键
  • putget
  • remove 方法用于从映射中删除给定键对应的元素。size 方法用于返回映射中的元素数

更新映射项

  • getOrDefault 方法应对第一次插入键值对

  • merge 方法:

    image-20220712132954128

映射视图

  • 键集、 值集合、键 / 值对集(实现了Map.Entry接口的类的对象)

    image-20220712133053672
  • 枚举一个映射的所有键

    image-20220712133230821
  • 枚举entry

    image-20220712133219132

弱散列映射

链接散列集与映射

  • LinkedHashSet 和 LinkedHashMap类用来记住插人元素项的顺序

    image-20220712133835882
  • 链接散列映射将用访问顺序,而不是插入顺序,对映射条目进行迭代

标识散列映射

  • IdentityHashMap 中, 键的散列值不用 hashCode 函数计算的, 用 System.identityHashCode 方法计算
  • 根据对象的内存地址计算散列码
  • 比较两个对象时,使用 == ——不同的键对象即使内容相同,也被视为不同的对象

视图与包装器

算法

排序与混合

  • 使用 List 接口的 sort 方法并传入一个 Comparator 对象
  • Collections 类的 shuffle 用于混合

二分查找

  • Collections 类的 binarySearch 方法
  • 必须提供实现 List 接口的集合

其他算法

image-20220712135224243

集合与数组转换

  • 数组到集合

    image-20220712135417554
  • 集合到数组

    image-20220712135448186

其他集合

  • 并发访问使用 ConcurrentHashMap

  • 属性映射:

    • 键与值都是字符串
    • 表可以保存到一个文件中,也可以从文件中加载
    • 使用一个默认的辅助表
  • 栈:扩展为Vector类,但原始的栈为java.util.Stack<E>

  • BitSet:高效地存储位序列

    image-20220712135717718

并发

  • java.lang.Thread

  • static void sleep(long millis)

    image-20220712140816121 image-20220712140816121

中断线程

  • 当对一个线程调用 interrupt 方法时,线程的中断状态将被置位

    image-20220712140912258

线程状态

  • 新建线程:new 操作符创建一个新线程时,线程状态为new

  • 可运行线程:调用 start 方法,线程处于 runnable 状态

  • 被阻塞线程、等待线程

    • 当一个线程试图获取一个内部的对象锁,该锁被其他线程持有,线程阻塞
    • 当线程等待另一个线程时,进入等待状态——在调用 Object.wait 方法、 Thread.join 方法、等待 java.util.concurrent 库中的 Lock 或 Condition
    • 有几个方法有超时参数,调用它们导致线程进入计时等待状态,一直保持到超时期满或者接收到适当的通知——Thread.sleep 和 Object.wait、Thread.join、 Lock,tryLock
  • 被终止线程

    image-20220712142241239

同步

    • 用 ReentrantLock 保护代码块的基本结构如下

      image-20220712143455170
      • 构确保任何时刻只有一个线程进入临界区
      • 一旦一个线程封锁了锁对象,其他线程都无法通过 lock 语句
    image-20220712143348266
  • 条件对象:条件变量用来自动阻塞一个线程,直到某特殊情况发生唤醒该线程为止

  • synchronized 关键字自动提供一个锁以及相关的“条件”