Map映射
操作
根据键来找值(键值对)
key不可重复,value可以
Hashmap
定义
一个数组装着各个链表的头节点,通过计算一个元素的哈希值,找到数组下标,并在对应链表后面增加一个新的节点。
扩容
第一次使用时初始化,在必要时进行扩容,且大小永远是2的倍数(power of 2)
如果只扩容链表长度,查询速度将会越来越慢–>增长数组长度可以解决这个问题
当一支链表(单链表)长度过长(大于8),使用红黑树(JDK1.8以上)。数组存放根节点,二分查找提升查找速率,而且旋转次数比平衡二叉树少。
负载因子
用于判断挡前是否需要进行扩容
插入新的元素之后,当前的数组占用率到达n%以上则需要扩容
扩容时会重新计算所有元素的哈希值,得到新的下标并得到新的哈希表。
默认0.75
LinkedHashmap
普通的Hashmap的存放顺序和输入顺序是没有关系的,按照哈希值的计算结果摆放,而LinkedHashmap记录的before和after即存放的前后关系,每一个节点都是双向链表。
访问顺序(accessOrder)可以使得最近访问过的元素放在最后。
Treemap
自动维护顺序的一种map,可以看作是在维护一颗红黑树
stream流
比较像流水线,通过链式调用,持续操作。对数据有大量复杂连续操作时使用。
流在运行时会先记录所有步骤,建立完整流水线再进行操作
summaryStatistics.getMax()//统计类
stream一般不返回直接结果,而是返回optional类,这样是为了避免空指针出现异常
数组工具Arrays
操作数组不像集合那样方便,所以提供操作数组的工具方法
public static void main(String[] args) {
int[] array = {1, 5, 2, 4, 7, 3, 6};
Arrays.sort(array); //直接进行排序(底层原理:进行判断,元素少使用插入排序,大量元素使用双轴快速/归并排序)
System.out.println(array); //由于int[]是一个对象类型,而数组默认是没有重写toString()方法,因此无法打印到想要的结果
System.out.println(Arrays.toString(array)); //我们可以使用Arrays.toString()来像集合一样直接打印每一个元素出来
Arrays.sort(array);
System.out.println("排序后的结果:"+Arrays.toString(array));
System.out.println("目标元素3位置为:"+Arrays.binarySearch(array, 3)); //二分搜素,必须是已经排序好的数组!
//array array2为多维数组
System.out.println(Arrays.deepToString(array)); //使用deepToString就能到打印多维数组
System.out.println(Arrays.deepEquals(array2, array)); //使用deepEquals就能比较多维数组
}
print只能打印StringArrays.asList()
来将数组转换为一个 固定长度的List 不可使用add和remove等
集合类也有相关的简化操作工具Collections
集合类
集合类其实就是为了更好地组织、管理和操作我们的数据而存在的,包括列表、集合、队列、映射等数据结构。
集合类最顶层不是抽象类而是接口,因为接口代表的是某个功能,而抽象类是已经快要成形的类型,不同的集合类的底层实现是不相同的,同时一个集合类可能会同时具有两种及以上功能(既能做队列也能做列表),所以采用接口会更加合适,接口只需定义支持的功能即可。
小测试
反转链表
思考操作元素的位置:先走到表尾,再依次回到表头
类似递归概念,故考虑使用递归方法。
public static Node reverse(Node head) {
//在这里实现
if (head.next == null){
return head;
}
Node result = reverse(head.next);
head.next.next = head;
head.next = null;//这一行自己没想到
return result;
}
重建二叉树
描述:给出前序、中序,还原整个二叉树。
前序遍历:可以直接得到根节点的位置
中序遍历:通过得到的根节点的值,找到根节点的位置,得到左子树节点值,从而依次找到新的根节点的位置。
步骤
1.得到根节点的值
2.得到根节点的位置
3.将根节点左右序列分开,并重构为根节点的左右子树
4.在新的序列中重复上述步骤,直到分割到只剩下一个节点时开始回溯,重建整个二叉树
实现计算器
实现包含加减乘除、小数的计算公式的计算
步骤
使用两个栈,一个存放数字,一个存放运算。依次存放数字和运算符,当存放下一个运算符时,栈中已有两个数字和一个运算符,且下一个运算符的优先级小于等于上一个时进行运算,将结果放入数字栈中。
细节
//Deque
private static Deque<Character> opr = new LinkedList<>();
双端队列,两头均可进和出。
Character 是 char 对应的包装类,JDK1.5以后可以自动封装自动解封,
char ch='a';
Character ch1=ch;//自动封箱
Character c=new Character(a);
char c1=c;//自动解封
使用封装类可以获得更多的方法。
同理还有double和Double、long和Long
(str[i]-'0')
可以获得该char对应的数字
import java.util.*;
import java.util.LinkedList;
import java.util.stream.Collectors;
public class RebuildTree {
private static Deque<Character> opr = new LinkedList<>();
private static Deque<Double> number = new LinkedList<>();
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
String formula = scanner.nextLine();
char[] str = formula.toCharArray();
int i = 0;
while(i < str.length){
char c = str[i];
if(isOpr(c)){
Character peek = opr.peek();
while (peek != null && isHigherOpr(peek, c)){
cal();
peek = opr.peek();
}
opr.push(c);
i++;
}
else{
double tempNum = 0;
//小数分开
double tempNum2 = 0;
int times = 1;//小数位数
boolean flag = Boolean.FALSE;
//限制长度
while(i < str.length && !isOpr(str[i])){
if(str[i] == '.')flag = Boolean.TRUE;
else{
if(!flag){
tempNum = tempNum * 10 + (str[i] - '0');
}
else{
double val = str[i]-'0';
for(int j =0; j < times; j++)val /= 10;
tempNum2 = tempNum2 + val;
times ++;
}
}
i ++;
}
number.push(tempNum + tempNum2);
}
}
while(!opr.isEmpty()) cal();
System.out.println(number.peek());
}
private static boolean isHigherOpr(char peek, char c){
//True 则需要做运算
return (c == '+' || c == '-') || (peek == '*' || peek == '/');
}
private static boolean isOpr(char c){
return c == '+' || c == '-' || c == '*' || c == '/';
}
private static void cal(){
double a = number.pop();
double b = number.pop();
char c = opr.pop();
switch (c){
case '+':
number.push(a + b);
break;
case '-':
number.push(b - a);
break;
case '*':
number.push(a * b);
break;
case '/':
number.push(b / a);
break;
}
}
}
字符串匹配–KMP算法