当前位置: 首页 > news >正文

JavaSE丨集合框架入门:从0掌握Collection与List核心用法

        这节我们学习集合相关内容。

一、集合概述

        在Java中,集合(Collection)是一种用于存储和操作一组对象的数据结构。它提供了一组接口和类,用于处理和操作对象的集合。

        集合框架(Collection Framework)是Java中用于表示和操作集合的一组类和接口。它位于 java.util 包中,并提供了一系列的接口和类,包括集合接口 (Collection)、列表接口(List)、集合类(Set)、映射接口(Map)等。

        集合框架的主要目标是提供一种通用的方式来存储和操作对象的集合,无论集合的具体实现方式如何,用户都可以使用统一的接口和方法来操作集合。

集合理解:

集合和数组都可以存储多个元素值,对比数组,我们来理解下集合:

  • 数组的长度是固定的,集合的长度是可变的
  • 数组中存储的是同一类型的元素,集合中存储的数据可以是不同类型的
  • 数组中可以存放基本类型数据或者引用类型变量,集合中只能存放引用类型变量
  • 数组是由JVM中现有的 类型+[ ] 组合而成的,除了一个 length 属性 ,还有从 Object 中继承过来的方法之外,数组对象就调用不到其他属性和方法了
  • 集合框架由 java.util 包下多个接口和实现类组成,定义并实现了很多方法,功能强大

二、框架体系

2.1 组成要素

集合框架主要由三个要素组成:

1)接口

        整个集合框架的上层结构,都是用接口进行组织的。接口中定义了集合中必须要有的基本方法。 通过接口还把集合划分成了几种不同的类型,每一种集合都有自己对应的接口。

2)实现类

        对于上层使用接口划分好的集合种类,每种集合的接口都会有对应的实现类。 每一种接口的实现类很可能有多个,每个的实现方式也会各有不同。

3)数据结构

        每个实现类都实现了接口中所定义的最基本的方法,例如对数据的存储、检索、操作等方法。但是不同的实现类,它们存储数据的方式不同,也就是使用的数据结构不同。

集合框架继承体系图:

2.2 集合分类

1)单列集合(Single Column Collection)

根接口: java.util.Collection 单列集合是指每个集合元素只包含一个单独的对象,它是集合框架中最简单的形式

2)多列集合(Multiple Column Collection)

根接口: java.util.Map 多列集合是指每个集合元素由多个列(字段)组成,可以同时存储和操作多个相关的值

Collection 接口结构图:

Map 接口结构图:

三、Collection

        Collection接口是单列集合类的父接口,这种集合可以将数据一个一个的存放到集合中。它有两个重要的子接口,分别是 java.util.List java.util.Set

如图:

Collection是父接口,其中定义了单列集合(List和Set)通用的一些方法, Collection接口的实现类,都可以使用这些方法。

1)Collection集合基础方法

public interface Collection<E> extends Iterable<E> {//省略...//向集合中添加元素  boolean     add(E e)//清空集合中所有的元素    void        clear()//判断当前集合中是否包含给定的对象boolean     contains(Object o)//判断当前集合是否为空。boolean     isEmpty()//把给定的对象,在当前集合中删除boolean     remove(Object o)//返回集合中元素的个数int         size()//把集合中的元素,存储到数组中    Object[]    toArray()
}

2)Collection 泛型约束

通过泛型约束避免 “运行时类型转换异常”:

  • 没有泛型时,集合可以存储任意类型元素,取出时需要强制转换,容易出现 ClassCastException;
  • 有泛型后,集合在声明时指定<存储的数据类型>,编译器会自动检查元素类型,不符合则编译报错

正确写法:集合接口引用指向实现类对象,固定书写格式:

        接口类型 接口引用名 = new 实现类<>(构造方法实参);

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;public class GenericDemo {public static void main(String[] args) {// 1. 接口引用指向实现类(最推荐,符合面向接口编程)Collection<String> coll = new ArrayList<>(); // 只能且只能存StringList<Integer> list = new LinkedList<>();     // 有且只能存Integer// 2. 具体实现类引用指向实现类(合法,但灵活性低)ArrayList<Student> studentList = new ArrayList<>(); // 只能存Student// 存入正确类型元素(编译通过)coll.add("Java");list.add(100);studentList.add(new Student("张三", 20));}
}class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}
}

3)Collection 泛型补充

泛型支持 “父类型泛型引用指向子类型元素”,但反之不允许:

// 正确:集合中可以存Student的子类(假设Graduate是Student的子类)
ArrayList<Student> students = new ArrayList<>();
students.add(new Graduate("王五", 22)); // 合法,Graduate是Student的子类// 错误:父类型元素不能存入子类型泛型集合
ArrayList<Graduate> graduates = new ArrayList<>();
graduates.add(new Student("赵六", 21)); // 编译报错,Student不是Graduate的子类

泛型参数方法:

public interface Collection<E> extends Iterable<E> {//省略... //把一个指定集合中的所有数据,添加到当前集合中    boolean     addAll(Collection<? extends E> c)//判断当前集合中是否包含给定的集合的所有元素。    boolean     containsAll(Collection<?> c)//把给定的集合中的所有元素,在当前集合中删除。    boolean     removeAll(Collection<?> c)//判断俩个集合中是否有相同的元素,如果有当前集合只保留相同元素,如果没有当前集合元素清空    boolean     retainAll(Collection<?> c)//把集合中的元素,存储到数组中,并指定数组的类型  <T> T[]     toArray(T[] a)//返回遍历这个集合的迭代器对象    Iterator<E> iterator()
}

泛型方法测试案例:

import java.util.ArrayList;
import java.util.Collection;// 泛型方法测试
public class Test_Element {public static void main(String[] args) {// 1. 实例化两个集合对象,专门存放 String 类型元素// 集合实例化对象 固定写法Collection<String> c1 = new ArrayList<>();Collection<String> c2 = new ArrayList<>();// 2. 分别往 c1 和 c2 集合中添加元素String s1 = "hello";String s2 = "world";c1.add(s1);c1.add(s2);String s3 = "nihao";String s4 = "hello";String s5 = "okok";c2.add(s3);c2.add(s4);c2.add(s5);System.out.println("c1: " + c1);System.out.println("c2: " + c2);System.out.println("-----------");// 3. 将 c2 集合整体添加到 c1 中c1.addAll(c2);System.out.println("c1.size: " + c1.size());System.out.println("after addAll(c2), c1: " + c1);System.out.println("-----------");// 4. 判断是否包含指定元素boolean f = c1.contains("hello");System.out.println("contains hello: " + f);// 5. 创建 s6 对象,判断集合中是否包含该对象// 注意: s6 的地址 和 "world" 地址不一样// s6 是堆中临时 new 出来的,"world" 存在堆中的字符串常量池中String s6 = new String("world");// 结果显示 true,说明集合 contains 方法借助 equals 方法进行比较,而非 ==f = c1.contains(s6);System.out.println("contains(s6): " + f);System.out.println("-----------");// 6. 判断是否包含 c2 对象f = c1.containsAll(c2);System.out.println("containsAll(c2): " + f);System.out.println("-----------");// 7. 删除指定元素【底层借助 equals 比较,然后删除】f = c1.remove(s6);System.out.println("remove(s6): " + f);System.out.println("after remove, c1: " + c1);System.out.println("-----------");// 8. 删除 c2 整个集合【底层实现:遍历 c2,逐个元素 equals 比较,然后删除】f = c1.removeAll(c2);System.out.println("removeAll(c2): " + f);System.out.println("after remove, c1: " + c1);}
}

4)集合存放自定义类对象

定义Student类,创建多个对象放入集合中。测试集合的contains和remove方法。

注意:先不重写equals方法进行测试,再重写equals方法进行测试。

Student 学生类:

public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString(){return "name:"+name+",age:"+age;}/*@Overridepublic boolean equals(Object obj){if (this == obj) {return true;}if (obj == null) {return false;}if (getClass() != obj.getClass()) {return false;}Student other = (Student)obj;if (name == null) {if (other.name != null) {return false;}}else if (!name.equals(other.name)) {return false;}if (age != other.age) {return false;}return true;} */}

测试类:

import java.util.ArrayList;
import java.util.Collection;public class Test_Student {public static void main(String[] args) {//准备学生对象 让其中两个学生对象的属性一模一样Student s1 = new Student("zs",20);Student s2 = new Student("ls",19);Student s3 = new Student("ww",22);Student s4 = new Student("tom",18);Student s5 = new Student("zs",20);//1.定义只能存储Student对象的集合Collection<Student> coll = new ArrayList<>();//2.往集合里添加元素coll.add(s1);coll.add(s2);coll.add(s3);coll.add(s4);coll.add(s5);//3.输出集合元素个数,输出集合对象System.out.println("coll.size:"+coll.size());System.out.println("coll:"+coll);System.out.println("---------------------");//4.判断s5是否存在boolean flag = coll.contains(s5);System.out.println("contains(s5):"+flag);//5.删除s5flag = coll.remove(s5);System.out.println("remove(s5):"+flag);System.out.println("coll.size:"+coll.size());System.out.println("coll:"+coll);}
}

注意事项:集合中contains、remove等方法,底层借助元素对象的equals方法进行值比较,所以如果要用集合存放自定义类对象,注意重写自定义类的equals方法!

四、集合遍历

单列集合的遍历,一般有3种方法:

4.1 toArray

        借助Collection接口中toArray()方法实现,方法原型为:Object[ ]  toArray();

遍历格式:

//将集合转化成数组
Object[] array = 集合引用.toArray();//遍历数组
for (int i = 0; i < array.length; i++) {System.out.println(array[i]);
}

4.2 迭代器

        迭代器是集合框架提供的一种遍历集合元素的方式。通过调用集合的 iterator() 方法可以获取一个迭代器对象,然后使用迭代器的 hasNext() 方法判断是否还有下一个元素,使用 next() 方法获取下一个元素

固定格式:

//1.获取迭代器对象
Iterator<集合元素类型> iterator = 集合对象.iterator();//2.借助迭代器中hasNext()和next()方法完成遍历
while (iterator.hasNext()) {//获取集合元素集合元素类型 变量名 = iterator.next();//对集合元素进行输出System.out.println(变量名);
}

示例:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class CollectionTraverse {public static void main(String[] args) {Collection<String> coll = new ArrayList<>();coll.add("Java");coll.add("Python");coll.add("C++");// 1. 获取迭代器对象Iterator<String> it = coll.iterator();// 2. 循环遍历(hasNext()判断是否有下一个元素)while (it.hasNext()) {// 3. next()获取下一个元素String element = it.next();System.out.println(element);// 遍历中删除元素(只能用迭代器的remove()方法)if (element.equals("Python")) {it.remove(); // 安全删除当前元素}}System.out.println("删除后集合:" + coll); // [Java, C++]}
}
  • 迭代器的优点是提供了一种统一的遍历方式,对单列集合(直接或间接实现了Collection接口),都可以通过迭代器按顺序访问元素
  • 迭代器隐藏了集合的具体实现细节,提供了一种抽象的访问接口,使代码更加灵活和可复用
  • 如果在迭代过程中对集合进行修改(添加、删除元素),可能会导致迭代器抛出 ConcurrentModificationException 异常。因此,在使用迭代器遍历时,不要修改集合结构

4.3 foreach 

除了使用迭代器遍历集合之外,JDK1.5及以后版本JDK,提供了增强for循环实现集合遍历,这种方式相对迭代器遍历更简单。

遍历格式:

 for(集合元素类型 变量名 : 集合) {//操作元素变量
}

每次循环,引用变量会指向集合中的一个元素对象,然后在循环体中对该元素对象进行操作

示例:

import java.util.ArrayList;
import java.util.Collection;public class CollectionTraverse {public static void main(String[] args) {Collection<String> coll = new ArrayList<>();coll.add("Java");coll.add("Python");coll.add("C++");// 增强for循环遍历for (String element : coll) { System.out.println(element); // 直接获取每个元素}}
}

五、List 接口

        java.util.List 接口继承了 Collection 接口,是常用的一种集合类型。

        List 集合具有 Collection 集合的特点之外,还具有自己的一些特点:

  • List 是一种有序集合

例如,向集合中存储的元素顺序是8、2、5。那么集合中就是按照这个顺序进行存储的

  • List 是一种带索引的集合

可以通过元素的下标索引,精确查找对应的元素数据

  • List集合可以存放重复元素

可以把相同的数据,在List集合中多次保存

5.1 继承体系

        List接口继承了Collection接口,Collection接口继承了Iterable接口。

List接口的实现类:

5.2 常用方法

//返回集合中指定位置的元素。
E       get(int index);
//用指定元素替换集合中指定位置的元素,并返回被替代的旧元素。
E       set(int index, E element);
//将指定的元素,添加到该集合中的指定位置上。
void    add(int index, E element);
//从指定位置开始,把另一个集合的所有元素添加进来
boolean addAll(int index, Collection<? extends E> c);
//移除列表中指定位置的元素, 并返回被移除的元素。
E       remove(int index);
//查收指定元素在集合中的所有,从前往后查到的第一个元素(List集合可以重复存放数据)
int     indexOf(Object o);
//查收指定元素在集合中的所有,从后往前查到的第一个元素(List集合可以重复存放数据)
int     lastIndexOf(Object o);
//根据指定开始和结束位置,截取出集合中的一部分数据
List<E> subList(int fromIndex, int toIndex);

注意:除了这些方法之外,还有从父接口Collection中继承过来的方法

示例:

import java.util.ArrayList;
import java.util.List;public class ListInheritMethodDemo {public static void main(String[] args) {List<Integer> list = new ArrayList<>();// 1. add(E e):末尾添加list.add(10);list.add(20);list.add(30);System.out.println("集合:" + list); // [10, 20, 30]// 2. addAll(Collection):添加另一个集合List<Integer> other = new ArrayList<>();other.add(40);other.add(50);list.addAll(other);System.out.println("添加other后:" + list); // [10, 20, 30, 40, 50]// 3. contains(Object o):判断包含boolean has20 = list.contains(20);System.out.println("是否包含20:" + has20); // true// 4. remove(Object o):删除首次出现的元素boolean removed = list.remove(Integer.valueOf(30)); // 注意:int需装箱为IntegerSystem.out.println("是否删除30:" + removed); // trueSystem.out.println("删除后:" + list); // [10, 20, 40, 50]// 5. size():元素个数System.out.println("元素个数:" + list.size()); // 4// 6. clear():清空list.clear();System.out.println("清空后是否为空:" + list.isEmpty()); // true}
}

5.3 ArrayList

        java.util.ArrayList 是最常用的一种List类型集合, ArrayList 类底层使用动态数组来实现数据的存储,所以它的特点是:增删慢,查找快。

        在日常的开发中,查询数据也是用的最多的功能,所以ArrayList是最常用的集合。

        但是,如果项目中对性能要求较高,并且在集合中大量的数据做增删操作,那 ArrayList 就不太适合了。

示例:

import java.util.ArrayList;public class ArrayListDemo {public static void main(String[] args) {// 1. 创建ArrayList(指定初始容量为5,避免频繁扩容)ArrayList<String> list = new ArrayList<>(5);// 2. 添加元素list.add("Java");list.add("Python");list.add("C++");list.add(1, "Go"); // 在索引1处插入"Go"System.out.println("添加后:" + list); // [Java, Go, Python, C++]// 3. 获取元素String element = list.get(2);System.out.println("索引2的元素:" + element); // Python// 4. 修改元素String old = list.set(3, "JavaScript");System.out.println("替换的旧元素:" + old); // C++System.out.println("修改后:" + list); // [Java, Go, Python, JavaScript]// 5. 删除元素String removed = list.remove(1); // 删除索引1的元素System.out.println("删除的元素:" + removed); // GoSystem.out.println("删除后:" + list); // [Java, Python, JavaScript]// 6. 查询操作int index = list.indexOf("Python");System.out.println("Python的索引:" + index); // 1boolean contains = list.contains("Java");System.out.println("是否包含Java:" + contains); // true// 7. 其他操作System.out.println("元素个数:" + list.size()); // 3list.clear();System.out.println("清空后是否为空:" + list.isEmpty()); // true}
}

5.4 LinkedList

        java.util.LinkedList 底层采用的数据结构是双向链表,其特点是:增删快,查找慢

        它的特点刚好和 ArrayList 相反,所以在代码中,需要对集合中的元素做大量的增删操作的时候,可以选择使用 LinkedList 。

        注意:这里描述的快和慢,需要在大量的数据操作下,才可以体现,如果数据量不大的话,每一种集合的操作几乎没有任何区别。

示例:

import java.util.LinkedList;public class LinkedListDemo {public static void main(String[] args) {// 1. 创建LinkedList(无需指定初始容量,链表无“容量”概念)LinkedList<String> list = new LinkedList<>();// 2. List基础操作:添加、获取、修改list.add("A"); // 尾部添加(等同于addLast)list.add(1, "B"); // 索引1处插入list.add("C");System.out.println("初始列表:" + list); // [A, B, C]String elem = list.get(2); // 获取索引2的元素System.out.println("索引2的元素:" + elem); // CString oldElem = list.set(1, "B+"); // 修改索引1的元素System.out.println("修改的旧元素:" + oldElem); // BSystem.out.println("修改后列表:" + list); // [A, B+, C]// 3. Deque双端队列操作:头部/尾部增删查list.addFirst("Head"); // 头部添加list.addLast("Tail"); // 尾部添加System.out.println("双端操作后:" + list); // [Head, A, B+, C, Tail]String first = list.getFirst(); // 获取头部String last = list.getLast(); // 获取尾部System.out.println("头部元素:" + first + ",尾部元素:" + last); // Head、Taillist.removeFirst(); // 删除头部list.removeLast(); // 删除尾部System.out.println("删除头尾后:" + list); // [A, B+, C]// 4. 栈操作(LIFO)list.push("Top1"); // 栈顶添加(头部)list.push("Top2");System.out.println("栈结构:" + list); // [Top2, Top1, A, B+, C]String popElem = list.pop(); // 栈顶删除(头部)System.out.println("弹出的栈顶元素:" + popElem); // Top2System.out.println("弹出后栈:" + list); // [Top1, A, B+, C]// 5. 其他操作System.out.println("是否包含A:" + list.contains("A")); // trueSystem.out.println("元素个数:" + list.size()); // 4list.clear();System.out.println("清空后是否为空:" + list.isEmpty()); // true}
}
对比维度ArrayListLinkedList
底层结构动态数组(连续存储)双向链表(非连续存储)
随机访问(get效率高(O(1)效率低(O(n)
头部 / 尾部增删效率低(O(n),需移动元素)效率高(O(1),仅改指针)
中间位置增删效率低(O(n)效率中等(O(n)定位 +O(1)修改)
内存占用可能有冗余空间(扩容预留)每个节点额外存储前后指针(内存开销更大)
适用场景读多写少、频繁随机访问写多读少、频繁头部 / 尾部操作

5.5 Vector

       Vector是在JDK1.0引入的,它实现了List接口,属于Java集合框架的一部分,其基于动态数组(Dynamic Array)实现,线程安全,Vector在功能和使用方式上和 ArrayList非常相似。

        ArrayList是在JDK 1.2引入的,非线程安全,但单线程环境下性能更高效,是 Vector的一个非线程安全的替代品

示例:

import java.util.Vector;public class VectorDemo {public static void main(String[] args) {// 1. 创建Vector(指定初始容量10,扩容增量5)Vector<String> vector = new Vector<>(10, 5);// 2. 添加元素vector.add("Java");vector.add("Python");vector.add(1, "C++"); // 索引1处插入System.out.println("添加后:" + vector); // [Java, C++, Python]// 3. 获取与修改元素String elem = vector.get(2);System.out.println("索引2的元素:" + elem); // PythonString oldElem = vector.set(0, "JavaScript");System.out.println("修改的旧元素:" + oldElem); // JavaSystem.out.println("修改后:" + vector); // [JavaScript, C++, Python]// 4. 删除元素vector.remove(1); // 删除索引1的元素System.out.println("删除后:" + vector); // [JavaScript, Python]// 5. 其他操作System.out.println("是否包含Python:" + vector.contains("Python")); // trueSystem.out.println("元素个数:" + vector.size()); // 2// 6. 枚举器遍历(特有方式,类似迭代器)System.out.println("枚举器遍历:");var enumeration = vector.elements();while (enumeration.hasMoreElements()) {System.out.println(enumeration.nextElement());}}
}
对比维度VectorArrayList
线程安全线程安全(方法被 synchronized 修饰)线程不安全(无同步机制)
性能单线程环境下性能较低(同步开销)单线程环境下性能更高
扩容机制默认扩容至原来的 2 倍,可指定增量默认扩容至原来的 1.5 倍,不可指定增量
迭代器支持 Enumeration 和 Iterator仅支持 Iterator 和 ListIterator
初始容量默认初始容量 10无参构造器初始容量为 0(JDK 8+)

六、List 小结

        这节学习了 Collection 与 List 相关内容,下节我们再进一步学习 Set 等相关内容。

http://www.dtcms.com/a/354460.html

相关文章:

  • Two Knights (数学)
  • Feign整合Sentinel实现服务降级与Feign拦截器实战指南
  • uni-app 网络请求与后端交互完全指南:从基础到实战
  • 智能养花谁更优?WebIDE PLOY技术与装置的结合及实践价值 —— 精准养护的赋能路径
  • 【LeetCode】29. 两数相除(Divide Two Integers)
  • PhotoshopImageGenerator:基于Photoshop的自动化图像数据集生成工具
  • C# 操作 DXF 文件指南
  • WAF对比传统防火墙的优劣势
  • 从Cgroups精准调控到LXC容器全流程操作​:用pidstat/stress测试Cgroups限流,手把手玩转Ubuntu LXC容器全流程​
  • 打破存储局限:CS 创世 SD NAND 如何优化瑞芯微(RK)与北京君正平台的贴片式 SD 卡性能
  • 横扫SQL面试——流量与转化率分类
  • 机器人电源电感的认证和认证细节,知多少?
  • Spring Boot 整合 SSE, http长连接
  • odoo打印新解
  • lesson48:Ubuntu下Python与三大数据库实战:MySQL、MongoDB、Redis全攻略
  • 基于uni-app的iOS应用上架,从打包到分发的全流程
  • 算法题打卡力扣第15题:三数之和(mid)
  • 本地构建的 Docker 镜像迁移到另一台电脑上运行
  • Python自动化测试完整教程:pytest + selenium实战
  • Windows 环境下搭建移动端自动化测试环境(JDK + SDK + Node.js + Appium)
  • 【Rust】 4. 函数与闭包
  • React过渡更新:优化渲染性能的秘密
  • 在Excel和WPS表格中隔一行插入一个空白行
  • HarmonyOS 中的 sharedTransition:实现流畅的页面过渡动画
  • 从数字到价值:ESG评级的深层变革
  • 鸿蒙 5.1 深度解析:ArkUI 4.1 升级与分布式开发新范式
  • Linux 软件编程(十三)网络编程:TCP 并发服务器模型与 IO 多路复用机制、原理epoll
  • 【Windows】netstat命令解析及端口状态解释
  • 【PostgreSQL内核学习:通过 ExprState 提升哈希聚合与子计划执行效率(二)】
  • 现代前端状态管理:从原理到实战(Vue/React全栈方案)