Java进阶-集合(1)
进入Java集合的学习,集合的数学概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。在Java中的集合也是类似的,先学习集合的框架,这次主要介绍一下Conllection接口。
一、概述
1、数组存储
数组存储具有两大缺点:
1)初始化后长度不可变,难以保存数量变化的数据。
2)只能按索引顺序存取,无法保存具有映射关系的数据。
如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。
2、集合
2.1 目的
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组)
2.2 作用
集合类主要负责保存、盛装其他数据,因此也被称为容器类。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
2.3 特点
1)接口和实现类相分离。
2)支持泛型,可以限制在一个集合中只能放入同一种数据类型的元素,如
List
list = new ArrayList<>(); // 只能放入String类型。关于泛型后续会再讲。
3)通过统一的方式——迭代器(Iterator) 实现访问(遍历)集合,而无需知道集合内部元素是按什么方式存储的。
2.4 区别
在存储的元素类型上
数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量)。
集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
2.5 步骤
集合操作的一般步骤如下
创建集合对象(容器)—->创建元素对象(物品)—->将元素添加进集合(物品装入容器)—->集合操作(相关方法如add()、remove()…/遍历集合/…)
遍历集合的三种方式—->iterator迭代器、for-each循环、for循环
二、Collection接口
1、概述
Collection 接口:Iterable 的子接口,也是 List、Set 和 Queue 的父接口(存放一组单值的最大接口,单值:集合中的每个元素都是一个对象),一般很少直接使用此接口直接操作。
Iterator 接口 :集合的输出接口,主要用于遍历输出Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。
2、Collection 接口中常用的方法
Collection 接口中常用的方法可以看成是对容器的操作
add(Object o)、addAll(Collection c) (前者为元素增删操作,后者为集合整体元素增删操作,以下同理)
remove(Object o)、removeAll(Collecrion c)
contains(Object o)、containsAll(Collection c),判断是否包含
其他如 clear()、isEmpty()、size()、toArray() (把集合转换为一个数组)等等。不必硬记,会用就行,更多参考java API文档。
3、遍历集合元素方法
Iterator迭代器(推荐)、for-each循环、for循环
3.1 几点规则
1)Iterator迭代器和for-each循环迭代变量不是集合元素本身,系统只是依次把集合元素的值赋给迭代变量,所以在遍历时不能对 Collection 集合里的元素进行修改,否则会抛出 ConcurrentModificationException
2)for循环在遍历时可以对 Collection 集合里的元素进行修改。
3)java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改。
4、示例
直接看代码,示例1
1 | import java.util.ArrayList; //导包 |
运行结果
1 | list2 集合中的元素如下: |
示例2
1 | import java.util.ArrayList; //导包 |
运行结果
1 | list1 集合中的元素数量:3 |
注意:
retainAll() 方法的作用与 removeAll() 方法相反,即保留两个集合中相同的元素,其他全部删除。
Collection 是接口,不能实例化,可以通过其实现类ArrayList调用 Collection 方法
5、关于泛型(后续将详细介绍)
- 上述代码系统可能输出一些警告提示 (即未使用泛型来限制集合里的元素类型),可以先不理会。
- 在传统模式下,把一个对象“丢进”集合中后,集合会忘记这个对象的类型(系统把所有的集合元素都当成 Object 类型)。从 Java 5 以后,可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型。
- 泛型编程(有兴趣的自行了解)
6、其他(自行学习)
- Collection类:操作 Set、List 和 Map 等集合的工具类。提供了许多操作集合的静态方法,可以实现集合元素的排序、查找替换和复制等操作。
- 使用Lambda表达式遍历Collection集合。
三、Queue/Dueue子接口(可不看)
Queue 是 Java 提供的队列实现,有点类似于 List。Dueue 是 Queue 的一个子接口,为双向队列。
- ArrayDueue:基于数组实现的双端队列,按“先进先出”的方式操作集合元素。
四、List子接口
List 实现了 Collection 接口,主要有两个常用的实现类:ArrayList 类和 LinkedList 类。
1、List接口常用方法
1.1 判断两个对象相等
- equals() 方法比较,相等返回true。
1.2 增加
- void add(int index, Object element):将元素 element 插入到 List 集合的 index 处,索引范围 [0, size)
- boolean addAll(int index, Collection c):将集合 c 所包含的所有元素都插入到 List 集合的 index 处
1.3 删除
- Object remove(int index):删除并返回 index 索引处的元素
1.4 修改
- Object set(int index, Object element):将 index 索引处的元素替换成 element 对象,返回被替换的旧元素
1.4 查询
- Object get(int index):返回集合 index 索引处的元素
- int indexOf(Object o):返回对象 o 在 List 集合中第一次出现的位置索引
- int lastIndexOf(Object o):返回对象 o 在 List 集合中最后一次出现的位置索引
1.5 其他
- List subList(int fromlndex, int tolndex):返回从索引 fromlndex(包含)到索引 tolndex(不包含)处所有集合元素组成的子集合,返回的列表由此列表支持,因此返回列表中的非结构性更改将反映在此列表中,反之亦然
- ListIterator<E> listIterator(int index):返回一个 ListIterator 对象(双向的迭代器),从列表的指定位置开始
1.6 默认方法
- void replaceAll(UnaryOperator<E> operator):对列表中的每一个元素执行特定的操作,并用处理的结果替换该元素
- void sort(Comparator<E> c):使用提供的 Comparator 来比较元素排序该列表
1.7 常用构造器
- ArrayList():构造一个初始容量为 10 的空列表
- ArrayList(Collection<? extends E> c):构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的
- HashSet():构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75
- HashSet(Collection<? extends E> c):构造一个包含指定 collection 中的元素的新 set。
上述方法无需硬记,忘记时导包选中List后ctrl+B查看源码即可
2、LinkedList
2.1 优点
对顺序访问进行了优化。
LinkedList 类采用链表结构保存对象,便于向集合中插入或删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
2.2 缺点
随机访问的速度相对较慢。
LinkedList 类随机访问元素的速度相对较慢(随机访问是指检索集合中特定索引位置的元素)。
2.3 方法
除了Collection接口和List 接口中的所有方法,还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)来用。
2.4 示例
使用 LinkedList 实现仓库管理系统中商品名称的记录入库,并输出第一个录入的商品名称和最后一个商品名称
1 | import java.util.Iterator; |
运行结果
1 | *************** 商品信息 *************** |
==示例总结(自己完成)==
在学习过程中要养成总结的习惯(可以回顾以前所学,加深印象的同时能发现盲区,横扫问题解决不足)。针对上述示例,用到的方法有
1.对象创建:字符串对象的创建、LinkedList集合对象的创建
2.接口方法:Collection接口的方法(add…)、List接口的方法(for循环遍历用到的get()方法得到对应索引的元素…)、LinkedList集合方法(addLast()、removeLast()、getFirst()、getLast()…)
3.遍历方法:三种遍历方法(迭代器、for循环、for-each)
4.迭代器:迭代器对象的创建、迭代器的next()和hasNext()方法
5.泛型……
2.5 总结(重点)
总结一下ArrayList 类和 LinkedList 类的区别,非常重要,面试可能会问到。两者都是List接口的实现类,区别如下:
1)ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
2)对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
3、ArrayList (常用)
一个用数组实现的 List(能进行快速的随机访问,效率高且实现了可变大小的数组)
3.1 常用构造方法(重载)
- ArrayList():构造一个初始容量为 10 的空列表。(无参构造)
- ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。
3.2 常用方法
包括Collection接口的所有方法和List接口的方法。
常用:添加—add(),访问—get(),修改—set(),删除—remove,大小—size(),位置—index(),迭代方法(for,for-each,iterator),其他方法可自行总结添加
3.3 几点注意
- 注1:调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素 (下标0开始,有效最大到3),即set方法不会改变 List 集合的长度。
- 注2:在使用 List 集合时需要注意区分 indexOf() 方法和 lastIndexOf() 方法。前者是获得指定对象的最小索引位置,而后者是获得指定对象的最大索引位置(前提条件是指定的对象在 List 集合中有重复的对象,否则这两个方法获取的索引值相同,就没有意义)
List subList(int fromlndex, int tolndex)方法:左闭右开。
3.4 示例
1)创建一个商品类 Product,在该类中定义 3 个属性和 toString() 方法,分别实现 setter/getter 方法
1 | public class Product { //创建Product类 (标准javabean) |
2)创建测试类(两种方法比较)
2.1)以前的方法(未用集合)
1 | public class ProductTest { //创建测试类 |
运行结果
1 | *************** 商品信息 *************** |
2.2)集合方法
1 | import java.util.ArrayList; // 导包 |
2 示例
1)subList()方法(要求集合元素全为字符串,即使用泛型)
1 | import java.util.ArrayList; |
运行结果
1 | arr集合中的元素数量:7 |
五、Set子接口
1、概述
- set集合中的对象是无序的。
- 不能包含重复的元素,且最多只能包含一个null元素。
2、TreeSet
2.1 概述
- 实现了 Set 接口和 SortedSet 接口,是一个有序的 Set(能从 Set 里面提取一个有序序列),SortedSet 接口是 Set 接口的子接口,可以实现对集合的自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的(升序排序)
- TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。
- 如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。
2.2 方法
实现Collection接口的所有方法。
TreeSet类常用方法(以下 E为数据类型)
1)E first()、E last():返回集合的第一个和最后一个元素
2)E poolFirst()、E poolLast():获取并移除集合的第一个和最后一个元素
3)截取子TreeSet的方法:
SortedSet<E> subSet(E fromElement,E toElement)、SortedSet<E> headSet<E toElement〉、SortedSet<E> tailSet(E fromElement):返回一个新的集合,新集合包含原集合中( fromElement 对象与 toElement对象之间、 toElement 对象之前、fromElement 对象之后)的所有对象。且左闭右开。
2.3 示例
需求:有 5 名学生参加考试,当老师录入每名学生的成绩后,程序将按照从低到高的排列顺序显示学生成绩。此外,老师可以查询本次考试是否有满分的学生存在,不及格的成绩有哪些,90 分以上成绩的学生有几名。使用 TreeSet 类创建 Set 集合,完成学生成绩查询功能。
1 | import java.util.Iterator; //导包 |
运行结果
1 | ------------学生成绩管理系统------------- |
注:在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 集合中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。
3、HashSet
3.1 概述
1)HashSet是 Set 接口的典型实现(Set 集合中最常用的实现类)。按照 Hash 算法存储集合中的元素,具有很好的存取和查找性能。
2)基于 HashMap 实现,为优化査询速度而设计的 Set。HashSet 底层使用 HashMap 来保存所有元素,实现比较简单。
3.2 特点
- 没有实现SortedSet接口,不能保证元素的排列顺序(顺序可能与添加顺序不同,即可能发生变化)
- HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步
- 集合元素值可以是 null。
元素相等:两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
3.3 构造方法(重载)
1)HashSet():构造一个新的空的 Set 集合。
HashSet hs = new HashSet(); // 调用无参的构造函数创建HashSet对象
2)HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 集合中存放的集合元素类型(泛型)。c 表示其中的元素将被存放在此 Set 集合中。
HashSet<String> hss = new HashSet<String>(); // 创建泛型的 HashSet 集合对象
3.4 示例
使用 HashSet 创建一个 Set 集合,并向该集合中添加 4 套教程。
1 | import java.util.HashSet; //导包 |
运行结果
1 | ******书栈网教程****** |
注:如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。