import java.util.Deque;
import java.util.LinkedList;
import java.util.Queue;
public class LinkedListClass {
public static void main(String[] args) {
//创建队列 - 先进先出
Queue<String> queue = new LinkedList<>();
//进出队列的方法
queue.offer("怪物猎人");
queue.offer("赛博朋克2077");
System.out.println(queue);
//获取队列头元素的方法
String s1 = queue.peek();//只是查看访问
System.out.println("队列头:" + s1);
System.out.println("队列" + queue);
s1 = queue.poll();//获取队列头元素同时将元素从队列中弹出
System.out.println("队列头:" + s1);
System.out.println("队列" + queue);
//创建双端队列
Deque<String> deque = new LinkedList<>();
//将指定的元素添加为此列表的尾部(最后一个元素)。
deque.offer("怪物猎人");
deque.offer("赛博朋克2077");
System.out.println(deque);
//在列表前端插入元素
deque.offerFirst("黎明杀机");
System.out.println(deque);
//在列表末尾添加元素
deque.offerLast("杀戮尖塔");
System.out.println(deque);
//检索但不删除队列头部
s1 = deque.peekFirst();
System.out.println("队列头:" + s1);
System.out.println("队列" + deque);
//检索但不删除队列尾部
s1 = deque.peekLast();
System.out.println("队列尾:" + s1);
System.out.println("队列" + deque);
//检索并且删除队列头部
s1 = deque.pollFirst();
System.out.println("队列头:" + s1);
System.out.println("队列" + deque);
//检索并删除队列尾部
s1 = deque.pollLast();
System.out.println("队列尾:" + s1);
System.out.println("队列" + deque);
//双向队列的方法基本上都有头和尾两个实现方法
}
}
数组: 物理空间连续, 自带位置, 快速定义到任意位置-查询效率高
链表: 物理空间不连续, 结点为单位, 只能从头结点访问
add方法效率:
import java.util.ArrayList;
import java.util.LinkedList;
public class EfficiencyOfAdd {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 10000000; i++) {
arrayList.add(0);
linkedList.add(0);
}
//因为数组列表的物理内存是连续的,所以插入元素需要将插入位置元素之后包括插入位置的元素全部后移
long time1 = System.currentTimeMillis();
arrayList.add(0, 1);
long time2 = System.currentTimeMillis();
System.out.println("arraylist的首位插入速度:" + (time2 - time1));
//链表则需要从头或者尾节点开始访问,直到访问到下标对应元素停止,但是插入元素只需要修改1~3个元素
time1 = System.currentTimeMillis();
linkedList.add(0, 1);
time2 = System.currentTimeMillis();
System.out.println("linkedList的头部节点插入速度:" + (time2 - time1));
//但数组列表插入元素的下标越大,那么后移元素越少,插入时间就越短
time1 = System.currentTimeMillis();
arrayList.add(5000000, 1);
time2 = System.currentTimeMillis();
System.out.println("arraylist的index = 5000000插入速度:" + (time2 - time1));
//链表的插入元素下标越靠近中间,需要访问的元素就越多,导致访问的时间变长,最后抵消了插入元素本身的效率提升
time1 = System.currentTimeMillis();
linkedList.add(5000000, 1);
time2 = System.currentTimeMillis();
System.out.println("linkedList的index = 5000000插入速度:" + (time2 - time1));
//故综合效率还是ArrayList列表较高
}
}
get方法效率:
import java.util.ArrayList;
import java.util.LinkedList;
public class EfficiencyOfGet {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
LinkedList<Integer> linkedList = new LinkedList<>();
for (int i = 0; i < 10000000; i++) {
arrayList.add(0);
linkedList.add(0);
}
//因为数组列表的物理内存是连续的,所以可以直接访问下标对应元素
long time1 = System.currentTimeMillis();
arrayList.get(5000000);
long time2 = System.currentTimeMillis();
System.out.println("arraylist的查询速度:" + (time2 - time1));
//链表则需要从头或者尾节点开始访问,直到访问到下标对应元素停止,所以效率低
time1 = System.currentTimeMillis();
linkedList.get(5000000);
time2 = System.currentTimeMillis();
System.out.println("linkedList的查询速度:" + (time2 - time1));
}
}
在这里插入代码片public class TreeClass<T> {
private class Node<T>{
private T data;
private Node<T> left;//左叶子节点
private Node<T> right;//右叶子节点
//构造方法
public Node(T data) {
this.data = data;
}
}
//定义根节点
private Node<T> root;
//遍历节点后将节点添加到指定位置
private void addToNode(Node<T> node, T data){
//如果根节点数值为空,将新节点赋值给根节点
if(root == null){
root = new Node<T>(data);
return;
}
//如果新节点值小于父节点则遍历下一个左叶子节点 原来的值已经是可比较的了
if(((Comparable)data).compareTo(node.data) < 0){
//如果左节点为空就将新节点加入
if(node.left == null){
node.left = new Node<T>(data);
return;
}
//左节点不为空继续遍历左节点
addToNode(node.left, data);
}else if(((Comparable)data).compareTo(node.data) > 0){//如果新节点值大于当前节点 就遍历当前节点右节点
//如果右节点为空将新节点加入
if(node.right == null){
node.right = new Node<T>(data);
return;
}
//如果不为空再次调用addToNode方法比较右节点与data值大小
addToNode(node.right, data);
}
}
//遍历
private void travelNode(Node<T> node){
//先遍历左节点
if(node.left != null){
travelNode(node.left);
}
//如果左节点为空则输出节点值
System.out.println(node.data);
//在遍历右节点
if(node.right != null){
travelNode(node.right);
}
}
//add外部调用方法
public void add(T data){
addToNode(root, data);
}
//二叉树遍历外部调用方法
public void travel(){
travelNode(root);
}
}
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
public class Demo03Stack {
public static void main(String[] args) {
// Deque当你使用它的 push 和 pop 方法时, 他就变成了栈
// 链表栈
Deque<String> stack = new LinkedList<>();
stack.push("a");
stack.push("b");
stack.push("c");
System.out.println(stack);
String s = stack.pop();
System.out.println("栈顶元素: " + s);
System.out.println("栈: " + stack);
// 栈: 数组栈
Stack<String> stack2 = new Stack<>();
stack2.push("h");
stack2.push("i");
System.out.println(stack2);
String s2 = stack2.pop();
System.out.println("stack2的栈顶: " + s2);
}
}
Set(I): API 和 Collection 的API 一样
|- HashSet: 散列表/无序的, 不是随机!! 不允许重复元素
结论: hashCode和equals要一起重写
注意: equals方法判断相同的两个对象, hashCode方法结果一定要一致
equals判断不相同的两个对象, hashCode结果要尽量不一样
import java.util.Objects;
public class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//equals方法定义比较的方式和比较内容
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
//hashCode方法用来寻找被比较的链表元素位置
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo {
public static void main(String[] args) {
//创建接口Set的实现类HashSet对象
Set<Integer> set0 = new HashSet<>();
//HashSet列表不能加入重复元素
set0.add(0);
set0.add(0);
set0.add(1);
set0.add(1);
set0.add(2);
set0.add(2);
System.out.println(set0);
Set<String> set1 = new HashSet<>();
//HashSet列表为散列结构,位置经过特殊算法获得故与加入顺序不同
set1.add("黎明杀机");
set1.add("怪物猎人");
set1.add("赛博朋克2077");
System.out.println(set1);
Set<Student> set2 = new HashSet<>();
//1.HashSet表在加入元素时先调用元素的hashCode方法计算出元素hash值相应位置
//2.找到位置后如果位置中没有元素则直接添加
//3.否则调用元素的equals方法逐个比较位置中链表里的各个元素,如果相同则不添加,如果都不同将元素添加至链表末尾
//4.不同的hashCode值有可能计算得到同一个位置(极其少见)
set2.add(new Student("hzt", 20));
set2.add(new Student("hzt", 20));
System.out.println(set2);
}
}
|- SortedSet(I)
|- TreeSet: Comparable Comparator
特点: 不允许重复, 和equals方法无关
取决于 compare 或者 compareTo 方法
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetClass {
public static void main(String[] args) {
//二叉树的比较使用的是泛型的compare或compareTo方法,有Comparator优先用compare方法
Set<String> stringSet = new TreeSet<>();//使用空参构造方法时必须保证二叉树泛型为可比较的
stringSet.add("lucy");
stringSet.add("lucy");
stringSet.add("jack");
stringSet.add("ana");
stringSet.add("abby");
stringSet.add("mike");
//与HashSet一样都不允许重复,但是列表顺序是由二叉树的特性进行升序排列的
System.out.println(stringSet);
//如果使用的泛型不是Comparable的企且没有实现Comparator方法则需要在构造方法参数中实现Comparator的compare方法
// Set students = new TreeSet<>();
Set<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.name.length() - o2.name.length();//根据学生姓名长度升序排列
}
});
//否则在使用方法时会抛出类型转换异常,因为二叉树在比较时会将泛型强制转换为Comparable类型来调用compareTo方法
students.add(new Student("Emilia", 22));
students.add(new Student("hzt", 20));
students.add(new Student("hzt", 20));
students.add(new Student("lucy", 19));
System.out.println(students);
}
}
复习: Object中 hashCode() 方法默认得到的是 对象的物理地址
hashCode没有重写, 所有对象的哈希值都不一样
哈希值不一样, 定位到的set中的位置, 可能相同
Map(I): 映射表 key->value, 通过key 获得value
Map中的key, 单独的一部分, 就是Set
格式: xx = xxx, xx:xxx
|- HashMap
|- TreeMap
|- LinkedHashMap
常用API
V put(key, value)
V remove(key)
V get(key)
containsKey containsValue isEmpty size…
putAll
迭代Map对应的三个方法
entrySet()
keySet()
values()
代码实现:
import java.util.*;
import java.util.Map.Entry;//可以直接导入类到包中来使用Map的内部类Entry<>;
public class HashMapDemo {
public static void main(String[] args) {
//Map是用来存储key->value的映射列表,默认key已知而value是程序员主要想获得的值
Map<Integer, String> map = new HashMap<>();
//HashMap的排序根据key来进行,排序方式与HashSet相同
//HashMap的key值不能重复与Set列表相同,但value值可以重复
//map使用put方法添加和修改元素,返回值为key位置添加或修改前的value值
String s = map.put(2, "张三");
map.put(1, "张三");
map.put(4, "王五");
map.put(3, "hzt");
System.out.println("put加入新键值对的返回值:" + s);
System.out.println(map);
//key值相同则表示才此位置进行value值修改
s = map.put(1,"李四");
System.out.println("put修改键值对应value的返回值:" + s);
System.out.println(map);
//获取key位置value值
s = map.get(1);
System.out.println("get获取键值对应value值:" + s);
//删除key位置value值, 返回值为删除的value值
s = map.remove(1);
System.out.println("remove方法返回删除的value值:" + s);
System.out.println(map);
//map的迭代
//1.键值遍历(创建Set列表存储键值(因为键值不能重复),用keySet方法返回此地图中包含的键的Set视图)
System.out.println("键值遍历");
Set<Integer> set = map.keySet();
for (Integer key:set
) {
System.out.println(key + "=" + map.get(key));//输出键值对应value值
}
//2.键值对遍历(创建Set列表存储键值对(Entry), 用entrySet方法返回此地图中包含的映射的Set视图)
System.out.println("键值对遍历");
Set<Map.Entry<Integer,String>> set1 = map.entrySet();
for (Entry<Integer,String> couple: set1
) {
System.out.println(couple.getKey() + "=" + couple.getValue());
}
//3.value值遍历(创建List列表存储value值(可重复),用values方法返回此地图中包含的值的Collection视图。)
//但无法获取键值
System.out.println("value值遍历");
Collection<String> list = map.values();//因为返回的是Collection类型所以需要强制类型转换
for (String value: list
) {
System.out.println(value);
}
}
}
手动输入字符串 ewretyuyjytrerthy
统计这个字符串中每个字符出现的次数
e:3, w:1, r:3, t:3…
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
//输入字符串,获取每个不同字符出现次数
public class charCountDemo {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
String s = console.nextLine();
//定义地图列表存储每个字符以及对应的出现次数
Map<Character, Integer> map = new HashMap<>();
//将字符串转换为字符数组
char[] chars = s.toCharArray();
//遍历字符数组
for(char c: chars){
//如果此字符在map中对应的value值为空则表明是第一次出现
if(map.get(c) == null){
//将字符对应value值设置为1
map.put(c, 1);
}else {
//之后每次遇到此字符就将字符对应value值加1
map.put(c, map.get(c) + 1);
}
// map.merge(c, 1, Integer::sum);
}
System.out.println(map);
}
}
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
//TreeMap用来排序
Map<String, Integer> treeMap = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
treeMap.put("怪物猎人", 328);
treeMap.put("黎明杀机", 67);//由于自己设置的比较器方法是根据key的长度判断key是否相等,所以"怪物猎人"和"黎明杀机"其实代表的key值相同
treeMap.put("赛博朋克2077", 279);
treeMap.put("文明6", 116);
//结果:按照comparator排列,没有重复的key值
System.out.println(treeMap);
}
}
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo {
public static void main(String[] args) {
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("lucy", 0);
linkedHashMap.put("jack", 1);
linkedHashMap.put("hzt", 2);
linkedHashMap.put("aili", 3);
//结果:按照插入顺序排列,有序
System.out.println(linkedHashMap);
}
}