Java 集合
1.引入:
- 集合是一个容器,是一个载体,可以依次容纳多个对象,数组就是一个集合;
- 集合是存储引用数据类型,不能存储基本数据类型,也不能直接存储java对象,存的是java对象的内存地址
- 集合本身就是一个引用对象,一个大的容器
- 在java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到不同的数据结构中;数据结构就是数据的存储结构,常见的数据结构有:数组,二叉树,链表,哈希表等;所以使用不同的集合就是使用了不同的数据结构
- java中已经将数据结构实现了,就是已经写好了的一些常用的集合类(集合实现类),要选择在什么情况下选择合适的集合去使用即可
- 所有的集合类(集合实现类)和 集合接口都在java.util 包下
7. 集合框架(集合继承结构):整个集合框架就围绕一组标准接口而设计
-
在java中的集合分为两大类:
一类是单个方式存储元素,这一类集合中的超级父接口:java.util.Collection
一类是以键值对的方式存储元素,这一类集合中的超级父接口:java.util.Map -
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
-
集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:
接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。
2. Collection 接口
- 超级父接口是Iterable接口:
Iterable意思是可迭代的,可遍历的,所有集合元素都是可迭代的,可遍历的
Iterable接口中有一个 Iterator() 方法,返回一个Iterator对象
Iterator是叫集合的迭代器对象,Collection 接口继承了Iterable接口,就可以直接调用这个方法,通过这个迭代器去迭代这个集合中的元素对象
迭代器对象Iterator接口,里面有方法:hasNext() next() remove()
Collection 接口和Iterator接口是关联关系(has a),和Iterable接口是泛化/继承关系(is a)
在UML图中,继承是空心三角形实线指向父,实现关系(like a)是空心三角形虚线指向父,关联关系是带箭头的实线
Collection 接口的子类型:List接口、Set 接口
- Collection 接口:
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素; Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。当然,它的子接口还有很多,只学习常用的
Collection 接口存储一组不唯一,无序的对象。
- List接口:有序可重复,元素有下标
List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
List 接口存储一组不唯一,有序(插入顺序)的对象。
有序:存进去的顺序和取出来的顺序相同,不是大小排序
可重复:已经有个1,还可以再放个1
- Set 接口:无序不可重复,元素没有下标
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
Set 接口存储一组唯一,无序的对象。
集合实现类(集合类):Java提供了一套实现了Collection接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
以下仅仅是常用的:
- 实现List接口 :ArrayList类,LinkedList类 ,Vector类
- 实现Set接口:HashSet类;SortedSet接口是Set接口的子接口,SortedSet接口的实现类:TreeSet类实现了Set接口;
(1)ArrayList类:底层采取了数组这种数据结构,实现了可变大小的数组,是非线程安全的
(2)LinkedList类 :底层采取了双向链表这种数据结构
(3)Vector类:底层采取了数组这种数据结构,是线程安全的,效率较低,使用较少
(1)HashSet类:底层采取了哈希表这种数据结构,实际上HashSet集合在new的时候,底层是new了一个HashMap集合,向HashSet集合存储元素,实际上是存储到HashMap集合中了
(2)SortedSet接口:由于继承了Set接口,其特点有无序不可重复,没有下标,但放在SortedSet集合中的元素可以自动排序,我们成为可排序集合
(3)SortedSet接口的实现类的TreeSet类:底层采取了二叉树这种数据结构,实际上TreeSet集合在new的时候,底层是new了一个TreeMap集合,向TreeSet集合存储元素,实际上是存储到TreeMap集合中了
3.Map集合(Map接口)
- Map接口和Collection 接口没有关系
- Map集合以key和value的这种键值对的方式存储元素,key和value都是存储java对象的内存地址,所有Map集合的key都是无序不可重复的,就是说Map集合的key和Set集合存储元素的特点相同,Map集合的key,就是一个Set集合,往Set集合中放数据,实际上放到Map集合的key部分
- 其实现类:
(1)HashMap 类
(2)Hashtable类,其子类是Properties - 子接口:
SortedMap接口:其实现类有TreeMap类
(1)HashMap 类:底层采取了哈希表这种数据结构,是非线程安全的
(2)Hashtable类:底层采取了哈希表这种数据结构,是线程安全的,使用较少
(3)Hashtable类的子类是Properties:底层采取了哈希表这种数据结构,表示一个持久的属性集,存储元素时,也是采取key和value的形式,属性列表中每个键及其对应值都是一个字符串。
(4)SortedMap接口:由于SortedMap集合是Map接口的子接口,key都是无序不可重复的,同时,放在SortedMap集合key部分的元素会自动按照大小顺序排序,成为可排序的集合
(5)SortedMap接口的实现类有TreeMap类:底层采取了二叉树这种数据结构,key部分的元素自然会自动按照大小顺序排序,
2. Collection 接口中常用的方法
- 在没有使用泛型之前,Collection可以存储Object的所有子类型,(但牢记集合中不能直接存储基本数据类型,也不存储java对象),使用泛型之后,Collection中只能存储某个具体的类型
2. Collection 接口里面都是抽象方法:
方法1. boolean add(Object e); 向集合中添加元素
方法2. int size(); 获取集合中元素的个数
方法3.void clear(); 清空集合
方法4. boolean contains(Object o); 判断集合中是否包含元素o,包含返回true
方法5. boolean remove(Object o); 删除集合中的元素o
方法6. boolean isEmpty(); 判断集合是否为空
方法7. Object[] toArray(); 将集合转换为数组
package 集合;
//都需要导入java.util包
import java.util.ArrayList;
import java.util.Collection;
public class Jihe01 {
public static void main(String[] args) {
//创建一个集合对象
//Collection c = new Collection();报错,因为接口是抽象的,不可以实例化
//多态
Collection c = new ArrayList();
// 方法1. boolean add(Object e); 向集合中添加元素
c.add(200);//自动装箱,实际上是放进去一个对象的内存地址,Integer x = new Integer(200); c.add(x);
c.add(3.14);//自动装箱,实际上是放进去一个对象的内存地址 Double x = new Double(3.14);c.add(x);
c.add(true);//自动装箱
// 方法2. int size(); 获取集合中元素的个数
System.out.println(c.size());//3
// 方法3.void clear(); 清空集合
c.clear();
System.out.println(c.size());//0
// 方法4. boolean contains(Object o); 判断集合中是否包含元素o,包含返回true
c.add("加油");
System.out.println(c.contains("加油"));//true
// 方法5. boolean remove(Object o); 删除集合中的元素o
c.remove("加油");
System.out.println(c.size());//0
// 方法6. boolean isEmpty(); 判断集合是否为空
System.out.println(c.isEmpty());//true
// 方法7. Object[] toArray(); 将集合转换为数组
c.add(200);
c.add(3.14);
c.add("加油");
c.add(new Object());
c.add(new Student());
Object[] obj = c.toArray();
for(int i=0;i<obj.length;i++) {
Object o = obj[i];
System.out.println(o);//直接输出一个引用,就是会自动调用引用.toString()方法
//因为此处的String类,Integer类都已经重写了toString()方法,而下面的Object和Student类没有重写,就还是实例化名加@加地址
}
/*遍历数组的结果:
*200
3.14
加油
java.lang.Object@4926097b
集合.Student@762efe5d */
}
}
class Student{
}
3.关于集合遍历或迭代:(以下讲解的遍历方式或迭代方式是所有的Collection通用的一种方式,在所有的Collection及其子类中使用,在Map中不能用)
单独拿出来讲“ Collection 接口调用 iterator()方法来获取一个迭代器对象Iterator”
package 集合;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Jihe02 {
public static void main(String[] args) {
//创建集合对象,用到多态
Collection c = new HashSet();
//添加元素
c.add(200);
c.add(3.14);
c.add("加油");
c.add(new Object());
// 对集合Collection进行迭代/遍历:
// 首先,获取集合对象的迭代器对象Iterator
// 第二步:通过迭代器对象Iterator中的方法完成对集合对象的遍历或迭代,
/*迭代器Iterator(Iterator接口)共有三种方法:
* 1. boolean hasNext(); 若仍有元素可以迭代,则返回true
* 2. Object next(); 返回迭代的下一个元素(就是指这个方法让迭代器前进一位,并且将指向的元素返回)
* 迭代器最初并没有指向第一个元素对象,先hasNext()判断有元素可以迭代吗?有,则调用next(),让迭代器前进一位
* 依次将元素将集合中的元素取出
* boolean hasNext = it.hasNext();
if(hasNext) {
Object obj = it.next();
System.out.println(obj);
}
应该使用循环来实现将集合中的元素依次取出*/
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
/*加油
3.14
200
java.lang.Object@7de26db8
*/
}
}
package 集合;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Jihe0202 {
public static void main(String[] args) {
//创建集合对象,用到多态
Collection c = new HashSet();
//添加元素
c.add(1);//自动装箱,就是Integer x = new Integer(1); c.add(x);
c.add(11);
c.add(111);
//获取迭代器,循环迭代
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
if(obj instanceof Integer) {
System.out.println("Integer型对象");
}
System.out.println(obj);
}
/*想说明:
* 在集合中传进去什么类型的数据,取出时仍然是什么类型的数据
* 上面是传进去了Integer对象,取出仍是Integer对象
* 只不过在输出时,有println(obj)方法:
* 源码:
* public void println(Object x) {
String s = String.valueOf(x);
}
将输出对象都转换为String类型的,同时,String类重写了toString()方法*/
}
/*
Integer型对象
1
Integer型对象
11
Integer型对象
111*/
}
当集合结构发生改变时(例如 增添或删除个别元素时),没有重新获取迭代器
package 集合;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Jihe02020 {
public static void main(String[] args) {
Collection c = new ArrayList();
// 如果在集合中是空时,就获取迭代器,添加元素后,
// 就是集合结构发生改变时,没有重新获取迭代器
// 出现异常:java.util.ConcurrentModificationException
// Iterator it = c.iterator();
//添加元素
c.add(1);
c.add(11);
c.add(111);
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
//删除元素后,重新获取迭代器
c.remove(11);
Iterator i = c.iterator();
while(i.hasNext()) {
Object obj = i.next();
System.out.println(obj);
}
//迭代器Iterator接口中的第三个方法:
/*default void remove() {
throw new UnsupportedOperationException("remove");
}
就是remove()方法,删除的是迭代器当前指向的元素*/
c.add(200);
Iterator j = c.iterator();
while(j.hasNext()) {
Object obj = j.next();
System.out.println(obj);
j.remove();
}
System.out.println(c.size());
}
/*
1
11
111
1
111
1
111
200
0
*/
}
4. Collection 接口里面的:contains方法 中底层会调用调用了equals方法进行比对,来判断判断集合中是否包含某个元素,所以存放在一个集合中的类型,一定要重写equals方法;同时,记住:String类和Integer类都已经重写了equals方法;
package 集合;
import java.util.ArrayList;
import java.util.Collection;
public class Jihe03 {
//深入Collection 集合中的contains方法:
// boolean contains(Object o); 判断集合中是否包含元素o,包含返回true
public static void main(String[] args) {
Collection c = new ArrayList();
String s1 = new String("abc");
c.add(s1);
String s2 = new String("abcd");
c.add(s2);
String s3 = new String("abc");
// s3并没有添加到集合c中
// 那c集合中是否包含元素s3呢?
System.out.println(c.contains(s3));//true
/*分析:
*关键看底层的源代码:
*在ArrayList中找到contains方法,
*public boolean contains(Object o) {
return indexOf(o) >= 0;
}
然后在indexOf(o)中:
if (o.equals(es[i])) {
return i;
}
调用了equals方法,
其中的o是s3,es[i]是s1
就是 s3.equals(s1)
分析:
在堆中中new出了两个String对象s1 和 s3 ,
s1 和 s3中都保存的是在方法区的常量池中的同一个字符串"abc"的内存地址,
向右赋值,给了栈中的两个变量 s1 和 s3,
此时的实例变量s1 和 s3 中保存的是 两个String对象s1 和 s3 在堆中的内存地址
然后,s1的内存地址送到集合 c 的ArrayList数组中,s3并没有添加到集合c中,
但因为底层的contains方法调用了equals方法,
因为String类的equals方法是重写了,比较的是两个元素的内容,
因为s1 和 s3保存的内容都是字符串"abc"
所以c.contains(s3) 就是相当于 s3 也在集合c中
* */
}
}
放在集合中的元素一定要重写equals方法
package 集合;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class Jihe0303 {
public static void main(String[] args) {
// 创建集合对象,用到多态
Collection c = new ArrayList();
// 创建用户对象
User u1 = new User("jack");
User u2 = new User("jack");
// 添加元素
c.add(u1);
// System.out.println(c.contains(u2));//false
/*
* contains方法 中底层会调用调用了equals方法进行比对,因为User类没有重写equals方法
* 所以此处调用Object类中的equals方法,它是用“==”,就是比较两个元素对象的内存地址 就是 u1.equals(u2) 是 false
*/
// 重写User类中的equals方法后
System.out.println(c.contains(u2));// true
}
}
class User {
public String name;
public User() {
}
public User(String name) {
this.name = name;
}
// 重写equals方法,就是姓名一样,就可以认为同一个用户
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof User)) {
return false;
}
if (obj == this) {
return true;
}
User s = (User) obj;
if (s.name.equals(this.name)) {
return true;
}
return false;
}
}
4. Collection 接口里面的:remove方法 ; 因为底层调用了equals方法
//创建集合对象
Collection cc = new ArrayList();
String s1 = new String("abc");
cc.add(s1);
String s2 = new String("abc");
cc.remove(s2);
System.out.println(cc.size());//0
/*分析:因为contains方法在确认元素属于集合时,底层调用了equals方法,所以s1和s2认为是一样的,
* 删除其中一个,就认为同时删除了
* */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现