[集合]-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

Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443)

Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443)

Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443) * 引言: * 正文: * 一、 核心认知:Flink CDC 与全增量同步逻辑 * 1.1 Flink CDC 核心原理 * 1.1.1 与传统数据同步方案的对比(实战选型参考) * 1.2 全增量同步核心逻辑(MySQL→Hive) * 1.2.1 关键技术点(实战必关注,每个点都踩过坑) * 二、 环境准备:生产级环境配置(可直接复用) * 2.1 核心依赖配置(pom.xml)

中秋满月皆十六圆?Java实证求解后的真相

中秋满月皆十六圆?Java实证求解后的真相

目录 前言 一、天文上的满月 1、形成原理及定义 2、出现时间及观测 3、文化意义 二、Java模拟月满计算 1、整体实现逻辑 2、主计算方法详解 3、核心天文算法详解 3.1 儒略日计算基础 3.2 时间参数计算 3.3 天文参数计算 3.4 周期项修正计算 4、辅助方法详解 4.1 角度标准化 4.2 日历与儒略日转换 4.3 儒略日转日历 三、近年中秋满月计算及对比 1、近年中秋满月计算 2、近年计算与公布时间对比 四、总结 前言

Spring Boot 4.0 + JDK 25 + GraalVM:下一代云原生Java应用架构

Spring Boot 4.0 + JDK 25 + GraalVM:下一代云原生Java应用架构

🧑 博主简介:ZEEKLOG博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可关注公众号 “ 心海云图 ” 微信小程序搜索“历代文学”)总架构师,16年工作经验,精通Java编程,高并发设计,分布式系统架构设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。 🤝商务合作:请搜索或扫码关注微信公众号 “ 心海云图 ” Spring Boot 4.0 + JDK 25 + GraalVM:下一代云原生Java应用架构 摘要 随着云原生架构的快速演进,传统Java应用面临的“启动慢、内存高、体积大”三座大山亟待解决。

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南

HTML5+CSS3+JavaScript实现高木同学圣诞树GalGame完整开发指南 摘要:圣诞节快要到了,本文详细介绍了一个基于Web技术栈开发的完整GalGame(美少女游戏)项目。从项目架构设计、核心技术实现到性能优化,全面阐述如何使用纯前端技术构建具有丰富交互功能的视觉小说游戏。项目包含完整的对话系统、角色表情管理、分支剧情和存档功能,是Web前端技术在游戏开发领域应用的优秀实践案例。 📋 目录 * 1. 项目概述与目标 * 2. 技术架构选型 * 3. 核心功能实现 * 4. 界面设计与响应式布局 * 5. 开发难点与解决方案 * 6. 性能优化与用户体验 * 7. 部署与运行指南 * 8. 扩展功能与未来展望 * 9. 总结与收获 1. 项目概述与目标 1.1 项目背景 随着HTML5、CSS3和现代JavaScript技术的快速发展,Web平台已经能够承载复杂的交互应用。GalGame作为强调剧情叙事和角色互动的游戏类型,非常适合使用Web技术来实现。本项目选择热门动漫《擅长捉弄人的高木同学》作为题材,结合圣诞节主题,开发一个温馨有趣