[集合]-java

[集合]-java

一.什么是集合

1.0特殊的容器--数组

      数组也是一种集合,可以装基本数据类型,也可以用来装对象。在实际开发中,如果是一组对象,优先使用集合而不是数组当容器,而不是集合,因为集合的长度是可以伸缩的。

1.1什么是容器

      集合是很多容器的总称,专门用来装Java对象的。如果基本数据类型的值,放到集合中,会自动装箱位对应的包装类对象。

1.2容器的存储结构

      数组的元素是相邻的,连续的,内存中开辟一整块完整的存储空间。而集合的类型很丰富,底层可能是数组,可能是链表,也可能是树结构等。有的集合是有序的,有的是无序的,有点是允许元素重复的,有的是不允许元素重复的。

1.3分类

1.3.1Collection

用途:用于存储一组对象

1.3.2Map

用途:用于存储一组(key,value)键值对/映射关系

1.4关注点

(1)增删查改,遍历等操作

(2)不同集合的特点

(3)底层的实现原理

二.Set

2.1知识点

/** * 1. HashSet:追求极致性能的去重选择 * * - 底层结构:哈希表 (Hash Table) * - 存储规律:无序(不保证插入顺序,也不保证自然顺序)。 * - 核心特点: * 1. 查询、插入、删除效率极高,平均时间复杂度 O(1)。 * 2. 允许存储一个 null 元素。 * 3. 内存占用相对较小。 * - 迭代顺序:不可预测,随扩容可能改变。 * * 适用场景:仅需去重,对顺序无任何要求时的默认首选。 */ /** * 2. LinkedHashSet:兼顾性能与插入顺序 * * - 底层结构:哈希表 + 双向链表 (Hash Table + Doubly LinkedList) * - 存储规律:严格按插入顺序存储。 * - 核心特点: * 1. 查询效率接近 HashSet (O(1)),但略慢。 * 2. 额外维护链表指针,内存开销略高于 HashSet。 * 3. 允许存储一个 null 元素。 * * 适用场景:需要去重,且必须按插入顺序遍历时。 */ /** * 3. TreeSet:需要排序与范围查询的选择 * * - 底层结构:红黑树 (Red-Black Tree,一种自平衡二叉搜索树) * - 存储规律:按元素的自然顺序(升序)或自定义比较器排序。 * - 核心特点: * 1. 支持排序、获取最值(first/last)、范围查询(subSet)等操作。 * 2. 查询、插入、删除效率为 O(log n)。 * 3. 默认不允许 null 元素(会抛出 NullPointerException)。 * *注:若使用自定义 Comparator 且显式处理 null,则可存入 null。 * * 适用场景:需要自动排序、查找最值或范围数据时。 */ /** * 3.1 HashSet 与 LinkedHashSet 的去重原理 * * 依赖于元素对象的两个方法:hashCode() 和 equals()。 * * 1. 哈希定位:首先调用元素的 hashCode() 方法。 * - 如果哈希值不同,直接判定元素不重复,放入集合。 * - 如果哈希值相同,进入下一步(可能存在哈希冲突)。 * 2. 内容比对:调用 equals() 方法比较内容。 * - 如果 equals() 返回 true,判定为重复元素,添加失败。 * - 如果 equals() 返回 false,判定为不同元素,放入集合。 * * 注意:重写 equals() 时,必须重写 hashCode(),以保证逻辑一致。 */ /** * 3.2 TreeSet 的去重原理 * * 依赖于元素的比较结果。 * * 1. 判定标准:当比较结果返回 0 时,判定元素重复。 * 2. 优先策略:元素类实现 Comparable 接口,重写 compareTo() 方法。 * 3. 灵活策略:创建 TreeSet 时传入 Comparator 接口实现,自定义 compare() 方法。 * * 注意:如果两个对象通过比较器判断为相等(返回0),TreeSet 视为同一元素。 */ /** * 自定义对象用于测试去重 */ /** * 总结:如何选择? * * 1. 需要排序 / 范围查询? -> 选 TreeSet * 2. 需要保持插入顺序? -> 选 LinkedHashSet * 3. 什么都不需要,只要最快的去重? -> 选 HashSet (默认首选) * * 线程安全提示:以上三种 Set 实现均非线程安全。 * 多线程环境下需使用 Collections.synchronizedSet() 或并发包下的类。 */

2.2 coding

package Collection.Set; import Collection.Student; import Collection.Teacher; import org.junit.jupiter.api.Test; import java.util.Comparator; import java.util.HashSet; public class HashSet_ { /** * 1. HashSet:追求极致性能的去重选择 * * - 底层结构:哈希表 (Hash Table) * - 存储规律:无序(不保证插入顺序,也不保证自然顺序)。 * - 核心特点: * 1. 查询、插入、删除效率极高,平均时间复杂度 O(1)。 * 2. 允许存储一个 null 元素。 * 3. 内存占用相对较小。 * - 迭代顺序:不可预测,随扩容可能改变。 * * 适用场景:仅需去重,对顺序无任何要求时的默认首选。 */ /** * 2. LinkedHashSet:兼顾性能与插入顺序 * * - 底层结构:哈希表 + 双向链表 (Hash Table + Doubly LinkedList) * - 存储规律:严格按插入顺序存储。 * - 核心特点: * 1. 查询效率接近 HashSet (O(1)),但略慢。 * 2. 额外维护链表指针,内存开销略高于 HashSet。 * 3. 允许存储一个 null 元素。 * * 适用场景:需要去重,且必须按插入顺序遍历时。 */ /** * 3. TreeSet:需要排序与范围查询的选择 * * - 底层结构:红黑树 (Red-Black Tree,一种自平衡二叉搜索树) * - 存储规律:按元素的自然顺序(升序)或自定义比较器排序。 * - 核心特点: * 1. 支持排序、获取最值(first/last)、范围查询(subSet)等操作。 * 2. 查询、插入、删除效率为 O(log n)。 * 3. 默认不允许 null 元素(会抛出 NullPointerException)。 * *注:若使用自定义 Comparator 且显式处理 null,则可存入 null。 * * 适用场景:需要自动排序、查找最值或范围数据时。 */ /** * 3.1 HashSet 与 LinkedHashSet 的去重原理 * * 依赖于元素对象的两个方法:hashCode() 和 equals()。 * * 1. 哈希定位:首先调用元素的 hashCode() 方法。 * - 如果哈希值不同,直接判定元素不重复,放入集合。 * - 如果哈希值相同,进入下一步(可能存在哈希冲突)。 * 2. 内容比对:调用 equals() 方法比较内容。 * - 如果 equals() 返回 true,判定为重复元素,添加失败。 * - 如果 equals() 返回 false,判定为不同元素,放入集合。 * * 注意:重写 equals() 时,必须重写 hashCode(),以保证逻辑一致。 */ /** * 3.2 TreeSet 的去重原理 * * 依赖于元素的比较结果。 * * 1. 判定标准:当比较结果返回 0 时,判定元素重复。 * 2. 优先策略:元素类实现 Comparable 接口,重写 compareTo() 方法。 * 3. 灵活策略:创建 TreeSet 时传入 Comparator 接口实现,自定义 compare() 方法。 * * 注意:如果两个对象通过比较器判断为相等(返回0),TreeSet 视为同一元素。 */ /** * 自定义对象用于测试去重 */ /** * 总结:如何选择? * * 1. 需要排序 / 范围查询? -> 选 TreeSet * 2. 需要保持插入顺序? -> 选 LinkedHashSet * 3. 什么都不需要,只要最快的去重? -> 选 HashSet (默认首选) * * 线程安全提示:以上三种 Set 实现均非线程安全。 * 多线程环境下需使用 Collections.synchronizedSet() 或并发包下的类。 */ @Test public void testHashSetAdd() { HashSet hashSet = new HashSet(); hashSet.add("Hello"); hashSet.add("World"); hashSet.add("Hello");// 重复元素,自动去重,即添加失败 System.out.println(hashSet); } @Test public void testHashSetAddStudentAndCheckEqual() { HashSet set = new HashSet(); set.add(new Student("张三",89)); set.add(new Student("张三",89)); set.add(new Student("李四",78)); set.add(new Student("王五",91)); for(Object o:set){ System.out.println(o); } Student s1 = new Student("张三",89); Student s2 = new Student("张三",89); System.out.println(s1 == s2);//false System.out.println(s1.equals(s2));//true } }

三.List

3.1List接口的方法

package Collection.List; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; import java.util.function.Predicate; public class List_ { /* * List接口:继承自Collection接口 * 1.List特点和子类: * - 所有List接口的实现类有共同点:有序、元素可重复 - 所有List接口的实现类有相同的API:在List接口中定义 * List接口的实现类有:ArrayList、LinkedList、Vector,Stack继承自Vector ArrayList:jdk1.2开始出现的,底层是数组实现 LinkedList:jdk1.2开始出现的,底层是链表实现 Vector:jdk1.0开始出现的,底层是数组实现 * Stack:jdk1.0开始出现的,底层是数组实现 2.List的方法: * 2.1 增 * 2.1.1 boolean add(ElementType e) : 添加一个元素 * 2.1.2 void add(int index, ElementType e) : 添加一个元素,指定位置 * 2.1.3 boolean addAll(Collection<? extends ElementType> c) : 添加一个集合 * 2.1.4 boolean addAll(int index, Collection<? extends ElementType> c) : 添加一个集合,指定位置 * 2.2 删 * 2.2.1 ElementType remove(int index) : 删除指定位置的元素 * 2.2.2 boolean remove(Object o) : 删除一个元素 * 2.2.3 void clear() : 删除所有元素 * 2.2.4 boolean removeAll(Collection<?> c) : 删除一个集合 * 2.2.5 boolean retainAll(Collection<?> c) : 保留一个集合 * 2.2.6 removeIf(Predicate<? super ElementType> filter) : 删除满足条件的元素 * 2.3 查 * 2.3.1 ElementType get(int index) : 获取指定位置的元素 * 2.3.2 int indexOf(Object o) : 获取指定元素的索引 * 2.3.3 int lastIndexOf(Object o) : 获取指定元素的索引,从后往前找 * 2.3.4 List<ElementType> subList(int fromIndex, int toIndex) : 获取指定范围的子列表 * 2.4 改 * 2.4.1 ElementType set(int index, ElementType element) : 修改指定位置的元素 * 2.4.2 void replaceAll(UnaryOperator<ElementType> operator) : 替换所有元素 * 2.5 遍历 * 2.5.1 forEach(Consumer<? super ElementType> action) : 遍历 * 2.5.2 Iterator<ElementType> iterator() : 获取迭代器 * 2.5.3 for循环 + size() + get(index) * 2.5.4 ListIterator<ElementType> listIterator() : 获取列表迭代器 * *boolean hasNext() : 判断是否还有下一个元素 * *boolean hasPrevious() : 判断是否还有上一个元素 * *ElementType next() : 获取下一个元素 * *int nextIndex() : 获取下一个元素的索引 * *ElementType previous() : 获取上一个元素 * *int previousIndex() : 获取上一个元素的索引 * */ @Test public void testListCreateAndAdd() { /*2.1 增 * 2.1.1 boolean add(ElementType e) : 添加一个元素 * 2.1.2 void add(int index, ElementType e) : 添加一个元素,指定位置 * 2.1.3 boolean addAll(Collection<? extends ElementType> c) : 添加一个集合 * 2.1.4 boolean addAll(int index, Collection<? extends ElementType> c) : 添加一个集合,指定位置*/ ArrayList<String> list = new ArrayList<>(); list.add("Apple"); list.add("Orange"); list.add("Pear"); //System.out.println(list); ListIterator<String> iterator = list.listIterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } @Test public void testListRemove() { /*2.2 删 * 2.2.1 ElementType remove(int index) : 删除指定位置的元素 * 2.2.2 boolean remove(Object o) : 删除一个元素 * 2.2.3 void clear() : 删除所有元素 * 2.2.4 boolean removeAll(Collection<?> c) : 删除一个集合 * 2.2.5 boolean retainAll(Collection<?> c) : 保留一个集合 * 2.2.6 removeIf()*/ ArrayList<String> list = new ArrayList<>(); list.add("Apple"); list.add("Orange"); list.add("Pear"); System.out.println("原始列表为 : " + list); ListIterator<String> iterator = list.listIterator(); /*while (iterator.hasNext()) { System.out.println(iterator.next()); }*/ list.remove("Pear"); System.out.println("删除后的列表为 : " + list); list.remove(1); System.out.println("删除后的列表为 : " + list); list.add("Apple"); list.add("Orange"); list.add("Pear"); System.out.println("添加后的列表为 : " + list); Predicate<String> predicate = new Predicate<String>() { @Override public boolean test(String s) { return s.startsWith("A"); } }; list.removeIf(predicate); System.out.println("removeIf删除后的列表为 : " + list); } @Test public void testQuery(){ /* 2.3 查 * 2.3.1 ElementType get(int index) : 获取指定位置的元素 * 2.3.2 int indexOf(Object o) : 获取指定元素的索引 * 2.3.3 int lastIndexOf(Object o) : 获取指定元素的索引,从后往前找 * 2.3.4 List<ElementType> subList(int fromIndex, int toIndex) : 获取指定范围的子列表*/ ArrayList<String> list = new ArrayList<>(); list.add("Apple"); list.add("Orange"); list.add("Pear"); list.add("Watermaloon"); list.add("Banana"); list.add("Apple"); list.add("Apple"); list.add("Banana"); System.out.println("原始列表为 : " + list); System.out.println("Apple的索引为 : " + list.indexOf("Apple")); System.out.println("索引1的元素为 : " + list.get(1)); System.out.println("最后一个Apple的索引为 : " + list.lastIndexOf("Apple")); System.out.println("子列表为 : " + list.subList(2,5)); } @Test public void testTraverse(){ /*2.5 遍历 * 2.5.1 forEach(Consumer<? super ElementType> action) : 遍历 * 2.5.2 Iterator<ElementType> iterator() : 获取迭代器 * 2.5.3 for循环 + size() + get(index) * 2.5.4 ListIterator<ElementType> listIterator() : 获取列表迭代器 * *boolean hasNext() : 判断是否还有下一个元素 * *boolean hasPrevious() : 判断是否还有上一个元素*/ ArrayList<String> list = new ArrayList<>(); list.add("Apple"); list.add("Orange"); list.add("Pear"); list.add("Watermaloon"); list.add("Banana"); list.add("Apple"); list.add("Apple"); list.add("Banana"); System.out.println("遍历方法1 : "); for(String s:list){ System.out.println(s); } System.out.println("遍历方法2 : "); for(int i = 0; i < list.size(); i++){ System.out.println(list.get(i)); } System.out.println("遍历方法3 : "); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } }

3.2ArrayList和Verctor

3.2.1ArrayList和Vector的区别

/* * 各种数组的区别 : * 1.List接口的子类 * Vector : 古老的动态数组 * Stack : 古老的栈结构容器 * ArrayList : jdk1.2开始出现的较新的动态数组 * LinkedList : jdk1.2开始出现的较新的链表(同时可以当栈节后,队列使用) * 2.动态数组ArrayList和Vector的区别 : * - ArrayList : * 1.线程不安全,速度更快 * 2.new ArrayList()的底层的数组长度默认是0,首次添加元素时,会自动扩容为10 * 3.扩容机制 : 数组扩容为原来的1.5倍 * 4.优点 : 空间利用率高 * 5.缺点 : 扩容频率高,搬家次数多,无法手动指定扩容的数量 * * - Vector : * 1.线程安全,速度更慢 * 2.new Vector()的底层的数组长度默认是10 * 3.优点 : 扩容频率少,搬家次数少 * 4.缺点 : 空间浪费的可能性大 * 5.Vector考虑到自己扩容太快,一次变为原来的2倍长度,就提供了另一种扩容的方式,你可以手动指定扩容多少空间。 * * */

3.2.2ArrayList最初容量为0,首次添加后长度变为10,扩容机制为原来的1.5倍

创建伊始:
添加10个元素

package Collection.List; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Vector; public class ArrayList_ { /* * 各种数组的区别 : * 1.List接口的子类 * Vector : 古老的动态数组 * Stack : 古老的栈结构容器 * ArrayList : jdk1.2开始出现的较新的动态数组 * LinkedList : jdk1.2开始出现的较新的链表(同时可以当栈节后,队列使用) * 2.动态数组ArrayList和Vector的区别 : * - ArrayList : * 1.线程不安全,速度更快 * 2.new ArrayList()的底层的数组长度默认是0,首次添加元素时,会自动扩容为10 * 3.扩容机制 : 数组扩容为原来的1.5倍 * 4.优点 : 空间利用率高 * 5.缺点 : 扩容频率高,搬家次数多,无法手动指定扩容的数量 * * - Vector : * 1.线程安全,速度更慢 * 2.new Vector()的底层的数组长度默认是10 * 3.优点 : 扩容频率少,搬家次数少 * 4.缺点 : 空间浪费的可能性大 * 5.Vector考虑到自己扩容太快,一次变为原来的2倍长度,就提供了另一种扩容的方式,你可以手动指定扩容多少空间。 * * */ @Test public void testArrayList() { ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add("hello" + i); System.out.println("第" + i + "次添加元素"); } } @Test public void testVector() { Vector<String> v = new Vector<>(10,2);//创建一个容量为10,每次扩容2倍 for(int i = 0; i < 10; i++){ v.add("hello" + i); } System.out.println(v); v.add("hello10李"); System.out.println(v.size()); } }

3.3Stack

3.3.1Stack功能

Stack是栈结构的容器,它遵循 先进后出 的原则。 注意:Stack同时也是List接口的实现类,所以List之前的方法,它也有。 Stack提供了几个特殊的方法,来满足 先进后出的操作原则: (1)push(元素):入栈 (2)pop():弹出栈顶元素,然后返回 (3)peek():查看栈顶元素 (4)search(元素):从栈顶数这个元素是第几个

3.3.2code

package Collection.List; import org.junit.jupiter.api.Test; import java.util.Stack; public class Stack_ { /* * Stack是栈结构的容器,它遵循 先进后出 的原则。 注意:Stack同时也是List接口的实现类,所以List之前的方法,它也有。 Stack提供了几个特殊的方法,来满足 先进后出的操作原则: (1)push(元素):入栈 (2)pop():弹出栈顶元素,然后返回 (3)peek():查看栈顶元素 (4)search(元素):从栈顶数这个元素是第几个 * */ @Test public void testStackCreateAndAdd() { Stack<String> s = new Stack<>(); s.push("Apple"); s.push("Orange"); s.push("Pear"); System.out.println(s); } @Test public void testStackRemove() { Stack<String> s = new Stack<>(); s.push("Apple"); s.push("Orange"); s.push("Pear"); System.out.println("原始栈为 : " + s); System.out.println("弹出的栈顶元素为 : " + s.pop()); System.out.println("弹出的栈顶元素为 : " + s.pop()); } @Test public void testStackQuery() { Stack<String> s = new Stack<>(); s.push("Apple"); s.push("Orange"); s.push("Pear"); System.out.println("原始栈为 : " + s); System.out.println(s.search("Apple")); } @Test public void testPeek(){ Stack<String> s = new Stack<>(); s.push("Apple"); s.push("Orange"); s.push("Pear"); System.out.println("原始栈为 : " + s); System.out.println("栈顶元素为 : " + s.peek()); } @Test public void testStackTraverse() { Stack<String> s = new Stack<>(); s.push("Apple"); s.push("Orange"); s.push("Pear"); System.out.println("原始栈为 : " + s); while (!s.empty()) { System.out.println(s.pop()); } } }

3.4.1创建顺序

调用 LinkedList 对象的核心内存流程:先加载类(方法区)→ 执行 main 方法(虚拟机栈)→ 在堆中创建 LinkedList 对象 → 虚拟机栈中存储对象引用

3.4.2LinkList方法

package Collection.List; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.LinkedList; public class LinkedList_ { /* 1 LinkedList 的核心特性 1.1 底层结构:双向链表,每个节点包含前驱、后继指针,无固定容量,无需扩容 1.2 核心优势:首尾元素增删效率高(O (1)),支持双端操作 1.3 核心劣势:随机访问效率低(O (n)),需遍历链表 2 List 接口通用方法(LinkedList 实现) 这类方法是所有 List 集合的通用方法,LinkedList 完全支持,核心用于 “通用的增删改查”。 2.1 新增元素 2.1.1 boolean add (E e):将元素添加到链表末尾,返回是否成功(始终返回 true) 2.1.2 void add (int index, E element):在指定索引位置插入元素(需遍历到指定位置,效率低) 2.1.3 boolean addAll (Collection<? extends E> c):将集合中所有元素添加到链表末尾 2.1.4 boolean addAll (int index, Collection<? extends E> c):从指定索引开始插入集合中所有元素 2.2 删除元素 2.2.1 E remove (int index):删除指定索引位置的元素,返回被删除的元素 2.2.2 boolean remove (Object o):删除第一个匹配的指定元素,返回是否删除成功 2.2.3 void clear ():清空链表中所有元素 2.3 修改元素 2.3.1 E set (int index, E element):替换指定索引位置的元素,返回原元素 2.4 查询元素 2.4.1 E get (int index):获取指定索引位置的元素(需遍历链表,随机访问效率低) 2.4.2 int size ():返回链表中元素的个数 2.4.3 boolean isEmpty ():判断链表是否为空 2.4.4 boolean contains (Object o):判断链表是否包含指定元素 2.4.5 int indexOf (Object o):返回第一个匹配元素的索引,无则返回 - 1 2.4.6 int lastIndexOf (Object o):返回最后一个匹配元素的索引,无则返回 - 1 2.5 遍历相关 2.5.1 Iterator<E> iterator ():获取普通迭代器,正向遍历 2.5.2 ListIterator<E> listIterator ():获取 List 迭代器,支持双向遍历 2.5.3 ListIterator<E> listIterator (int index):从指定索引开始的 List 迭代器 3 LinkedList 特有方法(双端队列 / 链表专属) 这类方法是 LinkedList 作为双端队列(Deque)的特有方法,专门优化 “首尾操作”,效率为 O (1),是 LinkedList 的核心优势。 3.1 首尾新增元素(队列 / 栈操作) 3.1.1 void addFirst (E e):将元素添加到链表头部 3.1.2 void addLast (E e):将元素添加到链表尾部(等同于 add (E e)) 3.1.3 boolean offerFirst (E e):添加元素到头部,返回是否成功(始终返回 true) 3.1.4 boolean offerLast (E e):添加元素到尾部,返回是否成功(始终返回 true) 3.1.5 void push (E e):将元素压入栈顶(等同于 addFirst (E e),栈操作) 3.2 首尾删除元素 3.2.1 E removeFirst ():删除并返回头部元素,链表为空时抛 NoSuchElementException 3.2.2 E removeLast ():删除并返回尾部元素,链表为空时抛 NoSuchElementException 3.2.3 E pollFirst ():删除并返回头部元素,链表为空时返回 null(更安全) 3.2.4 E pollLast ():删除并返回尾部元素,链表为空时返回 null(更安全) 3.2.5 E pop ():弹出栈顶元素(等同于 removeFirst (),栈操作) 3.3 首尾查询元素(不删除) 3.3.1 E getFirst ():获取头部元素,链表为空时抛 NoSuchElementException 3.3.2 E getLast ():获取尾部元素,链表为空时抛 NoSuchElementException 3.3.3 E peekFirst ():获取头部元素,链表为空时返回 null(更安全) 3.3.4 E peekLast ():获取尾部元素,链表为空时返回 null(更安全) */ /* *Vector和ArrayList的区别: 1.ArrayList底层是一个数组。 数组的元素是连续存储的,开辟一整块连续的存储空间,如果通过下标访问元素,效率非常高,时间复杂度是O(1)。 O(1)是一个固定值,代表无论数组长度是多长,根据下标查找元素的效率是一样的,都是一步到位。 比喻:大家出去住酒店,房间号是连续的,你告诉我房间号,我直接奔这个房间就可以了。 数组的长度是固定的,不过存了就要扩容。需要频繁的搬家。 数组如果中间插入或删除元素,后面的元素需要移动,因为要保证连续性。 2.LinkedList:它的底层是一个双向链表。 链表的元素不一定是连续的,需要前后元素记录对方的地址。比喻:大家出去住酒店,房间号不是连续的,甚至不在一个酒店。每个人需要记前后同学住在哪里。 如果要找第5个人,需要从第1个开始,陆续拿到下一个同学的地址,找到第5个。如果通过下标访问元素,效率较低,时间复杂度是O(n)。 链表不需要扩容,不需要搬家。 问:动态数组效率高,还是链表效率高? (1)如果根据下标访问元素,动态数组高。 (2)如果是非末尾位置插入和删除,理论上是链表高,因为不需要扩容移动元素,也不需要扩容。 如果是在Java中,双向链表的效率不见得高,因为双向链表需要结点对象,而创建结点,回收结点也是一个麻烦的事情。 * */ @Test public void testLinkedListCreateAndAdd() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); list.add("hello4"); System.out.println("添加后的列表为 : " + list); LinkedList<String> list2 = new LinkedList<>(Arrays.asList("hello5", "hello6", "hello7")); System.out.println("添加后的列表为 : " + list2); } @Test public void testAddAsInsert(){ LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); list.add(1,"hello4"); System.out.println("修改后的列表为 : " + list); } @Test public void testAddFirst() { LinkedList<String> list = new LinkedList<>(); list.addFirst("hello1"); list.addFirst("hello2"); list.addFirst("hello3"); System.out.println("添加后的列表为 : " + list); } @Test public void testAddLast() { LinkedList<String> list = new LinkedList<>(); list.addLast("hello1"); list.addLast("hello2"); list.addLast("hello3"); System.out.println("添加后的列表为 : " + list); } @Test public void testRemoveFirst() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); System.out.println("删除前的列表为 : " + list); list.removeFirst(); System.out.println("删除后的列表为 : " + list); } @Test public void testRemoveLast() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); System.out.println("删除前的列表为 : " + list); list.removeLast(); System.out.println("删除后的列表为 : " + list); } @Test public void testRemoveMediumElem(){ LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); list.add("hello4"); list.remove("hello2"); list.remove(2); System.out.println("删除后的列表为 : " + list); } @Test public void testGetFirst() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); System.out.println("获取的元素为 : " + list.getFirst()); } @Test public void testGetLast() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); System.out.println("获取的元素为 : " + list.getLast()); } @Test public void testSet() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); list.set(1, "hello4"); System.out.println("修改后的列表为 : " + list); } @Test public void testContains() { LinkedList<String> list = new LinkedList<>(); list.add("hello1"); list.add("hello2"); list.add("hello3"); System.out.println("列表中是否包含hello2 : " + list.contains("hello4")); } }

3.4Queue

package Collection.Queue; import org.junit.jupiter.api.Test; import java.util.LinkedList; import java.util.Queue; public class Queue_ { /* * 普通队列方法: * 1. 插入 * 1.1 add(E e):将元素添加到队列末尾,返回是否成功(始终返回 true) 队列满时抛出异常 * 1.2 offer(E e):将元素添加到队列末尾,返回是否成功(始终返回 true) 不抛出异常,返回特殊值 * * 2. 删除 * 2.1 remove():删除并返回队列头部元素,队列为空时抛 NoSuchElementException * 2.2 poll():删除并返回队列头部元素,队列为空时返回 null(更安全) * * 3.检查 * 3.1 element():返回队列头部元素,队列为空时抛 NoSuchElementException * 3.2 peek():返回队列头部元素,队列为空时返回 null(更安全) * * 4. 查询 * 4.1 size():返回队列元素个数 * 4.2 isEmpty():判断队列是否为空 * 4.3 contains(Object o):判断队列是否包含指定元素 * 4.4 toArray():返回队列元素 * * 5. 遍历 * 5.1 迭代器:iterator() * 5.2 增强for:for (E e : queue) * 5.3 普通for:for (int i = 0; i < queue.size(); i++) * */ @Test public void testQueueCreateAndOffer(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); System.out.println("添加后的队列为 : " + queue); } @Test public void testQueueRemoveAndPoll(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); System.out.println("删除前的队列为 : " + queue); queue.remove(); System.out.println("删除后的队列为 : " + queue); queue.poll(); System.out.println("删除后的队列为 : " + queue); } @Test public void testQueueElementAndPeek(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); System.out.println("队首元素为 : " + queue.element()); System.out.println("队首元素为 : " + queue.peek()); } @Test public void testQueueSizeIsEmptyContains(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); System.out.println("队列元素个数为 : " + queue.size()); System.out.println("队列是否为空 : " + queue.isEmpty()); System.out.println("队列是否包含 hello1 : " + queue.contains("hello1")); System.out.println("队列是否包含 hello4 : " + queue.contains("hello4")); } @Test public void testQueueToArray(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); Object[] list = queue.toArray(); for(Object o : list){ System.out.println(o); } } @Test public void testQueueIterator(){ Queue<String> queue = new LinkedList<>(); queue.offer("hello1"); queue.offer("hello2"); queue.offer("hello3"); for(String s : queue){ System.out.println(s); } } }

四.Map

Read more

【MySQL】win 10 / win11:mysql 5.7 下载、安装与配置

【MySQL】win 10 / win11:mysql 5.7 下载、安装与配置

目录 一、MySQL 下载 (1)MySQL 官网下载地址 (2)下载保存安装包 二、MySQL 安装 (1)运行安装包 (2)选择安装类型 (3)选择安装版本号 (4)配置服务端口 (5)配置 root 的密码 (6)配置服务名称 (7)安装完成 三、配置系统环境变量 (1)打开系统环境变量配置面板 (2)编辑系统变量 Path 四、验证安装完成 五、Navicat 测试连接 (1)连接数据库 (2)填写连接信息 (3)测试连接 (4)保存连接 (5)高级配置(

By Ne0inhk

Spring Boot 深度解析

一、Spring Boot 核心定位 Spring Boot 是基于 Spring 框架的快速开发脚手架,核心目标是简化Spring应用的初始化搭建和开发过程,解决了传统Spring应用「配置繁琐、依赖管理复杂、部署麻烦」的痛点。 * 核心理念:约定优于配置(Convention Over Configuration),通过默认配置减少开发者的配置工作; * 核心优势: 1. 自动配置:根据引入的依赖自动配置Spring组件(如引入spring-boot-starter-web自动配置Spring MVC); 2. 起步依赖:将常用依赖打包为starter,一键引入即可使用(如spring-boot-starter-web包含Spring MVC、Tomcat、Jackson等); 3. 嵌入式容器:内置Tomcat/Jetty/Undertow,无需手动部署WAR包,可直接运行JAR包; 4. 简化监控:通过spring-boot-starter-actuator快速实现应用监控; 5. 无代码生成/无XML配置:纯Java配置,开箱即用;

By Ne0inhk

Spring Boot 自动配置

目录 什么是自动配置? Spring 加载 Bean @ComponentScan @Import 导入类 导入 ImportSelector 接口的实现类 SpringBoot 原理分析 @EnableAutoConfiguration @Import(AutoConfigurationImportSelector.class)  AutoConfigurationPackage SpringBoot 自动配置流程 什么是自动配置? Spring Boot 的自动配置:当 Spring 容器启动后,一些配置类、bean 对象等就自动存入 Ioc 容器中,而不再需要我们手动去声明,从而简化了程序开发过程,省去了繁琐的配置操作 也就是说,Spring Boot 的自动配置,就是 SpinrgBoot 将依赖 jar 包中的配置类以及 Bean 加载到 Spring Ioc 容器中的过程 在本篇文章中,

By Ne0inhk
Rust异步编程高级模式:并发控制、超时机制与实战架构

Rust异步编程高级模式:并发控制、超时机制与实战架构

Rust异步编程高级模式:并发控制、超时机制与实战架构 一、异步并发控制:Semaphore、Mutex、RwLock的异步版本 1.1 为什么需要异步同步原语? 💡在同步编程中,我们使用std::sync::Mutex、std::sync::RwLock、std::sync::Semaphore等同步原语来控制并发访问。这些原语在多线程场景下非常有效,但在异步编程中,它们会导致任务阻塞,影响性能。 异步同步原语通过await关键字暂停任务,而不是阻塞线程,从而提高了CPU利用率。Tokio提供了一系列异步同步原语,如tokio::sync::Mutex、tokio::sync::RwLock、tokio::sync::Semaphore。 1.2 异步Mutex(互斥锁) 异步Mutex的使用方式与标准库的类似,但需要使用await来获取锁。 usetokio::sync::Mutex;usestd::sync::Arc;

By Ne0inhk