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