Java学习 day32_Queue&Map

Queue

在这里插入图片描述

1.1 特点

1, Queue接口是Collection一个子接口, 描述的数据结构是队列
2, Queue存储元素是有序
3, Queue 允许存储重复元素
4, Queue 不允许存储null元素( LinkedList除外)

Queue的删除操作, 是以返回一个null值作为 标记, 标记没有元素存储了


1.2 API

在这里插入图片描述


1.2.1 注意

1, 如果Queue存储元素的时候, 没有位置/空间 可以存储了, 如果使用add方法抛出异常, 如果使用offer返回布尔值 假
2, 如果Queue存储元素,没有存储任何内容/里面是空的 如果使用remove方法抛出异常, 如果使用poll 会返回null
3, 如果Queue存储元素,没有存储任何内容/里面是空的 如果使用element方法抛出异常, 如果使用peek会返回null
在这里插入图片描述

2 Deque

2.1 特点

1, Deque接口是Queue接口的一个子接口
2, Deque描述的数据结构是: 双端队列, 普通队列, 栈
3, 有序
4, 允许重复元素
5, 不允许存储null ( LinkedList除外)


2.2 API

api很多,自己看文档


3 ArrayQueue

1, ArrayDeque 是Deque接口的一个具体子实现
2, ArrayDeque描述的数据结构是: 普通队列, 双端队列, 栈
3, 底层是个数组: 循环数组
4, 默认的初始容量16 , 扩容机制2倍 --> 底层数组长度是2的幂值
5, 有序
6, 允许存储重复元素
7, 不允许存储null
8, 线程不安全
9, 在给定长度的构造方法里, 如果我们给定一个长度是小于8的话底层数组创建一个长度为8的数组, 如果给定的值大于等于8 , 那么这个被创建的底层数组的长度是大于给定值的最小的2的幂值 --> 底层数组长度是2的幂值 (比较方便于用位运算取模, 计算速度更快)
注意:如果给了16,那创建的数组长度是32

循环数组的好处, 不需要在每次删除之后, 移动n-1个元素, 仅需要移动头标记


3.2 构造方法

在这里插入图片描述


4 BlockingQueue: 阻塞队列

什么是阻塞队列:
如果队列的大小是一定的, 当队列存满的时候, 添加线程阻塞,
当队列为空的时候, 删除线程阻塞


4.1 特点

阻塞队列
有一些阻塞方法
阻塞队列, 要求有大小

在这里插入图片描述

public static void main(String[] args) throws InterruptedException {

        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        queue.add("zs");
        queue.add("ls");
        queue.add("wu");

        // 容量只有3,这时使用put添加就会导致阻塞
        queue.put("zl");
    }
public class Demo01 {
    public static void main(String[] args) throws InterruptedException {

        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        queue.add("zs");
        queue.add("ls");
        queue.add("wu");

        Mythread mythread = new Mythread(queue);
        mythread.start();  // 这一步要放在前面,不然你阻塞了,后面的代码运行不到

        // 容量只有3,这时使用put添加就会导致阻塞
        queue.put("zl");



    }
}

class Mythread extends Thread{


    ArrayBlockingQueue<String> queue;

    public Mythread(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(5000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        queue.remove();

    }
}

阻塞队列:如果队列的大小是一定的,当队列存满的的时候,添加线程阻塞,当队列为空的时候,删除线程阻塞

阻塞队列一般用于生产者消费者模式,只能这么执行多任务,如果要进来就必须等我完成任务,比如分布式服务器之间通信,一个服务器可以一直发,但接收的服务器维护阻塞队列来接收,避免数据过大导致服务器瘫痪

阻塞队列构造方法要求有大小,不然自己扩容就失去阻塞的意义了


5 Map

Java集合类分为两套集合体系, 一个Collection 一个是Map
Collection存储的是单个元素
Map 存储的是Key-Value数据/ 键值对

在网页上下载东西的过程,其实就是通过key获得一个value


5.1 Map的特点

1, Map集合体系的顶级接口
2, 存储的是key-value数据 (我们在研究Map下的集合类的时候, 关注的核心点在key上)
3, 有的子实现key是有序的, 有的子实现key是无序的,
4, key是不允许重复的(重复的定义不一样, hashMap不允许存储哈希值相同的)
5, 有的子实现key允许存储null, 有的子实现key是不允许存储null


5.2 API

在这里插入图片描述
在这里插入图片描述

 public static void main(String[] args) {
        Map<String, String> map = new LinkedHashMap<>();

        map.put("zs", "13");
        map.put("ls", "14");
        map.put("wu", "15");

        //System.out.println(map);

        /*

        // 是视图方法,如果改动set会反映到原map中
        Collection set = map.keySet();
        System.out.println(set);


        set.remove("ls");

        */


        /*
        // value()也是视图方法,会对原来的map产生影响
        Collection values = map.values();

        values.remove("13");
        System.out.println(values);

        System.out.println(map);
        */

        // 视图  返回成对的key-value数据
        // 关于Map.Entry<K,V>,是Map接口的内部接口:
        Collection<Map.Entry<String, String>> collection = map.entrySet();
        for (Map.Entry<String, String> entry : collection) {
            //System.out.println(entry);

            System.out.println(entry.getKey() + "  " + entry.getValue());
        }

    }

6 HashMap: 重点, 必问

在这里插入图片描述


补充一下:
无符号右移:>>>
右移:>>



6.1 HashMap的结构

数组 + 链表 + 红黑树

在这里插入图片描述

常问点:HashMap和HashTable的区别,到时候我再补充


6.2 特点

HashMap的时间复杂度可以达到O(1)

  • 1, HashMap是Map接口一个具体实现
  • 2, HashMap的底层结构是 数组 + 链表 + 红黑树 (红黑树是jdk1.8版本新加的结构)
  • 3, 数组的默认初始长度 16(第一添加元素产生长度为16,如果只定义一个HashMap不添加元素,底层数组不初始化,开始添加元素才初始化),数组的扩容机制(扩为原来的2倍) --> 数组长度必定2的幂值 --> 位运算
  • 4, HashMap存储key是无序的,而且这个顺序会动态变化
  • 5, HashMap不允许存储重复的key值 , 对于HashMap 的key的重复定义是什么? :
  • 6, HashMap允许存户 null 键,对应的hash值为0
  • 7, HashMap线程不安全
  • 8, HashMap的默认加载因子是0.75(我们也可以在构造方法中传入加载因子, 建议在0.5-1之间)
    什么是加载因子? 饱和度
    数组存储元素(key-value数据)到一定容量的时候, 就会扩容数组, 以避免链表和红黑树过多/过长导致存储效率降低
    扩容的阈值 = 加载因子 * 数组长度, 不是说在数组中存了阈值个位置就扩容,而是包含所有链表红黑树元素超过阈值就扩容,用空间换时间


    假如:
    数组长度 16
    默认加载因子 0.75
    阈值: 12(第13个往里存的时候就会扩容)

    注意: HashMap是怎么存储key-value数据的
    1, HashMap在存储一份key-value数据的时候, 先把key取出来, 计算
    2, key 经过计算, 得到一个 int类型 hash值
    3, 把这个经过计算的hash值和HashMap的数组长度取模, 得到下标
    4, 如果这个下标位置, 没有元素, 把这份key-value数据, 存到这个下标位置(存储的是一个结点类型, 这个结点类型里面包含key, value …)
    5, 如果这个下标位置, 已经存储了结点, 判断key值和这个下标位置的这些已经存在结点中的key值是否重复, 如果重复不添加, 如果不重复, 添加到这个位置(链表的尾部)
    6, 如果链表过长, 把链表转化为红黑树

注意: 从单纯的数学领域(当下), hash
Hash算法
MD4
MD5: 王小云
SHA1:
SHA2
····

  • 9, HashMap中 hash值的计算:
    Hash = (h = key.hashCode()) ^ (h >>> 16);
    (key.hashCode()调用的是Object自带的hashCode方法)
    从主观上, 我们希望每一个key都可以散列到数组的不同位置, 实际上是没有办法保证的
    实际上在我们hash值取下标(和数组长度取模)的过程中, 只有hash值的低位才会有用, 也就意味着,我们最终在hashmap中数组上所散列的位置依赖于 hash的低位
    所以h >>> 16位的原因是因为, 希望高位数也能参加到取模运算中来, 已达到充分散列的效果(希望)
    1010101010110
    0000000101010
  • 10, 如果我们在构造方法里给定一个长度, 那么HashMap会创建一个底层数组长度大于等于给定值的最小的2的幂值的一个数组
    10 -> 16
    16 -> 16
    20 -> 32
    (ArrayDeque是产生大于, 而非大于等于,给16会创建32)
  • 11, 我们存储到数组位置的key-value数据, 实际上数组存储的是一个Node类型的结点, 这个Node类型包含四个参数: hash, key, value, next
  • 12, 在HashMap中判断key重复的判断条件:
    判断两个要比较的key-value数据的key的hash值是否一样, 如果hash值一样, 会进一步判断, key值是否直接相等或者相equals
// 即使很幸运通过两个对象的hash值算出下标是一样的,也必须先判断hash值,再看key值是否相同
// 所以没有重写hashCode,只要对象地址值不一样,就会被看成两个元素
p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))
  • 13, 也就意味着, 如果我们用一个普通的java对象来充当key的时候, 我们希望比较的是这个对象里面的存储的内容, 那么要根据这个对象里面的参数重写hashCode 和 equals方法(仅重写equals是不行的)
    如果两个key的hashCode一样 并且 两个对象相equals, 那么对于hashmap来说这两个key就是重复的
    Eg: 如下: 如果我们希望这两个User对象被hashmap看做是同一个key值的话, 需要重写User类型的hashCode 和 equals方法, (按照 name和age重写)
public class Demo {
    public static void main(String[] args) {
        HashMap<User, String> map = new HashMap<>();

        // 二者的地址不相等,但内容相等
        map.put(new User("zs", 18), "20");
        map.put(new User("zs", 18), "20");
        System.out.println(map); // 此时map中只有一对元素
    }
}

class User{
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 14, 如果要存储的key-value数据, 这个key值已经在HashMap上存在, 那么我们会用新的这份key-value数据的value来覆盖已经存储到HashMap上的(已经存储的重复的)key-value的value值 (key值不会覆盖)
        map.put(new User("zs", 18), "20");
        map.put(new User("zs", 18), "90");
        System.out.println(map); // {User{name='zs', age=18}=90}
  • 15, 在HashMap存储中, 如果多个key-value数据散列到同一个下标位置, key值也不重复, 那么这个位置就会构建出一个单链表, 如果单链表过长, 那么这个链表要转化为红黑树.
    过长意味着: 链表长度超过8达到9的时候
    (算上新添加的结点)
  • 16, 在HashMap中, 某一个数组位置链表长度超过8达到9的时候, 一定会转化为红黑树吗?
    不一定,
    // 有两个选择
    // 如果底层数组长度是小于64的, 那么我们会对底层数组扩容
    // 如果底层数组长度是大于等于64的, 会对链表进行转化转化成红黑树

扩容,
key-value的hash是不变的, hash来源于key的hashCode
数组长度变了
取模结果变化 --> 存储位置有可能变化

  • 17, 如果一个key-value数据, 原本在下标为x的位置, 当发生扩容的时候, 他只可能重新散列到两个位置: (原因是因为数组长度是2的幂值)
    原本的x位置
    X + 旧长度的位置

Eg: 数组长度16
原本 3
扩容 : 数组长度 32
这个元素: 3, 19

  • 18, 红黑树什么时候转化会链表?
    有两种情况, 会导致红黑树转化会链表
    1, 删除元素的时候(删除的是红黑树上的元素),
    2, 在扩容的时候, 有可能会导致红黑树转化回链表(扩容会把一个元素位置的数据拆成两个位置)

删除的时候判断逻辑(红黑树 --> 链表)
根节点, 根节点的左右子结点, 根节点的左节点的左节点, 这个四个结点只要有一个是null, 那么就会在删除操作的时候, 把红黑树转化为链表

if (root == null || root.right == null ||
    (rl = root.left) == null || rl.left == null) {
    tab[index] = first.untreeify(map);  // too small
    return;
}

扩容的时候判断逻辑(红黑树 --> 链表)
由于扩容的时候, 红黑树的结点会散列成两部分(旧位置, 旧位置+旧长度), 这两部分经过散列之后, 任一个位置的key-value数据是小于等于6的话, 就要由红黑树转化回链表

  • 19, 存储到红黑树(特殊的二叉搜索树) 是要能比较大小, 在hashmap的红黑树中,怎么比较key值的大小那, 就是通过key值的hash比较大小(int)

  • 20, 已经存储到HashMap中的key-value数据, 不要通过引用来修改key值
    (可以先删除, 再重新添加)

6.3 构造方法

在这里插入图片描述


6.4 API

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


一点补充

前后端不分离:服务器要把数据和前端合并,服务器压力大

前后端分离:浏览器先去从前端服务器获取前端框架,前端代码会告诉你数据的位置在哪里,然后再从别的服务器那数据将前端填充,用户来完成前端和后端数据的合并,但是这样浏览器的消耗会相对大一点

在这里插入图片描述

key-value数据有自我描述性,在网络传输中适用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值