问题 引入
我们之前学习过数组,数组特点如下:
**长度固定:**数组在创建时需要指定长度,并且长度在创建后不可改变相同数据类型:例如int数组只能存储int类型的数据
**连续内存分配:**堆空间为数组开辟的内存是连续的,这也导致在插入和删除元素时需要元素整体移动,效率低下
**随机访问:**由于数组中的元素在内存中是连续存储的,并且可以通过索引来访问,因此可以通过索引直接访问数组中的任意元素,时间复杂度为O(1)
如果我们要存储的多个元素值数据类型不一致,或个数不固定时,数组就无法完美的满足我们的要求,这个时候我们会使用Java中提供的集合框架。
1.集合概述
在Java中,集合(Collection)是一种用于存储和操作一组对象的数据结构。它提供了一组接口和类,用于处理和操作对象的集合。
集合框架(Collection Framework)是Java中用于表示和操作集合的一组类和接口。它位于 java.util 包中,并提供了一系列的接口和类,包括集合接口(Collection)、列表接口(List)、集合类(Set)、映射接口(Map)等。
集合框架的主要目标是提供一种通用的方式来存储和操作对象的集合 ,无论集合的具体实现方式如何,用户都可以使用统一的接口和方法来操作集合。
集合理 解: 集合和数组都可以存储多个元素值,对比数组,我们来理解下集合:
数组的长度是固定的,集合的长 度是可变的
数组中存储的是同一类型的元素,集合中存储 的数据可以是不同类型的
数组中可以存放基本类型数据或者引用类型变量,集合中只能存放 引用类型 变量
数组是由JVM中现有的 类型+[] 组合而成的,除了一个 length属性 ,还有从Object中继承过来的方法 之外,数组对象就调用不到其他属性和方法了
集合框架由 java.util 包下多个接口和实现类组成,定义并实现了很多方法,功能强大
2.框架体系
集合框架主要有三个要素组成:
接口
整个集合框架的上层结构,都是用接口进行组织的。接口中定义了集合中必须要有的基本方法。
通过接口还把集合划分成了几种不同的类型,每一种集合都有自己对应的接口。
实现类
对于上层使用接口划分好的集合种类,每种集合的接口都会有对应的实现类。
每一种接口的实现类很可能有多个,每个的实现方式也会各有不同。
数据 结构
每个实现类都实现了接口中所定义的最基本的方法,例如对数据的存储、检索、操作等方法。但是不同的实现类,它们存储数据的方式不同,也就是使用的数据结构 不同。
集合框 架继承体系图:
集合分 类:
单列集合(Single Column Collection)
根接口: java.util.Collection
单列集合是指每个集合元素只包含一个单独的对象,它是集合框架中最简单的形式
多列集合(Multiple Column Collection)
根接口: java.util.Map
多列集合是指每个集合元素由多个列(字段)组成,可以同时存储和操作多个相关的值
Collection 接口 结构图:
Map 接口结构图如 下:
注意事 项:
图中列出了Java集合框架中的主要接口(并非全部),以及它们之间的继承关系
接口中定义了该种集合具有的主要方法
将来真正要使用的,是这些接口的实现类,每种实现类对接口的实现方式不同,底层所用的数据结构不同,其特点也不同
集合章 节学习基本要求:
要求会用集合存储数据可以从集合中取出数据
掌握每种集合的特点和应用场景
3.Collection
Collection接口是单列 集合类的父接口,这种集合可以将数据一个一个的存放到集合中。它有两个重要的子接口,分别是 java.util.List 和 java.util.Set
如图:
Collection是父接口,其中定义了单列集合(List和Set)通用的一些方法,Collection接口的实现类,都可以使用这些方法。
1 ) Collection 集 合基础方法
package java.util;
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()
}
案例展 示:
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collection;
public class Test03_CollectionBasic {
// 集合中 一般 也放 同一种类型数据
// contains remove
public static void main(String[] args) {
// 1.接口 引用 = new 实现类(实参);
// 暂时可把它当成固定写法
Collection coll = new ArrayList(); if (coll.isEmpty())
System.out.println("coll is Empty!");
// 2.任何引用类型都可以放入
// 自动扩容
coll.add("hello"); //String Integer i = 12;
coll.add(i);
coll.add(2.3); // Double
coll.add(1.2F); // Float
coll.add('a'); // Character
int[] arr = { 1, 2, 3 }; coll.add(arr); //int[]
// coll.add(new Student());
// 3.输出 coll.toString() System.out.println(coll); System.out.println("size: " + coll.size()); System.out.println("--------------");
// 4.清空 coll coll.clear(); System.out.println(coll);
// 5.判断是否为空
if (coll.isEmpty()) System.out.println("coll is Empty!");
else
System.out.println("coll is not empty!");
}
}
2 )警告问 题处理
集合接口 引用指向实现类对象,固定书写格式:
接口类型<存储的数据类型> 接口引用名 = new 实现类<>(构造方法实参);
例: Collection<String> coll = new ArrayList<>();
注意:上述格式定义的集合对象,只能存储 String 类型的元素,如 果存放其他引 用类型 元素,则编译报错。
集合中父类或实现类引用,指向具体类对象的写法也是如此例: ArrayList<Student> list = new ArrayList<>() ;注意,此时创建的list集合,只能存储Student类对象。
上述代码可以参考 ArrayList 构造方法 源码:
package java.util;
//ArrayList也用到了<E>,表示其为泛型类【后续泛型章节会具体讨论,暂时了解即可】
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable,
java.io.Serializable {
//省略...
/** 无参构造器
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
}
其中参数中的 E 代表范型,取自单词Element的首字母,目前大家可以理解为任何类型。
3 ) Collection 泛型参数 方法补充
package java.util;
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()
}
泛型方 法测试案例:
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collection;
//泛型方法测试
public class Test03_Element {
public static void main(String[] args) {
//1.实例化两个集合对象,专门存放String类型元素
// 集合实例化对象 固定写法
Collection<String> c1 = new ArrayList<>();
Collection<String> c2 = new ArrayList<>();
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("-------------");
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("-------------");
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方法进行测试。
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collection;
class Student {
private String name; private int age;
public Student() {}
public Student(String name, int age) { super();
this.name = name; this.age = age;
}
//重写equals方法
// @Override
// public 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 (age != other.age)
// return false;
// if (name == null) {
// if (other.name != null)
// return false;
// } else if (!name.equals(other.name))
// return false;
// return true;
// }
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]"; }
}
public class Test03_Student {
public static void main(String[] args) {
//准备学生对象,注意s1和s5两个对象的属性一模一样
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);
Collection<Student> coll = new ArrayList<>();
//2.往集合中添加元素coll.add(s1); coll.add(s2); coll.add(s3); coll.add(s4);
//3.输出集合元素个数,输出集合对象System.out.println("coll.size: " + coll.size()); System.out.println("coll: " + coll);
System.out.println("------------");
boolean flag = coll.contains(s5); System.out.println("contains(s5): " + flag);
flag = 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 方 法!
4.集合遍历
单列集合的遍历,一般有3种方法,我们由易到难分别介绍。
1.toArray
借助Collection接口中toArray()方法实现,方法原型为: Object[] toArray();遍历格 式:
//将集合转化成数组
Object[] array = 集合引用.toArray();
//遍历数组
for (int i = 0; i < array.length; i++) { System.out.println(array[i]);
}
案例展 示:
定义一个集合,添加多个String字符串,然后遍历输出!
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collection;
public class Test041_ToArray {
public static void main(String[] args) {
//1.实例化集合对象,专门存放String元素
Collection<String> c1 = new ArrayList<>();
String s1 = "hello"; String s2 = "world"; String s3 = "nihao";
//2.往集合中添加元素c1.add(s1); c1.add(s2); c1.add(s3);
/3.遍历集合第一种形式
Object[] array = c1.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
System.out.println("--------------"); }
}
2.迭代器
迭代器是集合框架提供的一种遍历集合元素的方式。通过调用集合的
iterator() 方法可以获取一个迭代器对象,然后使用迭代器的 hasNext() 方法判断是否还有下一个元素,使用 next() 方法获取下一个元素。
遍历固定格式:
//1.获取迭代器对象
Iterator<集合元素类型> iterator = 集合对象.iterator();
//2.借助迭代器中hasNext()和next()方法完成遍历
while (iterator.hasNext()) {
//获取集合元素
集合元素类型 变量名 = iterator.next();
//对集合元素进行输出System.out.println(变量名);
}
案例展 示:
用迭代器遍历上述案例中集合元素。
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator;
public class Test042_Iterator {
public static void main(String[] args) {
//1.实例化集合对象,专门存放String元素
Collection<String> c1 = new ArrayList<>();
String s1 = "hello"; String s2 = "world"; String s3 = "nihao";
c1.add(s1);
c1.add(s2); c1.add(s3);
Iterator<String> it = c1.iterator(); while(it.hasNext()) {
String s = it.next(); System.out.println(s);
}
}
}
注意,这种迭代器方式获取集合中的每一个元素,是一种Collection集合及其子类型集合通用的方式
迭代器原理分析(补充内容,了解即可) :
java.lang.Iterable 接口中,定义了获取迭代器的方法:
public interface Iterable { Iterator iterator();
}
java.util.Collection 接口继承了 java.lang.Iterable 接口
public interface Collection extends Iterable {
//...
}
所以, Collection 接口及其子接口中,都有一个获取迭代器对象的方法:
Iterator iterator();
java.util.Iterator 接口中,主要定义俩个方法:
public interface Iterator {
//返回当前迭代器中是否还有下一个对象
boolean hasNext();
//获取迭代器中的下一个对象
Object next(); }
迭代器 实现原理:
- 获取迭代器对象:集合类实现了 Iterable 接口,该接口定义了一个iterator() 方法,用于获取迭代器对象。迭代器对象是实现了 Iterator接口的具体类的实例。
- 迭代器位置初始化:在创建迭代器对象时,迭代器的位置通常初始化为集合的起始位置。不同的集合实现可能对位置初始化进行不同的处理。
- 遍历集合元素:通过调用迭代器对象的 hasNext() 方法,可以判断集合中是否还有下一个元素。如果有下一个元素,可以通过 next() 方法获取下一个元素,并将迭代器的位置后移。
- 迭代器状态管理:迭代器对象会记录当前迭代的状态,包括当前位置、遍历过程中的操作等。这些状态可以帮助迭代器在遍历过程中正确地访问和操作集合的元素。
- 结束迭代:当集合中没有更多元素时,迭代器的 hasNext() 方法将返回false ,表示遍历结束。
迭代器 next 方法 示意图: