1. Tricks
- 在无参构造方法调用有参构造方法
this(...);
,可以实现赋予默认参数
2. Hot Key
- [\alt+\ins] 自动生成constructor, getter, setter
psvm[\tab]
输入public static void main() {}
sou[\tab]
不换行打印;sout[\tab]
换行打印int index = new.Random()[\alt+\enter]
自动载入 java.util.Random;ArrayList<Integer> aList = new ArrayList<>()[\alt+\enter]
自动载入java.util.ArrayListcount.fori[\tab]
自动生成for循环语句for (int i = 0; i < count; i++) {}
3. Application Programming Interface (API)
0. 查阅API文档
- 所在包路径, 便于import (只有java.lang包下的类不用导包)
- 构造方法 (重载情况)
- 成员方法 (查看输入输出列表及类型)
1. java.util.Random
- java.util.Random 是线程安全的(synchronized),但不保证加密安全,多线程需要用 java.util.concurrent.ThreadLocalRandom,加密随机用 java.security.SecureRandom
- 需要 48-bit seed,所以seed 的数据类型要求是Long
随机生成一个Int
java import java.util.Random; int index = new Random().nextInt(10);
返回随机整数生成器,并打印(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));
传参的时候,比如
streamSize
要求long,但是给int也会向上自动进行类型转换,只要给的int不超过int的数据范围,否则应该后面加个L表示long类型的整数Random random = new Random()
时,seed的缺省值是缺省是当前系统时间的毫秒数- 总结
- 生成一个随机数
基本数据类型 变量名 = new Random().next基本数据类型();
- int 需要输入上界bound,返回[0, bound)中的一个随机整数
- double 不需要输入上界,返回[0, 1)之间的一个浮点数
- 生成一个序列
import java.util.stream.基本数据类型Stream;
基本数据类型Stream 变量名 = new Random().基本数据类型s(long streamSize, 基本数据类型 randomNumOrigin, 基本数据类型 randomNumBound);
- 生成一个随机数
2. java.util.Scanner
键盘输入
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
String str = sc.next();
3. Array 数组
- 特点
- 引用类型
- 运行期间长度不可变
- 数组中的元素类型必须统一
- 初始化方式
- 动态初始化 (不确定数组内容, 指定了元素个数)
类型名[] 数组名 = new 类型名[维度]
- 静态初始化 (指定了数组元素的个数与内容)
类型名[] 数组名 = new 类型名[]{对象1, 对象2,..., 对象3}
类型名[] 数组名 = {对象1, 对象2,..., 对象3}
- 动态初始化 (不确定数组内容, 指定了元素个数)
- 可以循环读取,进行索引
数组名[i]
; 直接打印数组, 输出的是十六进制地址值 - 数组中的元素可变. 修改元素的时候, 根据栈中的变量名对应的地址在堆中找到数据, 对数据进行修改而不是新建变量.
- 变量名之间赋值, 传递的是地址值, 并没有创建新数组.
- 异常
- 索引越界 (索引位置超过元素个数)
- 空指针 (索引一个空列表)
- 获取数组长度:
int len = aArray.length
这是成员属性
4. ArrayList 集合
- ArrayList的长度可以随意变换, 而数组不行
- 装在arraylist中的数据必须是同一种类型, 泛型只能是引用类型
- 定义集合
ArrayList<泛型> list = new ArrayList<泛型>();
- 直接打印数组输出地址, 直接打印集合打印的是内容;
- 向集合ArrayList中存储基本类型数据时, 必须使用基本类型的包装类(泛型不能是基本类型)> byte —> Byte
short —> Short
int —> Integer
long —> Long
float —> Float
double —> Double
char —> Character
boolean —> Boolean
- 常用
public boolean add(E, e)
添加元素public E remover(int index)
删除元素public E get(int index)
列表切片public int size()
列表元素个数
5. java.lang.String
- 特点
- 内容不可变,因此可以共享使用
- 效果上相当于字符数组char[], 本质上是比特数组byte[]
- 有且仅有直接写上双引号的字符串,在字符串常量池(堆)中。
String str = "ABC"
直接赋值的字符串,栈中的变量名对应的地址值指向堆中的字符串常量池,里面保存着字符串对象的地址值,这个地址值指向堆中的比特数组byte[]。只要池中有相同的字符串对象,就会重新利用,而不是再创建一个。 - 引用类型之间计算 == ,是比较地址是是否相同;基本类型比的是数据
- 构造
public String()
public String(char[] array)
public String(byte[] array)
- 常用
public boolean equals(Object obj)
比较的是内容,==
比较是地址public boolean equalsIgnoreCase(String str)
public String concat(String str)
拼接字符串public int length()
public char charAt(int index)
public int indexOf(String str)
参数字符串在本字符串中第一次出现的索引位置,未出现返回-1public String substring(int index)
从0索引,截取index到最后public String substring(int begin, int end)
- 拆分数组
public char[] toCharArray()
public byte[] getBytes()
public String replace(CharSequence oldString, CharString newString)
public String[] split(String regex)
regex需要是正则表达式- 大小写
public String toUpperCase()
public String toLowerCase()
6. java.lang.StringBuilder
- String对象的底层是被final修饰的byte[]数组, 创建之后不可改变;
- StringBuilder是字符串缓冲区, 可以提高字符串效率, 占用空间小, 效率高; 如果超出了StringBuilder的容量, 会自动扩容
- 构造方法
StringBuilder sb = new StringBuilder()
StringBuilder sb = new StringBuilder(String str)
String —> StringBuilder
- 成员方法
public StringBuilder append(...)
传入任意类型的数据, 转化为字符串后拼接到StringBuilder对象后面, 返回一个StringBuilderpublic String toString()
StringBuilder —> String
7. java.lang.Object
public boolean equals(Object obj)
两个数据进行比较, 基本类型比较值, 引用数据类型比较地址. 但是String也有同名方法, 她比较的就是内容而不是地址
8. java.util.Date
- 无参构造方法
Date date = new Date();
含参构造方法 (输入类型为long)
java long currentTime = System.currenttimeMillis(); Date date = new Date(currentTime)
获取1970年1月1日距今的毫秒数, 返回类型long
java long time = date.getTime();
Date 对象记录了被创建的那一时刻, 而不是打印的时刻
9. java.text.DateFormat
- 父类为Format类, Format类有子类: DateFormat, NumberFormat, MassageFormat
- DataFormat是一个抽象类,而无法实例化抽象类, 用子类java.text.SimpleDateFormat
- 构造方法
public SimpleDateFormat(Pattern)
模式用下面方式构造, 如:yyyy-MM-dd HH-mm-ss
或yyyy年MM月dd日, HH时mm分ss秒
- 格式化```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
- 不能new, 用该方法获得对象:
Calendar c = Calendar.getInstance();
public int get(int field)
, 其中field应该是不同的字段> Calender.YEAR
Calendar.MONTH
Calender.DATE
Calendar.DAY_OF_MONTH
Calendar.HOUR
Calendar.MINUTE
Calendar.SECOND
public void set(int field, int value)
给指定字段设置值public abstract void add(int filed, int amount)
给指定字段增减一定量public Date getTime()
历元至今的毫秒偏移量(Calendar对象 —> Date对象)
11. java.long.System
- 返回系统当前时间与历元件毫秒偏移量 ,
public static long currentTimeMillis()
- 拷贝原数组中的部分元素到目标数组,
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
4. 包装类
- 概念
- 没有专门操作基本类型的类. 封装起来后用成员方法操作基本类型数据.
- 包装类的名字, 除了int对应Integer, char对应Character, 其他的都是基本变量类型名首字母大写
- 装箱: 基本类型数据 —> 包装类对象
Integer int1 = Integer.valueOf(1998)
Integer int2 = Integer.valueOf("1998")
数字格式化, 如果不能通过格式化转化为对应的基本类型数据, 则会抛出异常
- 拆箱: 包装类对象 —> 基本类型数据
public int intValue(Integer integer)
返回基本类型数据 - JDK1.5之后, 基本类型数据和包装类之间可以相互转换
- 自动装箱:
Integer in = 1
; 相当于Integer in = new Integer(1)
- 自动拆箱:
in = in + 1
相当于in = in.intValue() + 1
- 自动装箱:
- 基本类型数据与字符串之间的转换
- 基本类型转字符串
- 包装类的静态方法 (toString):
String str = Integer.toString(e)
- String类的静态方法 (valueOf):
String str = String.valueOf(e)
- 加上一个空字符串:
String str = 1998 + ""
- 包装类的静态方法 (toString):
- 字符串转基本类型
- 包装类的静态方法:
int i = Integer.parseInt("12345")
,double d = Double.parseDouble("1234.5")
,boolean b = Boolean.parseBoolean("false");
- 包装类的静态方法:
- 基本类型转字符串
5. Collection 集合
结构```java |_Collection (顶层接口) |_List (子接口) | |_Vector (实现类) | |_ArrayList (实现类) | |_LinkedList (实现类) |_Set (子接口)
|_____TreeSet (实现类) |_____HushSet (实现类) |_____LinkedHushSet (实现类)
```
共性提取
- 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()
- Collection: 一些不包含索引的通用方法> public boolean add(E e)
- List: 有序集合; 允许重复; 有索引, 允许遍历;
- Set: 无序集合; 不允许重复; 没有索引, 不允许For循环遍历;
- java.util.Iterator 迭代器接口
- 常用方法:
boolean hasNext()
E next()
使用方法
- 使用集合类中的iterator方法获取迭代器对象,
Iterator<E> iterator()
- 再用Iterator类中的hasNext()方法判断集合中是否还有剩余元素; 用next()方法获取下一个元素;
java Iterator<String> it = col1.iterator(); while (it.hasNext()) { System.out.println(it.next()); }
- 使用集合类中的iterator方法获取迭代器对象,
增强for循环 (格式简单)
java for(元素类型名 变量名 : 集合或数组) { sout(变量名) }
- java.utils.Collections
public static <T> boolean addAll(Collection<T> c, T...elements)
静态方法public static void shuffle(List<?> list)
静态方法, 原地打乱集合元素public static <T> void sort(List<T>)
对自定义类的对象进行排序, 需要重写compareTo方法, 指定比较的规则 (升序排列: this.attr - o.getAttr; 降序排列: o.getAttr - this.attr)public static <T> void sort(List<T>, Comparator<? super T>)
创建一个Comparator实例, 前面的减后面的属性升序, 后面的减前面的属性降序
6. List 集合
java.util.List (Collection的子接口)
- 特点: 有序, 有索引, 元素可重复
- 特有的与索引相关的方法 (谨防索引越界)
public E add(int index, E element)
public E get(int index)
public E remove(int index)
public E set(int index, E element)
- ArrayList
- 多线程查询较快
- 用Array储存数据, 增删数据效率低
- LinkedList
- 多线程, 查询慢
- 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的子接口)
- 特点: 不支持重复元素, 没有索引, 不能遍历
- HashSet
- 特点: Hash Table存储查询效率快; 无序
Set<Integer> set = new HashSet();
- 使用Object类中的
int hashCode()
方法可以获得对象的哈希值(十进制逻辑地址) - 元素不同, 但是哈希值相同, 叫做哈希冲突
- Set会在调用add方法的时候, 调用hashCode方法和equals方法. 判断是否已有该Hash值对应的元素(判断Hash冲突), 如果两个元素的Hash值相同, 再用equals方法判断是否为相同元素. 如果Hash值相同equals方法判断也想同, 则不放入集合; 否则放入集合.
- 底层是 数组+链表 或 数组+红黑树; 如果链表的长度超过了8位, 就会将链表转化为红黑树
- 用HashSet存储自定义类型元素, 需要重写hashCode方法和equals方法, 才能保证唯一 (在class里重写getter/setter/toString/hashCode/equals方法)
- LinkedHashSet
- 特点: 有序, 不允许重复
- 底层是 哈希表(数组+链表/红黑树) + 链表, 多了一个链表记录元素顺序
- 泛型 Generic
- 再不确定数据类型的时候使用泛型作为未知的数据类型
E e: Element
,T t: Type
; 在创建对象的时候, 数据类型会被当做参数传递进去, 确定泛型的类型. - 使用泛型: 明确了储存什么类型, 知道会取出什么类型.
- 不是用泛型: 默认是Object类型, 可以存储任意类型的数据; 但是运行期间可能会引发异常.
- 使用方法
- 在类型名之后加上
<E>
, 即可在类中使用泛型 - 在接口名之后加上
<I>
, 即可在接口中使用泛型; 在实现类中指定接口的泛型, 活在创建实现类对象的时候确定泛型. - 在方法的修饰符和返回值类型之前添加
E
, 即可在方法中使用泛型
- 在类型名之后加上
- 泛型通配符
?
- 不能创建对象使用, 只能在方法的参数列表中使用.
- 泛型的上限限定:
<? extends E>
, 泛型只能是E的子类或本身 - 泛型的下限限定:
<? super E>
, 泛型只能是E的父类或本身
- 再不确定数据类型的时候使用泛型作为未知的数据类型
- 可变参数
- 参数列表中的数据类型确定, 但是参数个数不确定, 可用可变参数; 传入的是一个数组, 数组的长度跟传递进来的参数个数相同.
修饰符 返回值类型 方法名(数据类型...变量名) {}
- 一个方法只能有一个可变参数, 并且其只能放在参数列表的最后.
8. Map 集合
java.util.Map
- 相当于Python中的dict, 有key和value一一对应. key不可重复, value可重复.
- Map是一个接口
- java.util.HashMap
- implements Map
- 底层实现是哈希表,即数组+单向链表/红黑树;JDK1.8 之后,如果链表长度超过8,就自动转化为红黑树。
- 无序,存储、取出元素的顺序可能不同
Map<String, String> map = new HashMap<>();
- implements Map
- java.util.LinkedHashMap
- 底层实现是 哈希表(数组+单向链表/红黑树)+链表, 保证遍历的顺序;
- 继承了 HashMap;
- HashTable
- 是Map接口的一个实现,最早期的双列集合,和HashMap一样底层也是哈希表
- 与HashMap的区别
- key和value都不允许是null(除了HashTable外,所有的集合都可以保存空值空键)
- 是同步的。即线程安全的,单线程的,速度较慢
- JDK1.2之后,Vector集合被ArrayList集合取代,HashTable被HashMap取代。但HashTable的子类Properties依然活跃,是唯一一个与IO流相关的集合。
- 常用的方法
public V put(K key, V value)
- 如果key不重复,返回的v为null;
- 否则会新value覆盖原始的value,并且返回被替换的旧value
public V remove(Object key)
key 存在,返回对应的value,key不存在,返回nullpublic V get(Object key)
key 存在,返回对应的value,key不存在,返回nullboolean containsKey(Object key)
key 存在,返回true,key不存在,返回false- 遍历Map集合
public set<K> keySet()
获取Map集合中的全部键, 存为Set集合;然后通过遍历Set集合中的每个元素,并用Map.get(key)获取value的值。public Set<Map.Entry<K, V>> entrySet()
获取Map中的全部键值对对象的Set集合(装着Entry对象的Set集合)
- Map.Entry
该接口的对象可以记录键值对之间的映射关系
两个方法 getKey(),getValue()
有两种遍历Set集合的方式
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(); }
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
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;
}
}