Java 2.2 - Java 集合

news2024/11/15 17:52:37

Java 集合,也叫做容器,主要是由两大接口派生而来:一个是 Collection 接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于 Collection 接口,其下又有三个主要的子接口:List、Set、Queue

目录

Java 集合框架

(1)List、Set、Queue、Map的区别是什么?

List

Set

Queue

Map

(2)集合的底层数据结构都是什么?

List

Set

Queue

Map

* 拓展:为什么将插入链表的方式进行更改?

(3)如何选用集合?

(4)为什么要使用集合?

(5)ArrayList 和 Vector 的区别?

(6)ArrayList 和 LinkedList 

(7)双向链表和双向循环链表(1.6 1.7 LinkedList)

(8)RandomAccess 接口

(9)ArrayList 的扩容机制是怎么样的?

(10)comparable 和 Comparator 的区别

(11)无序性和不可重复性的含义是什么?

无序性

不可重复性

(12)HashSet、LinkedHashSet、TreeSet三者有何异同?

(13)Queue 和 Deque 有什么区别?

(14)ArrayDeque 和 LinkedList 的区别是什么?

(15)你知道 PriorityQueue 吗?

(16)HashMap 和 Hashtable 的区别是什么?

为什么 HashMap 的长度为什么需要是2的幂次方?

(17)HashMap 和 HashSet 有什么区别?

(18)HashMap 和 TreeMap 有什么区别?

(19)HashSet 如何检查重复?

(20)HashMap 的底层实现

1.8之前

1.8之后

(21)HashMap 多线程操作导致死循环问题

(22)HashMap 有哪几种常见的遍历方式?

(23)ConcurrentHashMap 和 Hashtable 的区别是什么?

ConcurrentHashMap

Hashtable

(24)ConcurrentHashMap 线程安全的具体实现方法 / 底层具体实现?

1.8 之前

1.8 之后


Java 集合框架

(列举主要继承和派生关系,并未全部列出)

(1)List、Set、Queue、Map的区别是什么?

List

有序的,可重复的。类似于数组

Set

无序的、不可重复的。类似于集合(数学上)

Queue

队列,一般是FIFO。

Map

key-value 键值对存储。key 无序的,不可重复的;value 无序的,可重复的。每个 key 最多映射到一个 value 上。

(2)集合的底层数据结构都是什么?

List

1、ArrayList:底层是数组 Object[]

2、LinkedList:底层是双向链表(1.6之前是循环链表,1.7之后取消循环)

3、Vector:底层是数组 Object[]

Set

1、HashSet(无序、唯一):基于 HashMap 实现,底层通过 HashMap 保存数据

2、LinkedHashSet:HashSet 的子类,内部通过 LinkedHashMap 实现。

3、TreeSet(有序、唯一):红黑树(自平衡的二叉排序树)

Queue

1、ArrayQueue:底层是数组 Object[] + 双指针

2、PriorityQueue(优先队列):底层是数组 Object[] 来实现二叉堆

Map

1、HashMap:

1.8 之前是数组 + 链表,通过“拉链法”解决哈希冲突(拉链法即创建一个链表数组,当发生哈希冲突的时候直接将其加入链表即可)。在1.8之前,将元素插入链表使用的是头插法。

1.8之后使用数组 + 链表 + 红黑树,当链表长度大于一个阈值(默认为8)(将链表转换为红黑树之前会进行判断,如果数组长度小于64,会优先对数组进行扩容,如果长度大于64则进行转换)时,将链表转换为红黑树,以减小搜索时间。在1.8之后,将元素插入链表使用的是尾插法。

2、Hashtable:数组 + 链表

3、LinkedHashMap:

LinkedHashMap 继承 HashMap,它的底层仍然是基于拉链式散列结构即由数组和链表或红⿊树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对插入的顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。

每当有新键值对节点插入,新节点最终会接在 tail 引用指向的节点后面。而 tail 引用则会移动到新的节点上,这样一个双向链表就建立起来了。

* 拓展:为什么将插入链表的方式进行更改?

答:1、扩容会导致链表的顺序被反转

2、头插法可能导致链表环形问题,这个问题会导致 CPU 使用率 100% 或者死循环问题。(多线程情况下)

(3)如何选用集合?

我们主要根据集合的特点进行选择,

比如我们需要键值对进行存储,选用的肯定是 Map 接口下的集合。需要排序的时候选用 TreeMap,不需要排序的时候选 HashMap,保证线程安全使用 ConcurrentHashMap。

比如我们只需要存放值的之后,选择 Collection 接口下的集合,需要保证元素唯一的时候选择 Set下的 TreeSet 或 HashSet,不要则选择 List 下的 ArrayList 或者 LinkedList。

(4)为什么要使用集合?

当我们需要保存一组类型相同的数据的时候,我们选用数组进行存储;

而在实际开发过程中,我们需要存储的数据类型往往是多样的,所以就出现了“集合”,它可以存储不同类型的数据。

而且数组在声明之后,长度不可变,这在开发中非常不方便,因为有些时候你不知道你需要存储多少数据;而使用集合基本不存在这样的问题,它的存储非常灵活,它可以存储不同类型不同数量的对象,并且可以保存 key - value 关系的数据。

(5)ArrayList 和 Vector 的区别?

ArrayList 是 List 的主要实现类,它的底层是数组 Object[],适合于频繁的查找工作,但是线程不安全,性能更好。在扩容的时候容量变为原来的1.5倍。

Vector 是 List 的古老实现类,它的底层是数组 Object[],线程安全,性能较低。在扩容时容量变为原来的2倍。

更现代的线程安全 List 实现类:CopyOnWriteArrayList(需要线程安全的时候用这个)

(6)ArrayList 和 LinkedList 

1、线程安全:它们都是不同步的,也就是都不是线程安全的。

2、底层数据结构:ArrayList 底层是数组,LinkedList 底层是双向链表。(1.6之前是循环链表)

3、插入和删除:

ArrayList 默认插入到尾部,时间复杂度是O(1),如果插入到位置 i ,时间复杂度是O(n-i),因为需要移动位置;

LinkedList 可以在头尾进行插入和删除,此时时间复杂度是O(1),如果需要在指定位置 i 进行插入和删除,时间复杂度为O(n),因为要先遍历寻找到位置 i 再进行操作。

4、快速随机访问:ArrayList 支持,LinkedList 不支持。

5、内存空间占用:ArrayList 空间浪费在尾部预留的空间;LinkedList 空间浪费在每个元素耗费更多空间。

项目中一般不会用到 LinkedList,它的作者 Joshua Bloch 自己说从来不会使用 LinkedList。 - -

(7)双向链表和双向循环链表(1.6 1.7 LinkedList)

它们的区别就在于:双向循环链表的头节点的 prev 指向尾节点;而其尾节点的 next 指向头节点。双向链表头节点的 prev 指向 null;尾结点的 next 指向 null。

(8)RandomAccess 接口

该接口为空,它存在的意义就是标识实现这个接口的类有快速随机访问的能力。

例如:ArrayList 底层实现了 RandomAccess 接口,而 LinkedList 没有实现,关键在于它们的底层数据结构不同。ArrayList 本身就可以实现快速随机访问,因为其底层是一个 Object 数组。而不是因为其实现了 RandomAccess 。

(9)ArrayList 的扩容机制是怎么样的?

ArrayList 的无参构造方法创建 ArrayList的时候,实际上初始化赋值了一个空数组,只有当向其中添加元素的时候才会分配空间。

ArrayList 每次扩容后容量会变为原来的1.5倍左右 int new = old + (old >> 1);

详见 黑马程序员

(10)comparable 和 Comparator 的区别

comparable 接口出自 java.lang 包,它有一个 compareTo(Object obj) 方法用来排序。

Comparator 接口出自 java.util 包,它有一个 compare(Object obj1, Object obj2) 方法用来排序。

 当需要对集合进行自定义排序的时候,例如对 song 对象 歌名 / 歌手 顺序进行排序的时候,我们可以通过重写 comparable 的 compareTo 方法和 使用自制的 Comparator 方法;或者以两个 Comparator 来实现排序,这种方法代表我们只能使用两个参数版的 Collection.sort()。

定制排序的编写方法:

1、comparable(对于没有实现 Comparable 接口的类,必须实现其才能通过重写 compareTo 方法进行排序)

重写方法

2、 Comparator 定制排序

(11)无序性和不可重复性的含义是什么?

无序性

无序性不等于随机性,无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值进行添加的。

不可重复性

是指添加的元素按照 equals() 进行判断的时候,返回值为false。

(12)HashSet、LinkedHashSet、TreeSet三者有何异同?

它们是 Set 接口的三个实现类,它们都能保证元素唯一且都不是线程安全的。

它们的区别在于底层的数据结构不同。HashSet 的底层数据结构为 HashMap;LinkedHashSet 的底层是 HashMap 和 链表,取出元素的顺序满足 FIFO;TreeSet 的底层数据结构为红黑树,排序的方法有自然排序和定制排序。

(13)Queue 和 Deque 有什么区别?

Queue 是单端队列,只能从队尾进行数据的添加,队首进行数据的删除。一般满足 FIFO。

Queue 拓展了 Collection 接口,根据 因为容量不足而导致的数据操作失败,一类方法在失败后会抛出异常,一类方法在失败后会返回特殊值。

Deque 拓展了 Queue,它是一个双端队列,可以从队尾队首任意一端进行数据的添加和删除,同样根据数据操作失败后的处理分为两类方法。

Deque 还有 push() 和 pop() 方法可以用来模拟栈。

(14)ArrayDeque 和 LinkedList 的区别是什么?

ArrayDeque 和 LinkedList 都实现了 Deque 接口,它们都具有队列的功能。

(15)你知道 PriorityQueue 吗?

PriorityQueue 在 JDK1.5 中被引入,它与 Queue 的区别在于:队列是 FIFO 的;而优先队列先出队的元素是优先级最高的元素。

要点:

1、PriorityQueue 利用二叉堆的数据结构进行实现,底层使用可变长的数据来存储数据。

2、PriorityQueue 的时间复杂度为 O(logn),即堆排序的时间复杂度。

3、PriorityQueue 不是线程安全的,且不能存储 null 和 non-comparable 对象。

4、PriorityQueue 默认是最小堆,但可以接收一个 Comparator 参数来自定义排序方法(优先级大小)。

PriorityQueue 在面试中经常出现于手撕算法中,尤其是堆排序、求第K大数等等,需要熟练掌握

(16)HashMap 和 Hashtable 的区别是什么?

1、HashMap 不是线程安全的,Hashtable 是线程安全的。

2、HashMap 效率高于 Hashtable(因为线程安全问题)。

3、HashMap 可以存储 null key 和 null value;Hashtable 不支持存储 null key 和 null value。

4、HashMap 默认容量为11,每次扩充变成 2n+1; Hashtable 默认大小为 16,每次扩充变成 2n。

HashMap 如果给定容量,它会初始化为 2 的幂次大小;Hashtable 会直接使用你给定的大小。

5、HashMap 底层数据结构使用的是数组、链表和红黑树;Hashtable 未使用红黑树。

* HashMap 中使用 2 的幂作为哈希表的大小代码。

这里做一下讲解:

我们希望使用 2 的幂大小,在二进制看来,实际上就是使得其最高位的下一位变为1,其余都为0(除了本身就是 2 的幂大小的数,这就是为什么 n 需要先减1)

那么我们的做法是这样的:

1、第1次将该数右移1位,与原数做或运算,这样就会有2位变成1;

2、第2次将该数右移2位,与原数做或运算,这样就会有4位变成1;

3、第3次将该数右移4位,与原数做或运算,这样就会有8位变成1;

4、第4次将该数右移8位,与原数做或运算,这样就会有16位变成1;

2、第5次将该数右移16位,与原数做或运算,这样就会有32位变成1;

这样就可以保证一个 int 类型的数在 + 1 之后一定是 2 的幂次大小,因为二进制数字如果都是1的话,那么他加一后就是首位为1其他位都是0,这个数字肯定是 2 的幂次方。

某次过程:

当某个数较小的时候,后面会有一些多余的运算但效率影响很小。

为什么 HashMap 的长度为什么需要是2的幂次方?

为了能让hashMap存取高效,尽量减少碰撞,也就是要尽量把数据分配均匀。在插入数据之前做取模运算,得到的余数就是将要存放的数据在哈希表中对应的下标。在HashMap中这个下标的取值算法是:(n - 1) & hash  n是哈希表的长度。

(17)HashMap 和 HashSet 有什么区别?

(18)HashMap 和 TreeMap 有什么区别?

HashMap 和 TreeMap 都继承自 AbstractMap,但是 TreeMap 还实现了 NavigableMap 和 SortedMap 接口。

Navigable 接口让 TreeMap 有了对集合内元素进行搜索的能力;

SortedMap 接口让 TreeMap 有了对 key 进行排序的能力(默认升序)。

Lambda 表达式实现自定义排序:

综上,TreeMap 相比 HashMap 有了对集合内元素搜索的能力和按照 key 进行排序的能力。

(19)HashSet 如何检查重复?

当把对象加入到 HashSet 中时,首先会计算对象的 hashcode 是否存在相同值;如果不存在则加入,如果存在相同的 hashcode,则使用 equals 方法判断它们是否真的一致;如果相同,HashSet就不会让加入操作成功。

(20)HashMap 的底层实现

1.8之前

HashMap 底层是 数组和链表 结合在一起实现。

HashMap 通过 key 的 hashcode 经过扰动函数后得到 hash 值,再通过 (n - 1) & hash 得到 value 需要插入的位置。如果当前位置存在元素的话,则比较它们是否相同。如果不同则使用拉链法解决冲突,如果相同的话直接进行覆盖。

* 扰动函数

扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法的原因是防止一些实现比较差的 hashcode() 方法产生影响,减小碰撞概率。

1.8之后

当链表长度大于阈值(默认为8)(将链表转换为红黑树之前对数组的长度进行判断,如果数组长度小于 64 先进行扩容,大于 64 才进行红黑树转换)时,将链表转换为红黑树,减少搜索时间。

为什么不使用二叉搜索树?

原因:二叉搜索树在某种情况下会退化成线性结构,而红黑树是一个平衡的二叉搜索树,可以避免这种情况。

(21)HashMap 多线程操作导致死循环问题

这个问题就是前面说的那个 头插法 导致的死循环问题。

在 1.8 之后得到了解决

(22)HashMap 有哪几种常见的遍历方式?

HashMap 有七种遍历方式

1、使用迭代器(Iterator)EntrySet 的方式进行遍历;

public class EntrySet {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
        while(iterator.hasNext()){
            Map.Entry<Integer, String> entry = iterator.next();
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
    }
}

2、使用迭代器(Iterator)KeySet 的方式进行遍历;

public class KeySet {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        Iterator<Integer> iterator = map.keySet().iterator();
        while(iterator.hasNext()){
            Integer key = iterator.next();
            System.out.println(key);
            System.out.println(map.get(key));
        }
    }
}

3、使用 For Each EntrySet 的方式进行遍历;

public class foreachEntrySet {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        for(Map.Entry<Integer,String> entry : map.entrySet()){
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
    }
}

4、使用 For Each KeySet 的方式进行遍历;

public class foreachKeySet {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        for(Integer key : map.keySet()){
            System.out.println(key);
            System.out.println(map.get(key));
        }
    }
}

5、使用 Lambda 表达式的方式进行遍历;

public class lambda {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");
        
        map.forEach((key, value) ->{
            System.out.println(key);
            System.out.println(value);
        });
    }
}

6、使用 Streams API 单线程的方式进行遍历;

public class streams1 {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        map.entrySet().stream().forEach((entry) -> {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        });
    }
}

7、使用 Streams API 多线程的方式进行遍历。

public class streams {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "JDK");
        map.put(3, "Spring Framework");
        map.put(4, "MyBatis framework");

        map.entrySet().parallelStream().forEach((entry) -> {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        });
    }
}

性能:

可以看出 EntrySet 的执行完成时间最短,性能最好;然后是 stream 和 keySet,性能最差的是 lambda表达式。

结论:entrySet 的性能比 keySet 的性能要高出一倍之多,所以我们应该尽量使用 entrySet 来进行Map 集合的遍历。

(23)ConcurrentHashMap 和 Hashtable 的区别是什么?

它们都是线程安全的,但是在实现线程安全的方式不同。

实现线程安全的方式

ConcurrentHashMap

1.7 的时候,ConcurrentHashMap 对整个桶数组进行了分割分段(Segment,分段锁),每一把锁只锁容器内其中一部分数据,多线程访问不同数据段内的数据的时候不存在冲突,可以提高并发访问率。

1.8 的时候,ConcurrentHashMap 摒弃了 Segment,转而使用 Node数组 + 链表 + 红黑树的数据结构进行实现,并发控制使用 synchronized 和 CAS 进行操作。整个就是 HashMap 进行优化且线程安全的升级版本。

Hashtable

也是使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法是,另一个线程也访问该方法,另一个线程可能进入阻塞或者轮询,这会导致竞争越来越激烈,效率越来越低。

(24)ConcurrentHashMap 线程安全的具体实现方法 / 底层具体实现?

1.8 之前

首先将数据分为 segment 的段进行存储,给每一段数据都配一把锁,当其中一个线程占用锁访问一段数据的时候,其他段的数据可以被访问。

数据结构:Segment 数组 和 HashEntry 数组

Segment 集成了 ReentrantLock,所以 Segment 是一种可重入锁。HashEntry 用于存储键值对数据。

一个 ConcurrentHashMap 包含一个 Segment 数组,该数组默认大小为 16,也就是默认最高可并发线程数为 16(并发写)。

Segment 的结构和 HashMap 类似,是一种由数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 数组保存链表元素。每个 Segment 守护对应的 HashEntry 数组。这也就是说对 HashEntry 数组内的元素进行修改的时候,首先获得对应的 Segment 的锁。

1.8 之后

ConcurrentHashMap 取消了 Segment 分段锁,采用 Node + CAS + synchronized 来保证并发安全,数据结构和 HashMap 1.8 的结构类似。Java 8 中,锁的粒度更细,synchronized 只锁定当前链表或红黑树的首节点,这样只要 hash 不冲突,就不会发生并发,就不会影响其他 Node 的读写,效率大幅提升。

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

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

相关文章

七大云安全威胁及其应对方法

关注公众号网络研究观获取更多内容。 对于任何依赖云来容纳快速增长的服务的企业来说&#xff0c;确保安全都是重中之重。然而&#xff0c;正如大多数云采用者很快意识到的那样&#xff0c;迁移到动态云环境需要新的和更新的安全措施&#xff0c;以确保数据和其他关键资产在整…

凯特王妃与戴安娜王妃:有跨越时空的优雅共鸣!

显而易见的是都是王妃,而王妃不仅是称谓也是一个头衔,她们同时要承担这个头衔应尽的职责! 在皇室世界里,总有一些名字,如同璀璨星辰,即便时光流转,依旧熠熠生辉。现在让我们揭开一段不为人知的幕后故事——凯特王妃与已故的戴安娜王妃之间,那些超越时代、共通的优雅与情…

前端模块化-手写mini-vite

前言 本文总结了一些关于 Vite 的工作原理&#xff0c;以及一些实现细节。 本节对应的 demo 可以在这里找到。 什么是 Vite Vite 是一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports&#xff0c;在服务器端按需编译返回&#xff0c;完全跳过了打包这个…

PyTorch深度学习实战(5)—— Tensor的命名张量和基本结构

1. 命名张量 命名张量&#xff08;Named Tensors&#xff09;允许用户将显式名称与Tensor的维度关联起来&#xff0c;便于对Tensor进行其他操作。笔者推荐使用维度的名称进行维度操作&#xff0c;这样可以避免重复计算Tensor每个维度的位置。支持命名张量的工厂函数&#xff08…

怎么删除iPhone重复照片:解放你的存储空间

在数字化时代&#xff0c;iPhone已成为我们记录生活点滴的重要工具。从家庭聚会的快乐时光到户外冒险的壮观景象&#xff0c;我们依靠iPhone捕捉无数珍贵瞬间。然而&#xff0c;这种便利性带来的一个副作用是&#xff0c;相册很快就会充满重复的照片&#xff0c;不仅占用了宝贵…

【IC设计】时序分析面试题总结(亚稳态、建立/保持裕量计算、最高时钟频率计算、时序违例解决办法)

文章目录 基本概念亚稳态建立时间和保持时间 常见问题1.为什么触发器要满足建立时间和保持时间&#xff1f;2.建立时间裕量和保持时间裕量的计算&#xff1f;3.最高时钟频率的计算&#xff1f;流水线思想&#xff1f;4.时序违例的解决办法&#xff1f; 基本概念 亚稳态 亚稳态…

简单的 CompletableFuture学习笔记

简单的 CompletableFuture学习笔记 这里记录一下自己学习的内容&#xff0c;简单记录一下方便后续学习&#xff0c;内容部分参考 CompletableFuture学习博客 1. CompletableFuture简介 在api接口调用时间过长&#xff0c;调用过多外围接口时&#xff0c;为了提升性能&#x…

Self-study Python Fish-C Note14 P50to51

函数 (part 4) 本节主要讲函数 递归 递归 (recursion) 递归就是函数调用自身的过程 示例1&#xff1a; def fun1(i):if i > 0: print(something)i-1fun1(i) fun1(5) # 这样就会打印五遍 somethingsomething something something something something要让递归正常工作&am…

IDEA2024.2重磅发布,更新完有4G!

JetBrains 今天宣布了其 IDE 家族版本之 2024.2 更新&#xff0c;其亮点是新 UI 现在已是默认设置&#xff0c;并且对 AI Assistant &#xff08;AI助手&#xff09;进行了几项改进。 安装密道 新 UI 的设计更加简约&#xff0c;可以根据需要以视觉方式扩展复杂功能。值得开发…

Arduino学习笔记2——初步认识Arduino程序

Arduino使用的编程语言是C。 一、注释文字 我们可以在程序中插入注释文字来提示开发者代码的作用。在Arduino中&#xff0c;单行注释用的是两个斜杠&#xff0c;多行注释用的是对称的斜杠加星号&#xff1a; 二、函数 和C语言相同&#xff0c;可以看到在打开IDE自动打开的默…

高并发下的分布式缓存 | Cache-Aside缓存模式

Cache-aside 模式的缓存操作 Cache-aside 模式&#xff0c;也叫旁路缓存模式&#xff0c;是一种常见的缓存使用方式。在这个模式下&#xff0c;应用程序可能同时需要同缓存和数据库进行数据交互&#xff0c;而缓存和数据库之间是没有直接联系的。这意味着&#xff0c;应用程序…

Java数据结构 | 二叉树基础及基本操作

二叉树 一、树型结构1.1 树的概念1.2 关于树的一些常用概念&#xff08;很重要&#xff01;&#xff01;&#xff01;&#xff09;1.3 树的表示形式1.4 树的应用 二、二叉树2.1 二叉树的概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基本操作2.5.1 代…

【前端可视化】 大屏可视化项目二 scale适配方案 g6流程图 更复杂的图表

项目介绍 第二个大屏可视化&#xff0c;整个项目利用scale进行按比例适配。 图表更加复杂&#xff0c;涉及到图表的叠加&#xff0c;mark&#xff0c;地图&#xff0c;g6流程图的能等 始终保持比例适配(本项目方案),始终满屏适配(项目一). echarts绘制较为复杂图表&#xff0…

C++:string类(auto+范围for,typeid)

目录 前言 auto typeid 范围for 使用方法 string类的模拟实现 默认构造函数 拷贝构造函数 swap 赋值重载 析构函数 迭代器iterator begin和end c_str clear size capacity []运算符重载 push_back reserve append 运算符重载 insert erase find npos…

postgresql 宝塔 连接不上,prisma

不太熟悉pgsql; 配置搞了半天; 一直连不上远程数据库; 后台经过探索发现需要以下配置 1. 端口放行; 5422 (pgsql的端口) 2.编辑 pg_hba.conf 文件最后新增一条,这样可以外部使用postgres超级管理员账号 host all all 0.0.0.0/0 md5 3. pris…

数据结构复杂度

文章目录 一. 数据结构前言1.1 数据结构1.2 算法 二. 算法效率2.1 时间复杂度2.1.1 T(N)函数式2.1.2 大O的渐进表示法 一. 数据结构前言 1.1 数据结构 什么是数据结构呢&#xff1f;打开一个人的主页&#xff0c;有很多视频&#xff0c;这是数据&#xff08;杂乱无章&#xf…

了解k8s架构,搭建k8s集群

kubernetes 概述 Kubernetes 集群图例 安装控制节点 安装网络插件 安装 calico 安装计算节点 2、node 安装 查看集群状态 kubectl get nodes # 验证容器工作状态 [rootmaster ~]# kubectl -n kube-system get pods

【学习笔记】:Maven初级

一、Maven简介 1、为什么需要maven Maven是一个依赖管理工具,解决如下问题: 项目依赖jar包多jar包来源、版本问题jar包导入问题jar包之间的依赖Maven是一个构建工具: 脱离IDE环境的项目构建操作,需要专门的工具2、Maven介绍 https://maven.apache.org/what-is-maven.htm…

代码随想录算法训练营第44天|LeetCode 1143.最长公共子序列、1035.不相交的线、53. 最大子序和、392.判断子序列

1. LeetCode 1143.最长公共子序列 题目链接&#xff1a;https://leetcode.cn/problems/longest-common-subsequence/description/ 文章链接&#xff1a;https://programmercarl.com/1143.最长公共子序列.html 视频链接&#xff1a;https://www.bilibili.com/video/BV1ye4y1L7CQ…

苹果离线打包机配置和打包

1、虚拟机安装 macOS虚拟机安装全过程&#xff08;VMware&#xff09;-腾讯云开发者社区-腾讯云 给 windows 虚拟机装个 mac 雪之梦 1、安装苹果镜像 去网上下载&#xff0c;打包机的镜像要和自己mac电脑上的保持一致。 同时打包机的用户名也需要和自己的mac保持一致。 2、…