Java3堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections,异常,线程创建/同步
Posted 码农编程录
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java3堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections,异常,线程创建/同步相关的知识,希望对你有一定的参考价值。
文章目录
- 1.堆栈/队列/数组/链表:数据结构即计算机组织管理数据的方式,堆栈指的是内存图中的栈,不是堆
- 2.红黑树:二查,二查平,二查平1倍
- 3.List子接口:集合,IndexOutOfBoundsException
- 4.ArrayList的扩容原理:Stringbuild默认长度=16,扩容2倍
- 5.LinkedList:push堆栈
- 6.set子接口:单例=keySet
- 7.Object类的hashcode方法:对象的真正的内存地址(无限种)->哈希码 (43亿不到的可能)。极端下多个明文 -> 同一密文 (哈希碰撞)。打印对象,.toString(),.hashCode()
- 8.String类的hashcode方法:a97,碰撞
- 9.哈希表(HashSet)原理:元素不重复
- 10.linkedHashset和Hashset区别:A a = new A(),coll.iterator().hasNext()
- 11.Map:Map和Collection是并列关系,Map.Entry < Integer,String > 是数据类型
- 12.内部接口:除了Inner访问受限于Outer,这两各自独立
- 13.统计字符出现个数:.containsKey(.charAt)
- 14.斗地主:list.addAll(set)
- 15.Collections类和TreeSet:return o1-o2是升序
- 16.错误和异常区别:Arrays.toString(array)
- 17.编译和运行异常:SimpleDateFormat
- 18.处理异常:方法声明抛出
- 19.finally关键字:catch相当于else if,finally相当于else,return
- 20.自定义异常:extends
- 21.线程两种创建方式:new Thread(new Runnable() ),extends Thread,implements Runable
- 22.卖票:原子性
- 23.线程同步:synchronized关键字/方法,Lock接口,ThreadLocal
- 24.卖包子:wait,notify
- 25.创建和启动2个子线程:一个打印1-10之间奇数,一个打印1-10之间偶数
- 26.使用三个线程循环打印出1~100
- 27.账户类:synchronized 方法
1.堆栈/队列/数组/链表:数据结构即计算机组织管理数据的方式,堆栈指的是内存图中的栈,不是堆
如下查询慢:知道张三在哪,不能马上知道王五在哪,挨个查。如下增删虽然不用整个动(如删除李四,只需要将箭头指向王五就行),但是还是要先查找到再删除
,效率还是慢。但是直接删除张三或马六头尾元素快。
2.红黑树:二查,二查平,二查平1倍
二叉树(二)
:每个节点最多两个子节点。查找树(查)
:左小右大(二分法)。平衡树(平)
:左右尽量相等(一边的节点层次不会超过另一边的两倍)。
二叉搜索树BST(二查)
:插入或查询一节点时,树的每一行只需要和这一行的一个节点比较(因为左小右大),复杂度完全依赖树深度。树的行数即高度=logn【n为节点总数,2的树行数次方为n】,BST读和写的复杂度都为logn。
有序数组
:查找时用二分查找法(和BST像),时间复杂度也为logn。有序数组查询最差情况logn,而当BST为单边时(最差情况),BST查询和插入都为o(n)。为什么很多情况下用BST,而不是有序数组二分查找?因为有序数组查找用二分查找logn,但是插入(不是查)要移动,插入时间复杂度为o(n)。
BST很少在语言内部数据结构存储里用(因为下面直线情况),自平衡二叉树AVL(二查平)
是BST(二查)
的继承优化:左子树和右子树都是平衡二叉树,而且左子树和右子树深度之差绝对值不会超过1(左旋和右旋),AVL读和写的复杂度最差情况都为O(logn)。
AVL平衡左右子树相差1,这个条件很苛刻,导致很多情况下都不满足这个平衡条件,需要旋转变换,变换的话需要浪费时间。红黑树(二平查1倍)
平衡条件更加宽松些左右深度差一倍
即> = 节点数相同,< = 节点数差一倍(因为红节点的子节点
必须为黑即黑红相间)。叶子节点(最后一个)和null节点都为黑节点
这样宽松条件导致我们在插入节点时候变化更少的,所以红黑树写的性能会高一些,所以treemap/hashmap底层采用红黑树(BST会变直线,AVL左右只能差1)。
如下是红黑树的插入变色
流程:最上面根节点必须为黑,插入节点(为叶子节点)必为红节点(看插入节点的父节点和父节点的兄弟节点即叔节点)。null节点算叶子节点即黑节点
,当前插入的003是爷爷节点001
的右右。
如下左旋+变色。
先序(先根)
:根左右
中序
:投影
后序
:左右
根(从下到上)
3.List子接口:集合,IndexOutOfBoundsException
package com.itheima01.list;
import java.util.ArrayList;
import java.util.List;
/*
Collection子接口:List
1. List的特点:重索序
1. 有先后顺序:元素存储的顺序和取出的顺序相同
2. 具有整数索引,就是下标
3. 允许重复元素
2. List的方法(带索引)(List特有的,共有的在Collection讲过)
1. add(int index, E element) :往索引位置添加一个元素
1. Java中的 三个越界异常
1. IndexOutOfBoundsException 集合
2. ArrayIndexOutOfBoundsException 数组
3. StringIndexOutOfBoundsException 字符串越界
2. get(int index):获取指定索引的元素
3. remove(int index):移除指定索引的元素
4. set(int index, E element) :修改指定索引的元素值
*/
public class ListDemo
public static void main(String[] args)
// add();
List<String> list = new ArrayList<>();
list.add("周楠");
list.add("王凤枝");
list.add("王凯");
String s = list.get(2);
System.out.println(s); //王凯
list.remove(2);
System.out.println(list); //[周楠, 王凤枝]
list.set(1,"昌老师");
System.out.println(list); //[周楠, 昌老师]
//11111111111111111111111111111111111111111111111111111111111111111111111111111
private static void add()
List<String> list = new ArrayList<>();
list.add("周楠");
list.add("王凤枝");
list.add("王凯");
/*
add(int index, element)
往指定索引位添加元素
index = list.size()
IndexOutOfBoundsException: : 索引越界异常
*/
list.add(3,"田锁"); //不越界,4越界
System.out.println(list);
String[] array = ;
//System.out.println(array[0]); //ArrayIndexOutOfBoundsException : 数组索引越界
String str = "abc"; // 字符串底层也是数组
// char c = str.charAt(3); //索引0,1,2
// System.out.println(c); //StringIndexOutOfBoundsException:字符串索引越界
4.ArrayList的扩容原理:Stringbuild默认长度=16,扩容2倍
ArrayList底层是存Object数组,扩容新建一个长度为原来1.5倍新数组(空)。
如下10进制的4就是2进制的0100(8421),3/2=1,ArrayList.java源码中出现左右移(二进制右移一位相当于十进制/2)
。
package com.itheima01.list;
import java.util.ArrayList;
/*
* ArrayList: 数组
* 1. 最常用: 适合 查询需求比较多的场景
* 2. 原理: ArrayList扩容原理
* ArrayList底层是数组,数组长度不可变,为什么ArrayList又可变呢? 因为数据迁移
*/
public class ArrayListDemo
public static void main(String[] args)
ArrayList<String> list = new ArrayList<>();
list.add("xx");
System.out.println(3 >> 1); // 1 //除2取整
System.out.println(4 >> 1); // 2
System.out.println(10 >> 1); // 5
System.out.println(3 << 2); //12 //3*2*2,左移2位
5.LinkedList:push堆栈
package com.itheima01.list;
import java.util.LinkedList;
/*
LinkedList特点
1. 底层数据结构: 双向链表
2. 查询速度慢,增删快(增删需求多而且增删首尾用LinkedList)
3. 特有方法(不能使用多态,父类不能调子类特有方法)
1. addFirst 元素添加在链表开头
2. addLast(add相同) 元素添加在链表结尾
3. getFirst 获取链表开头
4. getLast 获取链表结尾
5. removeFirst 移除并返回链表开头
6. removeLast 移除并返回链表结尾
//下面两个不需要掌握
7. pop 从此列表所表示的堆栈处弹出一个元素(最顶部元素弹出,removeFirst)
8. push 将元素推入此列表所表示的堆栈(元素存储到集合顶部,addFirst)
*/
public class LinkedListDemo
public static void main(String[] args)
// method01();
LinkedList<String> list = new LinkedList<>();
list.add("张三"); // 是链表,不是按堆栈结构添加元素
list.add("李四");
list.add("王五");
// 链表 -> 堆栈 ,张三在栈顶
// String pop = list.pop(); // 弹栈: 栈顶元素()
// String removeFirst = list.removeFirst();//效果同上
// System.out.println(pop);
list.push("王二"); //栈顶添加,效果等同于add
System.out.println(list);
private static void method01()
LinkedList<String> list = new LinkedList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.addFirst("王二");
list.addLast("马六");
System.out.println(list);
String first = list.getFirst();
String last = list.getLast();
System.out.println(first + "," + last);
System.out.println(list);
list.removeFirst();
list.removeLast();
System.out.println(list);
6.set子接口:单例=keySet
package com.itheima02.set;
import java.util.HashSet;
import java.util.Set;
public class SetDemo
public static void main(String[] args)
Set<String> set = new HashSet<>();
set.add("张三");
set.add("李四");
boolean result = set.add("王五");
System.out.println(result); //true
boolean result2 = set.add("王五");
System.out.println(result2); //false //元素不可重复
System.out.println(set);//[李四,张三,王五] //存取不保证顺序
7.Object类的hashcode方法:对象的真正的内存地址(无限种)->哈希码 (43亿不到的可能)。极端下多个明文 -> 同一密文 (哈希碰撞)。打印对象,.toString(),.hashCode()
package com.itheima03.hash;
/*
* Object类有一个方法: int hashCode() : 返回该对象的哈希码值。
* 1. 原理: 将对象的真正的内存地址(明文) 进行 哈希算法 加密之后产生的 哈希码值(密文)
* 2. 加密 :
* 明文 : 大家都看的懂东西 I love you
* 密文 : 明文经过加密算法变成密文 J mpwf zpv
* 加密算法: 数学 (凯撒加密: 字母按字母表右移动一位)
* 破解: 频率分析法 (e i -> d h),截获大量数据进行大数据分析e,i出现频率最高,密文中出现最多的是d,h
*
* 哈希算法: 公开
* 基本保证 一个明文 -> 一个密文 不同明文不同的密文
* 告诉你算法,告诉你密文, 算不出明文
*
* 3. 源码: public native int hashCode(); 本地方法
* native(本地关键字) 修饰的方法没有java方法体 (方法实现在JVM底层, 用C语言写的)
* 返回值 int (43亿不到的可能)
*/
public class HashcodeDemo
public static void main(String[] args)
Person p = new Person();
System.out.println(p); //com.itheima03.hash.Person@14ae5a5
// return getClass().getName() + "@" + Integer.toHexString(hashCode());
System.out.println(p.toString());//com.itheima03.hash.Person@14ae5a5
// 上面两个打印结果都一样
// 如下内存地址: 16进制哈希码值14ae5a5 和下面10进制相等
System.out.println(p.hashCode()); // 10进制: 21685669
class Person
8.String类的hashcode方法:a97,碰撞
String s1 = “abc”,如下h就是hash值
(只看最后一个h),b是98,c是99。
package com.itheima03.hash;
// String类重写了Object的hashcode方法 (31算法)
public class StringHashCodeDemo
public static void main(String[] args)
String s1 = "abc";
String s2 = "acD";
String s3 = "重地";
String s4 = "通话";
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354 //和上面哈希碰撞
System.out.println(s3.hashCode()); //1179395
System.out.println(s4.hashCode()); //1179395
9.哈希表(HashSet)原理:元素不重复
hashcode(密文)为HashSet(HashSet底层数据结构是hash表)做铺垫。如下三个abc字符串都为96354。问题:HashSet如何判定这个元素是否跟已存在的元素是重复即如下[重地,通话,abc,acD]?Set不存重复元素。S3因为是new出来的,和S1,S2明文即真正的内存地址不一样。
下面S1,S4,S5,S6都是不重复元素。竖:暗文或暗文%16同(因为数组长度为16
),equal不一样。横:明文和暗文和equal都不一样。HashSet是效率最高的set且元素不重复,同一链表(竖)hashcode一样,但是链表如果太长查询慢,所以假如同一hash值(hashcode)碰撞了8次,链表重构为红黑树
。
同一链表上都hash碰撞,数组的第一个位置余数=0,第二个位置余数=1。。。16的容量为什么到16*0.75=12
就扩容了?再哈希rehash(余数重新算)这段时间内,16没满,我还有的用,如果rehash非常快就不用提前。
package com.itheima02.set;
import java.util.HashSet;
import java.util.Objects;
/*
* HashSet: 判定重复元素:(明文地址【内】,hash值【外】,equals【名字】)。
* 内同-》重复不插, 内不同 外不同 默认e不同-》横插, 内不同 外同 e不同-》竖插
*
* person类父类的Object: 1. hashcode:明文内存地址加密得到密文hash值
*(不同明文产生不同密文,刘亦菲明文即两个内存地址不一样,密文hash值基本不会相同,万一碰撞了,还有下面2进 行保障)
* 2. equals == 比较真正内存地址
*
* 需求: 两个对象就算地址不同, 但是所有属性一一相同, 就认为是同一元素
* 解决: 重写hashcode和equals方法 -> 类中的所有属性,重写规范见文章:https://blog.csdn.net/weixin_43435675/article/details/112604089
*/
public class HashSetDemo02
public static void main(String[] args)
HashSet<Person> set = new HashSet<>();
set.add(new Person("高圆圆",18));
set.add(new Person("刘亦菲",19));
set.add(new Person("刘亦菲",19));//Person类继承Object类,new新地址
System.out.println(set);
class Person
String name;
int age;
public Person(String name, int age)
this.name = name;
this.age = age;
@Override
public boolean equals(Object o) //alt+insert选equals()and hashCode() //每个属性一一比对
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o; //o是外部传入,转成person
return age == person.age &&
Objects.equals(name, person.name);
@Override
public int hashCode()
return Objects.hash(name, age); //工具类Objects.java中hash方法中hashCode方法就是31算法,也是逐一遍历
@Override
public String toString()
return "Person" +
"name='" + name + '\\'' +
", age=" + age +
'';
没有重写Person类的hashcode和equals方法。
如下重写…如下就是hash表的应用:元素不重复,效率高。
10.linkedHashset和Hashset区别:A a = new A(),coll.iterator().hasNext()
package com.itheima00.question;
import java.util.HashSet;
import java.util.LinkedHashSet;
/*
* Set: 不保证 存入和取出顺序一致
* HashSet : 无序
* LinkedHashSet : 多个另一个链表, 来记录存入的顺序,有序即取出有序,所以效率变低(少用)
*/
public class Demo01
public static void main(String[] args)
HashSet<String> set = new LinkedHashSet<>(); //向上转型
set.add("张三");
set.add("李四");
set.add("王五");
set.add("马六");
System.out.println(set); //打印出有序的,LinkedHashSet不同于HashSet
package com.itheima00.question;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo02
public static void main(String[] args)
Collection<String> coll = new ArrayList<String>(以上是关于Java3堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections,异常,线程创建/同步的主要内容,如果未能解决你的问题,请参考以下文章