1. Tricks

  1. 在无参构造方法调用有参构造方法this(...);,可以实现赋予默认参数

2. Hot Key

  1. [\alt+\ins] 自动生成constructor, getter, setter
  2. psvm[\tab] 输入public static void main() {}
  3. sou[\tab] 不换行打印;sout[\tab] 换行打印
  4. int index = new.Random()[\alt+\enter] 自动载入 java.util.Random;ArrayList<Integer> aList = new ArrayList<>()[\alt+\enter] 自动载入java.util.ArrayList
  5. count.fori[\tab] 自动生成for循环语句 for (int i = 0; i < count; i++) {}

3. Application Programming Interface (API)

0. 查阅API文档

  1. 所在包路径, 便于import (只有java.lang包下的类不用导包)
  2. 构造方法 (重载情况)
  3. 成员方法 (查看输入输出列表及类型)

1. java.util.Random

  1. java.util.Random 是线程安全的(synchronized),但不保证加密安全,多线程需要用 java.util.concurrent.ThreadLocalRandom,加密随机用 java.security.SecureRandom
  2. 需要 48-bit seed,所以seed 的数据类型要求是Long
  3. 随机生成一个Intjava import java.util.Random; int index = new Random().nextInt(10);

  4. 返回随机整数生成器,并打印(Lambda表达式)java import java.util.Random; import java.util.stream.IntStream; /* first param: streamSize 数量,不给该参数,生成器无限制返回随机数 second param: randomNumOrigin 包括下界 third param: randomNumBound 不包括上界 */ IntStream randomInts = new Random().ints((long)5, 4, 10); randomInts.forEach(item -> System.out.print(item));

  5. 传参的时候,比如streamSize要求long,但是给int也会向上自动进行类型转换,只要给的int不超过int的数据范围,否则应该后面加个L表示long类型的整数

  6. Random random = new Random()时,seed的缺省值是缺省是当前系统时间的毫秒数
  7. 总结
    1. 生成一个随机数 基本数据类型 变量名 = new Random().next基本数据类型();
      1. int 需要输入上界bound,返回[0, bound)中的一个随机整数
      2. double 不需要输入上界,返回[0, 1)之间的一个浮点数
    2. 生成一个序列
      1. import java.util.stream.基本数据类型Stream;
      2. 基本数据类型Stream 变量名 = new Random().基本数据类型s(long streamSize, 基本数据类型 randomNumOrigin, 基本数据类型 randomNumBound);

2. java.util.Scanner

键盘输入

  1. import java.util.Scanner;
  2. Scanner sc = new Scanner(System.in);
  3. int num = sc.nextInt();
  4. String str = sc.next();

3. Array 数组

  1. 特点
    1. 引用类型
    2. 运行期间长度不可变
    3. 数组中的元素类型必须统一
  2. 初始化方式
    1. 动态初始化 (不确定数组内容, 指定了元素个数)
      类型名[] 数组名 = new 类型名[维度]
    2. 静态初始化 (指定了数组元素的个数与内容)
      类型名[] 数组名 = new 类型名[]{对象1, 对象2,..., 对象3}
      类型名[] 数组名 = {对象1, 对象2,..., 对象3}
  3. 可以循环读取,进行索引 数组名[i]; 直接打印数组, 输出的是十六进制地址值
  4. 数组中的元素可变. 修改元素的时候, 根据栈中的变量名对应的地址在堆中找到数据, 对数据进行修改而不是新建变量.
  5. 变量名之间赋值, 传递的是地址值, 并没有创建新数组.
  6. 异常
    1. 索引越界 (索引位置超过元素个数)
    2. 空指针 (索引一个空列表)
  7. 获取数组长度: int len = aArray.length 这是成员属性

4. ArrayList 集合

  1. ArrayList的长度可以随意变换, 而数组不行
  2. 装在arraylist中的数据必须是同一种类型, 泛型只能是引用类型
  3. 定义集合 ArrayList<泛型> list = new ArrayList<泛型>();
  4. 直接打印数组输出地址, 直接打印集合打印的是内容;
  5. 向集合ArrayList中存储基本类型数据时, 必须使用基本类型的包装类(泛型不能是基本类型)> byte —> Byte
    short —> Short
    int —> Integer
    long —> Long
    float —> Float
    double —> Double
    char —> Character
    boolean —> Boolean
  1. 常用
    1. public boolean add(E, e) 添加元素
    2. public E remover(int index) 删除元素
    3. public E get(int index) 列表切片
    4. public int size() 列表元素个数

5. java.lang.String

  1. 特点
    1. 内容不可变,因此可以共享使用
    2. 效果上相当于字符数组char[], 本质上是比特数组byte[]
    3. 有且仅有直接写上双引号的字符串,在字符串常量池(堆)中。 String str = "ABC"直接赋值的字符串,栈中的变量名对应的地址值指向堆中的字符串常量池,里面保存着字符串对象的地址值,这个地址值指向堆中的比特数组byte[]。只要池中有相同的字符串对象,就会重新利用,而不是再创建一个。
    4. 引用类型之间计算 == ,是比较地址是是否相同;基本类型比的是数据
  2. 构造
    1. public String()
    2. public String(char[] array)
    3. public String(byte[] array)
  3. 常用
    1. public boolean equals(Object obj) 比较的是内容,== 比较是地址
    2. public boolean equalsIgnoreCase(String str)
    3. public String concat(String str) 拼接字符串
    4. public int length()
    5. public char charAt(int index)
    6. public int indexOf(String str) 参数字符串在本字符串中第一次出现的索引位置,未出现返回-1
    7. public String substring(int index) 从0索引,截取index到最后
    8. public String substring(int begin, int end)
    9. 拆分数组
      1. public char[] toCharArray()
      2. public byte[] getBytes()
    10. public String replace(CharSequence oldString, CharString newString)
    11. public String[] split(String regex) regex需要是正则表达式
    12. 大小写 public String toUpperCase() public String toLowerCase()

6. java.lang.StringBuilder

  1. String对象的底层是被final修饰的byte[]数组, 创建之后不可改变;
  2. StringBuilder是字符串缓冲区, 可以提高字符串效率, 占用空间小, 效率高; 如果超出了StringBuilder的容量, 会自动扩容
  3. 构造方法
    1. StringBuilder sb = new StringBuilder()
    2. StringBuilder sb = new StringBuilder(String str) String —> StringBuilder
  4. 成员方法
    1. public StringBuilder append(...) 传入任意类型的数据, 转化为字符串后拼接到StringBuilder对象后面, 返回一个StringBuilder
    2. public String toString() StringBuilder —> String

7. java.lang.Object

  1. public boolean equals(Object obj) 两个数据进行比较, 基本类型比较值, 引用数据类型比较地址. 但是String也有同名方法, 她比较的就是内容而不是地址

8. java.util.Date

  1. 无参构造方法
    Date date = new Date();
  2. 含参构造方法 (输入类型为long)java long currentTime = System.currenttimeMillis(); Date date = new Date(currentTime)

  3. 获取1970年1月1日距今的毫秒数, 返回类型longjava long time = date.getTime();

  4. Date 对象记录了被创建的那一时刻, 而不是打印的时刻

9. java.text.DateFormat

  1. 父类为Format类, Format类有子类: DateFormat, NumberFormat, MassageFormat
  2. DataFormat是一个抽象类,而无法实例化抽象类, 用子类java.text.SimpleDateFormat
    1. 构造方法 public SimpleDateFormat(Pattern)
      模式用下面方式构造, 如: yyyy-MM-dd HH-mm-ssyyyy年MM月dd日, HH时mm分ss秒
    2. 格式化```java import java.text.SimpleDateFormat; import java.util.Date;

public class DemoSDF { public static void main(String[] args) { SimpleDateFormat sdf = new SimpleDateFormat(“yyyy年MM月dd日 HH时mm分ss秒”); Date date = new Date();

    String dateFormated = sdf.format(date);
    System.out.println(dateFormated);
}

}


   3. 解析, 如果解析失败需要抛出异常, 会要求你在函数中写上 `throw ParseException````java
    public static Date parse(String str) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        return sdf.parse(str);
    }

10. java.utils.Calendar

  1. 不能new, 用该方法获得对象: Calendar c = Calendar.getInstance();
  2. public int get(int field), 其中field应该是不同的字段> Calender.YEAR
    Calendar.MONTH
    Calender.DATE
    Calendar.DAY_OF_MONTH
    Calendar.HOUR
    Calendar.MINUTE
    Calendar.SECOND
  1. public void set(int field, int value) 给指定字段设置值
  2. public abstract void add(int filed, int amount) 给指定字段增减一定量
  3. public Date getTime() 历元至今的毫秒偏移量(Calendar对象 —> Date对象)

11. java.long.System

  1. 返回系统当前时间与历元件毫秒偏移量 , public static long currentTimeMillis()
  2. 拷贝原数组中的部分元素到目标数组, public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

4. 包装类

  1. 概念
    1. 没有专门操作基本类型的类. 封装起来后用成员方法操作基本类型数据.
    2. 包装类的名字, 除了int对应Integer, char对应Character, 其他的都是基本变量类型名首字母大写
  2. 装箱: 基本类型数据 —> 包装类对象
    1. Integer int1 = Integer.valueOf(1998)
    2. Integer int2 = Integer.valueOf("1998") 数字格式化, 如果不能通过格式化转化为对应的基本类型数据, 则会抛出异常
  3. 拆箱: 包装类对象 —> 基本类型数据
    public int intValue(Integer integer) 返回基本类型数据
  4. JDK1.5之后, 基本类型数据和包装类之间可以相互转换
    1. 自动装箱: Integer in = 1; 相当于Integer in = new Integer(1)
    2. 自动拆箱: in = in + 1 相当于in = in.intValue() + 1
  5. 基本类型数据与字符串之间的转换
    1. 基本类型转字符串
      1. 包装类的静态方法 (toString): String str = Integer.toString(e)
      2. String类的静态方法 (valueOf): String str = String.valueOf(e)
      3. 加上一个空字符串: String str = 1998 + ""
    2. 字符串转基本类型
      1. 包装类的静态方法: int i = Integer.parseInt("12345"), double d = Double.parseDouble("1234.5"), boolean b = Boolean.parseBoolean("false");

5. Collection 集合

  1. 结构```java |_Collection (顶层接口) |_List (子接口) | |_Vector (实现类) | |_ArrayList (实现类) | |_LinkedList (实现类) |_Set (子接口)

     |_____TreeSet (实现类)
     |_____HushSet (实现类)
     |_____LinkedHushSet (实现类)
    

    ```

  2. 共性提取

    1. Collection: 一些不包含索引的通用方法> public boolean add(E e)
      public boolean contains(E e)
      public boolean isEmpty(E e)
      public boolean remove(E e)
      public int size()
      public Object[] toArray()
      public void clear()
  1. List: 有序集合; 允许重复; 有索引, 允许遍历;
  2. Set: 无序集合; 不允许重复; 没有索引, 不允许For循环遍历;
    1. java.util.Iterator 迭代器接口
  3. 常用方法:
    1. boolean hasNext()
    2. E next()
  4. 使用方法

    1. 使用集合类中的iterator方法获取迭代器对象, Iterator<E> iterator()
    2. 再用Iterator类中的hasNext()方法判断集合中是否还有剩余元素; 用next()方法获取下一个元素;java Iterator<String> it = col1.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
  5. 增强for循环 (格式简单)java for(元素类型名 变量名 : 集合或数组) { sout(变量名) }

  1. java.utils.Collections
    1. public static <T> boolean addAll(Collection<T> c, T...elements) 静态方法
    2. public static void shuffle(List<?> list) 静态方法, 原地打乱集合元素
    3. public static <T> void sort(List<T>) 对自定义类的对象进行排序, 需要重写compareTo方法, 指定比较的规则 (升序排列: this.attr - o.getAttr; 降序排列: o.getAttr - this.attr)
    4. public static <T> void sort(List<T>, Comparator<? super T>)
      创建一个Comparator实例, 前面的减后面的属性升序, 后面的减前面的属性降序

6. List 集合

java.util.List (Collection的子接口)

  1. 特点: 有序, 有索引, 元素可重复
  2. 特有的与索引相关的方法 (谨防索引越界)
    1. public E add(int index, E element)
    2. public E get(int index)
    3. public E remove(int index)
    4. public E set(int index, E element)
  3. ArrayList
    1. 多线程查询较快
    2. 用Array储存数据, 增删数据效率低
  4. LinkedList
    1. 多线程, 查询慢
    2. java.util.LinkedList 有增删首尾元素的操作:> public void addFirst(E e)
      public void addLast(E e)
      public void push(E e) // 相当于 addFirst, 压栈

      public E getFirst()
      public E getLast() public E removeLast()
      public E removeFirst()
      public E pop() // 相当于 removeFirst, 出栈 public void clear() // 清空链表元素
      public boolean isEmpty()

7. Set 集合

java.util.Set (Collection的子接口)

  1. 特点: 不支持重复元素, 没有索引, 不能遍历
  2. HashSet
    1. 特点: Hash Table存储查询效率快; 无序
    2. Set<Integer> set = new HashSet();
    3. 使用Object类中的int hashCode()方法可以获得对象的哈希值(十进制逻辑地址)
    4. 元素不同, 但是哈希值相同, 叫做哈希冲突
    5. Set会在调用add方法的时候, 调用hashCode方法和equals方法. 判断是否已有该Hash值对应的元素(判断Hash冲突), 如果两个元素的Hash值相同, 再用equals方法判断是否为相同元素. 如果Hash值相同equals方法判断也想同, 则不放入集合; 否则放入集合.
    6. 底层是 数组+链表数组+红黑树; 如果链表的长度超过了8位, 就会将链表转化为红黑树
    7. 用HashSet存储自定义类型元素, 需要重写hashCode方法和equals方法, 才能保证唯一 (在class里重写getter/setter/toString/hashCode/equals方法)
  3. LinkedHashSet
    1. 特点: 有序, 不允许重复
    2. 底层是 哈希表(数组+链表/红黑树) + 链表, 多了一个链表记录元素顺序
  4. 泛型 Generic
    1. 再不确定数据类型的时候使用泛型作为未知的数据类型 E e: Element, T t: Type; 在创建对象的时候, 数据类型会被当做参数传递进去, 确定泛型的类型.
    2. 使用泛型: 明确了储存什么类型, 知道会取出什么类型.
    3. 不是用泛型: 默认是Object类型, 可以存储任意类型的数据; 但是运行期间可能会引发异常.
    4. 使用方法
      1. 在类型名之后加上<E>, 即可在类中使用泛型
      2. 在接口名之后加上<I>, 即可在接口中使用泛型; 在实现类中指定接口的泛型, 活在创建实现类对象的时候确定泛型.
      3. 在方法的修饰符和返回值类型之前添加E, 即可在方法中使用泛型
    5. 泛型通配符 ?
      1. 不能创建对象使用, 只能在方法的参数列表中使用.
      2. 泛型的上限限定: <? extends E>, 泛型只能是E的子类或本身
      3. 泛型的下限限定: <? super E>, 泛型只能是E的父类或本身
  5. 可变参数
    1. 参数列表中的数据类型确定, 但是参数个数不确定, 可用可变参数; 传入的是一个数组, 数组的长度跟传递进来的参数个数相同.
    2. 修饰符 返回值类型 方法名(数据类型...变量名) {}
    3. 一个方法只能有一个可变参数, 并且其只能放在参数列表的最后.

8. Map 集合

java.util.Map

  1. 相当于Python中的dict, 有key和value一一对应. key不可重复, value可重复.
  2. Map是一个接口
  3. java.util.HashMap
    1. implements Map
    2. 底层实现是哈希表,即数组+单向链表/红黑树;JDK1.8 之后,如果链表长度超过8,就自动转化为红黑树。
    3. 无序,存储、取出元素的顺序可能不同
    4. Map<String, String> map = new HashMap<>();
  4. java.util.LinkedHashMap
    1. 底层实现是 哈希表(数组+单向链表/红黑树)+链表, 保证遍历的顺序;
    2. 继承了 HashMap;
  5. HashTable
    1. 是Map接口的一个实现,最早期的双列集合,和HashMap一样底层也是哈希表
    2. 与HashMap的区别
      1. key和value都不允许是null(除了HashTable外,所有的集合都可以保存空值空键)
      2. 是同步的。即线程安全的,单线程的,速度较慢
    3. JDK1.2之后,Vector集合被ArrayList集合取代,HashTable被HashMap取代。但HashTable的子类Properties依然活跃,是唯一一个与IO流相关的集合。
  6. 常用的方法
    1. public V put(K key, V value)
      1. 如果key不重复,返回的v为null;
      2. 否则会新value覆盖原始的value,并且返回被替换的旧value
    2. public V remove(Object key)
      key 存在,返回对应的value,key不存在,返回null
    3. public V get(Object key)
      key 存在,返回对应的value,key不存在,返回null
    4. boolean containsKey(Object key)
      key 存在,返回true,key不存在,返回false
    5. 遍历Map集合
      1. public set<K> keySet() 获取Map集合中的全部键, 存为Set集合;然后通过遍历Set集合中的每个元素,并用Map.get(key)获取value的值。
      2. public Set<Map.Entry<K, V>> entrySet() 获取Map中的全部键值对对象的Set集合(装着Entry对象的Set集合
    6. Map.Entry 该接口的对象可以记录键值对之间的映射关系
      两个方法 getKey(),getValue()
  7. 有两种遍历Set集合的方式

    1. Enhanced For:java for (K key: map.keySet()) { // map.keySet().for + \tab V value = map.get(key); } for (Map.Entry<K, V> entry: map.entrySet()) { K key = entry.getKey(); V value = entry.getValue(); }

    2. While Loop:```java Iterator it1 = map.keySet().iterator(); // map.keySet().iterator().var + \tab while (iterator.hasNext()) { K key = iterator.next(); V value = map.get(key); }

Iterator> it2 = map.EntrySet().iterator(); while (it2.hasNext()) { Map.Entry = it2.next(); K key = entry.getKey(); V value = entry.getValue(); }


8. 用HashMap储存自定义类型的键值对
   1. Map集合要保证key是唯一的,必须重写 hashCode() 方法和 equals() 方法(用`\alt + \ins` 直接自动生成);而对value不做要求
   2. 想要打印的时候输出的不是地址值,就需要重写toString方法
9. JDK9对于集合添加元素的优化
   1. 单列集合用 add 方法,双列集合用 map 方法
   2. 现在List,Set,Map集合中都添加了静态的 of 方法来构造不可变的实例;
      1. `List<String> list = List.of("aaa", "bbb", "ccc");`
      2. `Map<K, V> map = Map.of(k1, v1, k2, v2)`
      3. `Set<String> set = Set("aaa", "bbb", "ccc")`
   3. 注意事项
      1. 因为元素不可变,所以适用于当集合中存储的元素已经确定的情况
      2. 只适用于List,Set,Map接口,而不适用于接口的实现类
      3. 返回不可改变的集合,不能再用 add 方法或 put 方法来添加元素了

<a name="6ba56687"></a>
## 9. 数据结构

1. 数据存储的常用结构: 栈, 队列, 数组, 链表, 红黑树
2. 栈 Stack
   1. 先进后出
   2. 入口出口在同一侧, 都在栈的顶端操作
3. 队列 Queue
   1. 先进先出
   2. 入口出口在不同侧, 队首元素先出队, 入队的排在队尾
4. 数组 Array
   1. 查询快 (数组的地址是连续的, 通过首地址找到数组, 通过数组的索引查找元素)
   2. 增删慢 (想要增删元素, 必须创建新数组, 并把原数组的数据复制过来; 原数组被垃圾回收而销毁)
5. 链表 LinkedList
   1. 增删快 (增删元素只需要修改临近元素的指针域, 对整体链表没有影响)
   2. 查询慢 (链表元素的地址并不连续, 每次查询元素必须从头一次遍历)
6. 红黑树 Red-Black Tree
   1. 树:
      1. 深度: Root Node --> Leaf Node的路径
      2. 高度: Child Node --> Leaf Node的路径
   2. 二叉树: 分支不能超过两个
   3. 排序树/查找树: 子树结点从左到右依次增大
   4. 平衡二叉树: 任一结点的左子树和右子树的高度差至多等于1
   5. 红黑树: 查询叶子结点的最大次数和最小次数不能超过2倍
      1. 结点可以使黑色或红色的
      2. 叶子结点, 根节点, 每个红色结点的子节点都是黑色的
      3. 每个点点到每一个叶子结点的所有路径上的黑色节点数相同
      4. 查询的速度非常快

<a name="2f8acf05"></a>
## 10. 斗地主发牌练习

```java
package Demo10;

import java.util.*;

public class DemoFightTheLandlord {
    public static void main(String[] args) {
        final Map<Integer, String> cards = prepareCards();
        List<Integer> indices = shuffledIndices(cards.size());
        List<ArrayList<Integer>> allSets = dealCards(indices);

        ArrayList<String> cards1 = seeCards(cards, allSets.get(0));
        ArrayList<String> cards2 = seeCards(cards, allSets.get(1));
        ArrayList<String> cards3 = seeCards(cards, allSets.get(2));
        ArrayList<String> cardsLeft = seeCards(cards, allSets.get(3));

    }

    public static Map<Integer, String> prepareCards() {
        List<String> colours = List.of("Spade", "Heart", "Diamond", "Club");
        List<String> numbers = List.of("2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");

        Map<Integer, String> cards = new LinkedHashMap<>();

        cards.put(0, "Big_Joker");
        cards.put(1, "Small_Jocker");

        for (String number : numbers) {
            for (String colour : colours) {
                cards.put(cards.size(), colour + '_' + number);
            }
        }

        return cards;
    }

    public static List<Integer> shuffledIndices(int cardsSize) {
        List<Integer> indices = new ArrayList<>();
        for (int i = 0; i < cardsSize; i++) {
            indices.add(i);
        }
        Collections.shuffle(indices);
        return indices;
    }

    public static List<ArrayList<Integer>> dealCards(List<Integer> indices) {
        ArrayList<Integer> p1 = new ArrayList<>();
        ArrayList<Integer> p2 = new ArrayList<>();
        ArrayList<Integer> p3 = new ArrayList<>();
        ArrayList<Integer> left = new ArrayList<>();

        for (int i = 0; i < indices.size() - 3; i++) {
            if (i % 3 == 0) {
                p1.add(indices.get(i));
            } else if (i % 3 == 1) {
                p2.add(indices.get(i));
            } else {
                p3.add(indices.get(i));
            }
        }

        for (int i = indices.size() - 3; i < indices.size(); i++) {
            left.add(indices.get(i));
        }

        List<ArrayList<Integer>> allSets = List.of(p1, p2, p3, left);
        return allSets;
    }

    public static synchronized ArrayList<String> seeCards(Map<Integer, String> cards, ArrayList<Integer> indices) {
        ArrayList<String> cardsForOne = new ArrayList<>();
        Collections.sort(indices);

        for (int index : indices) {
            cardsForOne.add(cards.get(index));
        }

        return cardsForOne;
    }
}