【Java 数据结构】Map和Set

news2024/12/26 12:04:36

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

1.概念

1.1概念及场景

1.2 模型

1.3 Map的说明

1.4 Map方法的介绍

1.5 Set的说明

1.6 Set方法的介绍

2、哈希表

2.1 什么是哈希表

3. 哈希冲突

3.1 概念

 3.2 降低哈希冲突的发生的概率

3.2.1 设计好的哈希函数

3.2.2 降低负载因子

3.3.当冲突发生时如何解决哈希冲突(简单介绍)

闭散列:有两种(线性探测法&&二次探测法)

线性探测

二次探测

开散列:它的叫法有很多,也叫做哈希桶/链地址法/拉链法

  ③若遇到负载因子过大,要扩容,那么存入的数据又该怎么进行处理???(链表中的每一个数要进行重新哈希),以下为二倍扩容后的图​编辑 实现一个哈希表

 重写hashCode()方法

性能分析

小结



1.概念

1.1概念及场景

①Map和Set的作用:

一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关 。

②Map和Set相比于其他类型的优点:

之前我们学过的常见搜索方式有:  直接遍历, 二分查找等

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

1. 根据姓名查询考试成绩

2. 通讯录,即根据姓名查询联系方式

3. 不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的 Map 和 Set 是 一种适合 动态查找的集合容器 

1.2 模型

1. 纯 key 模型:

eg.有一个英文词典,快速查找一个单词是否在词典中 ;快速查找某个名字在不在通讯录中

2.Key-Value 模型

eg.统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数 > ;梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

而 Map中存储的就是key-value的键值对,Set中只存储了Key

1.3 Map的说明

Map 中存储的是 key-value的键值对, Map 是一个接口类,该类没有继承自 Collection ,该类中存储的是 <K,V> 结构的键值对,并且 K 一定是唯一的,不 能重复 。

1.4 Map方法的介绍

方法解释

V get (Object key)

返回 key 对应的 value

V getOrDefault (Object key, V defaultValue)

返回 key 对应的 value , key 不存在,返回默认值

V put (K key, V value)

设置 key 对应的 value

V remove (Object key)

删除 key 对应的映射关系

Set<K> keySet ()

返回所有 key 的不重复集合

Collection<V> values ()

返回所有 value 的可重复集合

Set<Map.Entry<K, V>> entrySet ()

返回所有的 key-value 映射关系

boolean containsKey (Object key)

判断是否包含 key

boolean containsValue (Object value)

判断是否包含 value

Map的注意事项:

1. Map 是一个接口,不能直接实例化对象 ,如果 要实例化对象只能实例化其实现类 TreeMap 或者 HashMap。

2. Map 中存放键值对的 Key 是唯一的, value 是可以重复的(重复的情况,后面put的覆盖前面的)。

3. Map 中的 Key 可以全部分离出来,存储到 Set 中 来进行访问 ( 因为 Key 不能重复 ) 。

4. Map 中的 value 可以全部分离出来,存储在 Collection 的任何一个子集合中 (value 可能有重复 ) 。

5. Map 中键值对的 Key 不能直接修改, value 可以修改,如果要修改 key ,只能先将该 key 删除掉,然后再来进行重新插入。

TreeMap 和 HashMap 的区别:

Map 底层结构

TreeMap

HashMap

底层结构

红黑树

哈希桶

插入 / 删除 / 查找时间

复杂度

O(log2^N)

O(1)

是否有序

关于key有序

无序

线程安全

不安全不安全
插入/删除/查找区别需要进行元素比较通过哈希函数计算哈希地址

比较与覆写

key必须能够比较,否则会抛出

ClassCastException异常

自定义类型需要覆写equals和

hashCode方法

应用场景

需要 Key 有序场景下

Key 是否有序不关心,需要更高的

时间性能

其中 Set<Map.Entry<K, V>> entry Set() 这个方法非常复杂但也非常重要,所以要做一些具体的说明:

Map.Entry<K, V>  Map 内部实现的用来存放  <key, value>  键值对映射关系的内部类 ,该内部类中主要提供了  <key, value> 的获取, value 的设置以及 Key 的比较方式。

如何理解????通俗来说就是:

Entry是Map里面的一个内部类,而 Map.Entry<key,val> 的作用就是把一个个map元素(key,val) 打包成一个整体,而这个整体的类型就是 Map.Entry<K,V>, 然后我们有一个Set集合,它里面存放的每个元素的类型就是 Map.Entry<K,V>。这里可以联想到我们的单链表的内部类ListNode,将 val,next 打包成一个整体,那么它的类型就是ListNode。

 所以下面这段代码运行起来一定会把Set集合中存放的map中的每一个元素都输出出来:

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    map.put("hello",2);
    map.put("world",1);
    map.put("bit",3);
    Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
    for (Map.Entry<String,Integer> entry:entrySet) {
        System.out.println("key: "+entry.getKey()+" val: "+entry.getValue());
    }
}

该内部类Entry提供的一些方法也是比较重要的:

方法

解释

K getKey ()

返回 entry 中的 key

V getValue ()

返回 entry 中的 value

V setValue(V value)

将键值对中的 value 替换为指定 value

1.5 Set的说明

Set 与 Map 主要的不同有两点: Set 是继承自 Collection 的接口类, Set 中只存储了 Key 。

1.6 Set方法的介绍

方法

解释

boolean add (E e)

添加元素,但重复元素不会被添加成功

void clear ()

清空集合

boolean contains (Object o)

判断 o 是否在集合中

Iterator<E> iterator ()

返回迭代器

boolean remove (Object o)

删除集合中的 o

int size()

返回set 中元素的个数

boolean isEmpty()

检测 set 是否为空,空返回 true ,否则返回 false

Object[] toArray()

将 set 中的元素转换为数组返回

boolean containsAll(Collection<?> c)

集合 c 中的元素是否在 set 中全部存在,是返回 true ,否则返回false

boolean addAll(Collection<? extends

E> c)

将集合 c 中的元素添加到 set 中,可以达到去重的效果

Set的注意事项:

1. Set 是继承自 Collection 的一个接口类。

2. Set 中只存储了 key ,并且要求 key 一定要唯一。

3. Set 的底层是使用 Map 来实现的,其使用 key 与 Object 的一个默认对象作为键值对插入到 Map 中的。

4. Set 最大的功能就是对集合中的元素进行去重。

5. 实现 Set 接口的常用类有 TreeSet 和 HashSet ,还有一个 LinkedHashSet , LinkedHashSet 是在 HashSet 的基础上维护了一个双向链表来记录元素的插入次序。

6. Set 中的 Key 不能修改,如果要修改,先将原来的删除掉,然后再重新插入。

7. Set 中不能插入 null 的 key 。

TreeSet 和 HashSet 的区别 :

Set 底层结构

TreeSet

HashSet
底层结构红黑树哈希桶
插入/删除/查找时间复杂度O(log2^N)O(1)
是否有序

关于 Key 有序

不一定有序
线程安全

不安全

不安全
插入/删除/查找区别

按照红黑树的特性来进行插入和删除

1. 先计算key哈希地址 2. 然后进行

插入和删除

比较与覆写

key必须能够比较,否则会抛出

ClassCastException异常

自定义类型需要覆写equals和

hashCode方法

应用场景需要Key有序场景下

Key 是否有序不关心,需要更高的

时间性能

为什么HashMap和HashSet无序,而TreeMap和TreeSet有序??后面会解释到。

2、哈希表

2.1 什么是哈希表

最理想的搜索方法 , 即就是在查找某元素时 , 不进行任何比较的操作 , 一次直接查找到需要搜索的元素 , 可以达到这种要求的方法就是哈希表.

哈希表就是通过构造一种存储结构 , 通过某种函数使元素存储的位置与其关键码位形成一 一映射的关系 , 这样在查找元素的时候就可以很快找到目标元素.

哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)

例如:

存在一个数组集合 {1,7,6,4,5,9}.
哈希函数设置为:hash(key) = key % capacity;
capacity 为存储元素底层空间总的大小。

如图所示: 这样存储数据更加便于查找

 采取上面的方法,确实能避免多次关键码的比较,搜索的效率也提高的,但是问题来了,拿上述图的情况来举例子的话,我接着还要插入一个元素 14,该怎么办呢?

这个就是我们本章的重点,哈希冲突,4%10 = 4;14%10 = 4,此时发生了哈希冲突。

3. 哈希冲突

3.1 概念

首先我们得知道,哈希冲突是必然的,无论怎么插入,插入多少都无法杜绝,哪怕就插入两个元素4,14都发生了哈希冲突,我们能做的就是尽量避免哈希冲突的发生。

这也就是我们哈希表这种结构存在的问题。

哈希冲突的概念:两个不同关键字key通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。                

 把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

 3.2 降低哈希冲突的发生的概率

两种解决方法

1.设计好的哈希函数;2.降低负载因子

3.2.1 设计好的哈希函数

哈希函数设计原则:

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间。

  • 哈希函数计算出来的地址能均匀分布在整个空间中。

  • 哈希函数应该比较简单。

常用的两种哈希函数

1. 直接定制法

取关键字的某个线性函数为散列地址: Hash  Key  = A*Key + B

优点:简单、均匀。

缺点:需要事先知道关 键字的分布情况 使用场景:适合查找比较小且连续的情况。

力扣上这道题可以帮助我们理解: 字符串中第一个只出现一次字符

2. 除留余数法

设散列表中允许的 地址数为 m ,取一个不大于 m ,但最接近或者等于 m 的质数 p 作为除数,按照哈希函数: Hash(key) = key% p(p<=m), 将关键码转换成哈希地址

3.2.2 降低负载因子

下图是冲突率和负载因子的关系图:

 从图中我们可以直到要想降低冲突的概率,只能减小负载因子,而负载因子又取决于数组的长度。

公式:   负载因子 = 哈希表中元素的个数 / 数组的长度

因为哈希表中的已有的元素个数是不可变的,所以我们只能通过增大数组长度来降低负载因子。

3.3.当冲突发生时如何解决哈希冲突(简单介绍)

解决哈希冲突 两种常见的方法是: 闭散列 和 开散列

闭散列:有两种(线性探测法&&二次探测法)

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以  key 存放到冲突位置中的  下一个 ” 空位置中去。

线性探测

①什么是线性探测:

从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。

②线性探测的相关操作:

当插入操作时,通过哈希函数获取待插入元素在哈希表中的位置 ;如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到 ;下一个空位置,插入新元素

简而言之就是寻找下一个空的地方

③弊端:(可能会导致冲突元素均被放在一起) 

二次探测

①如何进行二次探测:

利用这个公式进入插入。其中:i = 1,2,3…,Hi是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小。

对于上述线性探测中的问题如果要插入44,产生冲突,使用解决后的情况为:

②重要结论:

当表的长度为质数且表装载因子 a 不超过 0.5 时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情 况,但在插入时必须确保表的装载因子a 不超过 0.5 ,如果超出必须考虑增容。

因此:闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。

开散列:它的叫法有很多,也叫做哈希桶/链地址法/拉链法

①什么是哈希桶???

开散列法又叫链地址法 ( 开链法 ) , 首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。 开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了。 参照下图:

 ②哈希桶如何进行存储???(链式存储法)

 ③若遇到负载因子过大,要扩容,那么存入的数据又该怎么进行处理???(链表中的每一个数要进行重新哈希),以下为二倍扩容后的图实现一个哈希表

代码如下:

public class HashBuck {
 
    static class Node {
        public int key;
        public int val;
        public Node next;
 
        public Node(int key,int val) {
            this.key = key;
            this.val = val;
        }
    }
 
    public Node[] array;
    public int usedSize;
 
    public static final double DEFAULT_LOAD_FACTOR = 0.75;
 
    public HashBuck() {
        this.array = new Node[10];
    }
 
    /**
     * put函数
     * @param key
     * @param val
     */
    public void put(int key,int val) {
        //1、找到Key所在的位置
        int index = key % this.array.length;
        //2、遍历这个下标的链表,看是不是有相同的key。有 要更新val值的
        Node cur = array[index];
        while (cur != null) {
            if(cur.key == key) {
                cur.val = val;//更新val值
                return;
            }
            cur = cur.next;
        }
        //3、没有这个key这个元素,头插法
        Node node = new Node(key, val);
        node.next = array[index];
        array[index] = node;
        this.usedSize++;
        //4、插入元素成功之后,检查当前散列表的负载因子
        if(loadFactor() >= DEFAULT_LOAD_FACTOR) {
            resize();//
        }
    }
     
    //扩容
    private void resize() {
        Node[] newArray = new Node[array.length*2];
        for (int i = 0; i < array.length; i++) {
            Node cur = array[i];
            while (cur != null) {
                int index = cur.key % newArray.length;//获取新的下标 11
                //就是把cur这个节点,以头插/尾插的形式 插入到新的数组对应下标的链表当中
                Node curNext = cur.next;
                cur.next = newArray[index];//先绑定后面
                newArray[index] = cur;//绑定前面
                cur = curNext;
            }
        }
        array = newArray;
    }
 
    private double loadFactor() {
        return 1.0*usedSize/array.length;
    }
 
    /**
     * 根据key获取val值
     * @param key
     * @return
     */
    public int get(int key) {
        //1、找到Key所在的位置
        int index = key % this.array.length;
        //2、遍历这个下标的链表,看是不是有相同的key。有 要更新val值的
        Node cur = array[index];
        while (cur != null) {
            if(cur.key == key) {
                return cur.val;
            }
            cur = cur.next;
        }
        return -1;
    }

 说明:以上的代码只是简单的实现了两个重要的函数:插数据和取数据

并且只是简单的实现,底层的树化并没有实现。

问题--》

问题一:以上代码的key是整形,所以找地址的时候,可以直接用 key % array.length,如果我的key是一个引用类型呢???,我怎么找地址???

下面这段代码,两者的 id 都一样,运行结果却不一样,这就和我们刚刚的相同的key发生冲突就不一致了。

class Person {
    public String id;
    public Person(String id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("134");
        Person person2 = new Person("134");
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
    }
}

 ④hashcode(解决引用类型情况下,把它变成是一个合法的整数)

但是这个时候直接输出他们的hashcode却是不相同的

 重写hashCode()方法

class Person {
    public String id;
    public Person(String id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id == person.id;
    }
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}
public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("134");
        Person person2 = new Person("134");
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
    }
}

1.为什么引用类型就要谈到 hashCode() ??

因为如果key是引用类型,就不能通过模上数组的长度来寻址了。而 hashCode() 作用就是返回对象的哈希代码值,简单来说,他就是一个整数

2.按道理来说,学号相同的两个对象应该是同一个人,为什么重写 hashCode(),返回对象的哈希代码值才会一样,不重写为什么会导致最终在数组中寻找的地址不相同??

因为底层的hashCode()是Object类的方法,底层是由C/C++代码写的,我们是看不到,但是因为它是根据对象的存储位置来返回的哈希代码值,这里就可以解释了,person1和person2本质上就是两个不同的对象,在内存中存储的地址也不同,所以最终返回的哈希代码值必然是不相同的,哈希代码值不同,那么在数组中根据 hash % array.length 寻找的地址也就不相同。而重写 hashCode() 方法之后,咱们根据 Person 中的成员变量 id 来返回对应的哈希代码值,这就相当于当一个对象,多次调用,那么返回的哈希代码值就必然相同。

所以我们的哈希表的实现就可以相应的改写成这样:

public class HashBuck<K,V> {
    static class Node<K,V> {
        public K key;
        public V val;
        public Node<K,V> next;
        public Node(K key,V val) {
            this.key = key;
            this.val = val;
        }
    }
    //往期泛型博客有具体讲到数组为什么这样写
    public Node<K,V>[] array = (Node<K,V>[]) new Node[10];
    public int usedSize;
    public static final double DEFAULT_LOAD_FACTOR = 0.75;
 
    public void put(K key, V val) {
        Node<K,V> node = new Node<>(key,val);
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while(cur != null) {
            if(cur.key.equals(key)) {
                cur.val = val;
                return;
            }
            cur = cur.next;
        }
        //头插
        node.next = array[index];
        array[index] = node;
        this.usedSize++;
        if(loadFactor() >= DEFAULT_LOAD_FACTOR) {
            reSize();
        }
    }
    private double loadFactor() {
        return this.usedSize * 1.0 / array.length;
    }
    private void reSize() {
        Node<K,V>[] newArray = (Node<K, V>[]) new Node[2 * array.length];
        for (int i = 0; i < array.length; i++) {
            Node<K,V> cur = array[i];
            while (cur != null) {
                Node<K,V> curNext = cur.next;
                int hash = cur.key.hashCode();
                int index = hash % newArray.length;
                cur.next = newArray[index];
                newArray[index] = cur;
                cur = cur.next;
            }
        }
        array = newArray;
    }
 
    public V get(K key) {
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while(cur != null) {
            if(cur.key == key) {
                return cur.val;
            }
            cur = cur.next;
        }
        return null;
    }
}

性能分析

虽然哈希表一直在和冲突做斗争,但在实际使用过程中,我们认为哈希表的冲突率是不高的,冲突个数是可控的,也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入 / 删除 / 查找时间复杂度是 O(1)

面试问题一:hashCode()和equals() 在HashMap中的作用分别是什么???

hashCode():用来找元素在数组中的位置;

equals():用来比较数组下链表中的每个元素的 key 与我的 key 是否相同。

equals也一样,如果不重写,上面的person1和person2的比较结果必然是不相同。

hashCode()和equals()就好比查字典,比如要查美丽,肯定要先查美字在多少页--hashCode(),然后它的组词有美景,美女,美丽,equals()就能找到美丽。

面试问题二:如果hashCode一样,那么equals一定一样吗? 如果equals一样,hashCode一定一样吗??

答案肯定是不一定,一定。

同一个地址下链表中的key不一定一样,就好比数组长度为10,4和14找到的都是4下标。

而equals一样,hashCode就一定一样,4和4肯定都在4下标。

所以这时候再回过头来看HashMap数据的打印时,就能明白HashMap和HashSet为什么无序了,它本身就不是一个顺序结构,至于TreeMap和TreeSet为啥有序,这就和我们之前学过的优先级队列是一个道理了。(整形的key,输出时,自然而然就排好序了,如果key是引用类型,则需要实现Comparable接口,或者传比较器)

小结

1. HashMap 和 HashSet 即 java 中利用哈希表实现的 Map 和 Set
2. java 中使用的是哈希桶方式解决冲突的
3. java 会在冲突链表长度大于一定阈值后,将链表转变为搜索树(红黑树)
4. java 中计算哈希值实际上是调用的类的 hashCode 方法,进行 key 的相等性比较是调用 key 的 equals 方法。所以如果要用自定义类作为 HashMap 的 key 或者 HashSet 的值,必须覆写 hashCode 和 equals 方法,而且要做到 equals 相等的对象,hashCode 一定是一致的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/465135.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

35岁程序员被裁赔偿27万,公司又涨薪让我回去,前提是退还补偿金,能回吗?

在大多数人眼里&#xff0c;35岁似乎都是一道槛&#xff0c;互联网界一直都有着“程序员是吃青春饭”的说法&#xff0c;。 如果在35岁的时候被裁能获得27万的赔偿&#xff0c;公司又涨薪请你回去上班&#xff0c;你会怎么选&#xff1f; 最近&#xff0c;就有一位朋友在网上…

Linux安装miniconda3

下载Miniconda&#xff08;Python3版本&#xff09; 下载地址&#xff1a;https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh 安装Miniconda&#xff08;需要连网&#xff09; &#xff08;1&#xff09;将Miniconda3-latest-Linux-x86_64.sh上传到/o…

研读Rust圣经解析——Rust learn-14(面向对象)

研读Rust圣经解析——Rust learn-14&#xff08;面向对象&#xff09; Rust面向对象对象包含数据和行为封装继承多态 实现面向对象书写最外层逻辑userServiceUser Rust面向对象 在一些定义下&#xff0c;Rust 是面向对象的&#xff1b;在其他定义下&#xff0c;Rust 不是 对象…

算法刷题|300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

最大递增子序列 题目&#xff1a;给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6…

c++文件操作Ofstream、Ifstream,如何获取文件长度

一、文件光标定位streampos 在读写文件时&#xff0c;有时希望直接跳到文件中的某处开始读写&#xff0c;这就需要先将文件的读写指针指向该处&#xff0c;然后再进行读写。 ifstream 类和 fstream 类有 seekg 成员函数&#xff0c;可以设置文件读指针的位置&#xff1b;ofstr…

OpenGL光照:颜色

知识点归纳 现实世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我们要做的工作是使用(有限的)数字来模拟真实世界中(无限)的颜色&#xff0c;因此并不是所有的现实世界中的颜色都可以用数字来表示。然而我们依然可以用数字来代表许多种颜色&#xff0c;并且你甚…

autosar

一 autosar简介 AUTOSAR&#xff0c;汽车开放系统架构&#xff08;AUTomotive Open System Architecture&#xff09;是一家致力于制定汽车电子软件标准的联盟。AUTOSAR是由全球汽车制造商、部件供应商及其他电子、半导体和软件系统公司联合建立&#xff0c;各成员保持开发合作…

QT DAY2

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->setFixedSize(600,600); //设置固定尺寸this->setWindowTitle("汪玉洁大聪明")…

Hadoop学习笔记(一)Hadoop的组成

1. HDFS NameNode用于记录整个数据的存储情况&#xff0c;具体的数据存储在各个Hadoop节点中&#xff0c;每个Hadoop的节点可以称为DataNode。假设Hadoop1到Hadoop100的机器每个都有1T的容量。那么一共就可以存储100T的数据。 NameNode(nn)&#xff1a;存储文件的元数据&…

位运算【巧妙思路、两种常见题型】

这里介绍两种代码中位运算非常常用的操作 n的二进制表示中第k位数——右移操作 &1 例如说&#xff0c;我们需要计算11的第2位数。 11 (1011)2 我们常规思路就是将其转化为二进制数后&#xff0c;直接观察对应位置的值 这里需要注意的是第k位数指的是从右开始的第k位&a…

Linux shell编程 条件语句

条件测试 test命令 测试表达式是否成立&#xff0c;若成立返回0&#xff0c;否则返回其他数值 格式1: test 条件表达式 格式2: [ 条件表达式 ]文件测试 [ 操作符 文件或者目录 ][ -e 1.txt ]#查看1.txt是否存在&#xff0c;存在返回0 echo $? #查看是上一步命令执行结果 0成…

DJ4-3 连续分配存储管理方式

目录 4.3.1 单一连续分配 4.3.2 固定分区分配 1. 分区说明表 2. 内存分配过程 4.3.3 动态分区分配 一、分区分配中数据结构 二、分区分配算法 三、分区分配操作 4.3.4 可重定位分区分配 1. 紧凑 2. 动态重定位 3. 动态重定位分区分配算法 连续分配是指为用户程…

【数据结构】堆(一)

&#x1f61b;作者&#xff1a;日出等日落 &#x1f4d8; 专栏&#xff1a;数据结构 如果我每天都找出所犯错误和坏习惯&#xff0c;那么我身上最糟糕的缺点就会慢慢减少。这种自省后的睡眠将是多么惬意啊。 目录 &#x1f384;堆的概念及结构&#xff1a; &#x1f384;堆的实…

万丈高楼平地起 AI帮你做自己

AI的自我介绍 AI是人工智能&#xff08;Artificial Intelligence&#xff09;的英文缩写&#xff0c;是一种通过计算机技术模拟和延伸人类智能的技术和应用。AI可以被看作是一种智能化的计算机程序或系统&#xff0c;它能够自动地执行一些需要人类智能才能完成的任务&#xf…

JavaEE初阶学习:初识网络

1.网络发展史 1.独立模式 独立模式:计算机之间相互独立&#xff1b; 2.网络互连 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&#xff0c;共享软件和数据&#xff0c;即以多个计算机协同工作来完成业务&#xff0c;就有了网络互连。 网络互连&#xff1a;将多…

除了Figma,再给你介绍10款好用的协同设计软件

组织结构越来越复杂&#xff0c;团队中的每个人都有独特的技能、经验和专业知识。我们怎样才能让团队更好地合作&#xff1f;在这种情况下&#xff0c;协同设计应运而生。 UI的未来是协同设计&#xff01;如果你想把握未来的设计趋势&#xff0c;不妨从使用高效的协同设计软件…

Docker的安装以及本地部署ILLA Builder

1.安装Docker&#xff0c;当前版本V4.18.0 。Docker引擎启动运行之后&#xff0c;效果如下图&#xff08;喜欢暗黑主题&#xff09; Docker启动可能出错&#xff0c;“Docker Desktop requires a newer WSL kernel version.” 如下图所示 解决方法&#xff0c;比较简单&#xf…

测试用例的基本要素和设计方法

作者&#xff1a;爱塔居 专栏&#xff1a;软件测试 作者简介&#xff1a;大三学生&#xff0c;希望同大家一起进步&#xff01; 文章简介&#xff1a;介绍写测试案例的功能需求测试和非功能需求测试和具体方法&#xff1a;判定表、正交表、等价类、边界值等 文章目录 目录 文章…

if条件语句

if条件语句 条件测试 test 测试表达式是否成立&#xff0c;若成立返回0&#xff0c;否则返回其他数值 格式1 &#xff1a;test 条件表达式&#xff1b;格式2 &#xff1a;[ 条件表达式 ] echo $?参数作用-d测试是否为目录 (Directory)-e测试目录或文件是否存在(Exist)-f测…

好物周刊#1:提示工程师养成指南

文章目录 &#x1f388; 项目ddruntiny-vuenetease-recent-profile &#x1f4bb; 软件BobScreenToGifSnipaste &#x1f578;️ 网站BrowserFramedocsmallDimmy.club &#x1f50c; 插件AdGuard[Global Speed: 视频速度控制](https://microsoftedge.microsoft.com/addons/deta…