Java3堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections,异常,线程创建/同步

Posted 码农编程录

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java3堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections,异常,线程创建/同步相关的知识,希望对你有一定的参考价值。

文章目录


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,异常,线程创建/同步的主要内容,如果未能解决你的问题,请参考以下文章

数据结构 List Set Collections Map - 07

HashMap 的数据结构

面试题

JavaSE——数据结构二叉树红黑树

HashMap(数组+链表+红黑树)

jdk1.8 HashMap 实现 数组+链表/红黑树