java集合框架
集合概述
-
概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。
-
和数组的区别:
-
数组长度固定,集合长度不固定。
-
数组可以存储基本类型和引用类型,集合只能存储引用类型。
- 位置: java.util.*;
Collection体系集合
Collection父接口
-
特点:代表一组任意类型的对象,无序、无下标、不能重复。
-
方法:
boolean add(Object obj) //添加一个对象。
boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
void clear() //清空此集合中的所有对象。
boolean contains(Object o) //检查此集合中是否包含o对象。
boolean equals(Object o) //比较此集合是否与指定对象相等。
boolean isEmpty() //判断此集合是否为空。
boolean remove(Object o) //在此集合中移除o对象。
int size() //返回此集合中的元素个数。
Object[] toArray() //姜此集合转换成数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42/**
* Collection接口的使用(一)
* 1.添加元素
* 2.删除元素
* 3.遍历元素
* 4.判断
*/
public class Demo1{
pubic static void main(String[] args){
//创建集合
Collection collection=new ArrayList();
// * 1.添加元素
Collection.add("苹果");
Collection.add("西瓜");
Collection.add("榴莲");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
// * 2.删除元素
collection.remove("榴莲");
System.out.println("删除之后:"+collection.size());
// * 3.遍历元素
//3.1 使用增强for
for(Object object : collection){
System.out.println(object);
}
//3.2 使用迭代器(迭代器专门用来遍历集合的一种方式)
//hasnext();判断是否有下一个元素
//next();获取下一个元素
//remove();删除当前元素
Iterator iterator=collection.Itertor();
while(iterator.hasnext()){
String object=(String)iterator.next();
System.out.println(s);
//删除操作
//collection.remove(s);引发错误:并发修改异常
//iterator.remove();应使用迭代器的方法
// * 4.判断
System.out.println(collection.contains("西瓜"));//true
System.out.println(collection.isEmpty());//false
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39/**
* Collection接口的使用(二)
* 1.添加元素
* 2.删除元素
* 3.遍历元素
* 4.判断
*/
public class Demo2 {
public static void main(String[] args) {
Collection collection=new ArrayList();
Student s1=new Student("张三",18);
Student s2=new Student("李四", 20);
Student s3=new Student("王五", 19);
//1.添加数据
collection.add(s1);
collection.add(s2);
collection.add(s3);
//collection.add(s3);可重复添加相同对象
System.out.println("元素个数:"+collection.size());
System.out.println(collection.toString());
//2.删除数据
collection.remove(s1);
System.out.println("删除之后:"+collection.size());
//3.遍历数据
//3.1 增强for
for(Object object:collection) {
Student student=(Student) object;
System.out.println(student.toString());
}
//3.2迭代器
//迭代过程中不能使用collection的删除方法
Iterator iterator=collection.iterator();
while (iterator.hasNext()) {
Student student=(Student) iterator.next();
System.out.println(student.toString());
}
//4.判断和上一块代码类似。
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28/**
* 学生类
*/
public class Student {
private String name;
private int age;
public Student(String name, int age) {
super();
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;
}
Collection子接口
List集合
-
特点:有序、有下标、元素可以重复。
-
方法:
void add(int index,Object o) //在index位置插入对象o。
boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
Object get(int index) //返回集合中指定位置的元素。
List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55/**
* List子接口的使用(一)
* 特点:1.有序有下标 2.可以重复
*
* 1.添加元素
* 2.删除元素
* 3.遍历元素
* 4.判断
* 5.获取位置
*/
public class Demo3 {
public static void main(String[] args) {
List list=new ArrayList<>();
//1.添加元素
list.add("tang");
list.add("he");
list.add(0,"yu");//插入操作
System.out.println("元素个数:"+list.size());
System.out.println(list.toString());
//2.删除元素
list.remove(0);
//list.remove("yu");结果同上
System.out.println("删除之后:"+list.size());
System.out.println(list.toString());
//3.遍历元素
//3.1 使用for遍历
for(int i=0;i<list.size();++i) {
System.out.println(list.get(i));
}
//3.2 使用增强for
for(Object object:list) {
System.out.println(object);
}
//3.3 使用迭代器
Iterator iterator=list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//3.4使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。
ListIterator listIterator=list.listIterator();
//从前往后
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
//从后往前(此时“遍历指针”已经指向末尾)
while(listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
//4.判断
System.out.println(list.isEmpty());
System.out.println(list.contains("tang"));
//5.获取位置
System.out.println(list.indexOf("tang"));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31/**
* List子接口的使用(二)
* 1.添加元素
* 2.删除元素
* 3.遍历元素
* 4.判断
* 5.获取位置
*/
public class Demo4 {
public static void main(String[] args) {
List list=new ArrayList();
//1.添加数字数据(自动装箱)
list.add(20);
list.add(30);
list.add(40);
list.add(50);
System.out.println("元素个数:"+list.size());
System.out.println(list.toString());
//2.删除元素
list.remove(0);
//list.remove(20);很明显数组越界错误,改成如下
//list.remove(Object(20));
//list.remove(new Integer(20));
System.out.println("元素个数:"+list.size());
System.out.println(list.toString());
//3-5不再演示,与之前类似
//6.补充方法subList,返回子集合,含头不含尾
List list2=list.subList(1, 3);
System.out.println(list2.toString());
}
}
List实现类
ArrayList【重点】
- 数组结构实现,查询块、增删慢;
- JDK1.2版本,运行效率快、线程不安全。
1
|
/**
|
注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:
1
|
|
ArrayList源码分析
-
默认容量大小:
private static final int DEFAULT_CAPACITY = 10;
-
存放元素的数组:
transient Object[] elementData;
-
实际元素个数:
private int size;
-
创建对象时调用的无参构造函数:
1
2
3
4
5//这是一个空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?
这就得看看add方法的源码了:
1
2
3
4
5public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到
ensureCapacityInternal(size + 1);
该方法源码如下:1
2
3private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}该方法中的参数minCapacity传入的值为size+1也就是 1,接着我们再进入到
calculateCapacity(elementData, minCapacity)
里面:1
2
3
4
5
6private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}上文说过,elementData就是存放元素的数组,当前容量为0,if条件成立,返回默认容量
DEFAULT_CAPACITY
也就是10。这个值作为参数又传入ensureExplicitCapacity()
方法中,进入该方法查看源码:1
2
3
4
5
6private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}我们先不要管modCount这个变量。因为elementData数组长度为0,所以if条件成立,调用grow方法,重要的部分来了,我们再次进入到grow方法的源码中:
1
2
3
4
5
6
7
8
9
10
11private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为
oldCapacity+一个增量
,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:1
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
这个值太大了以至于第二个if条件没有了解的必要。
最后一句话就是为elementData数组赋予了新的长度,
Arrays.copyOf()
方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()
的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。这时候再回到add的方法中,接着就向下执行
elementData[size++] = e;
到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。
Vector
-
数组结构实现,查询快、增删慢;
-
JDK1.0版本,运行效率慢、线程安全。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34/**
* Vector的演示使用
*
*1.添加数据
*2.删除数据
*3.遍历
*4.判断
*/public class Demo06 {
public static void main(String[] args) {
Vector vector = new Vector();
//1.添加元素
vector.add("苹果");
vector.add("草莓");
vector.add("西瓜");
//2。遍历
//for 或 增强for都行
//使用for
System.out.println("------使用for------");
for (int i = 0; i <vector.size() ; i++) {
System.out.println(vector.get(i));
}
//增强for
System.out.println("------增强for------");
for (Object o : vector) {
System.out.println(o);
}
//迭代器
System.out.println("------迭代器------");
Iterator i = vector.iterator();
while(i.hasNext()){
System.out.println(i.next());
}
//枚举器
System.out.println("------枚举器------");
Enumeration en = vector.elements();
//3.判断
vector.contains("苹果");
//4.其他方法
System.out.println(vector.firstElement());
//.....
}
}
LinkedList
- 链表结构实现,增删快,查询慢。
1
|
/**
|
LinkedList源码分析
LinkedList首先有三个属性:
- 链表大小:
transient int size = 0;
- (指向)第一个结点/头结点:
transient Node<E> first;
- (指向)最后一个结点/尾结点:
transient Node<E> last;
关于Node类型我们再进入到类里看看:
1
|
private static class Node<E> {
|
首先item存放的是实际数据;next指向下一个结点而prev指向上一个结点。
Node带参构造方法的三个参数分别是前一个结点、存储的数据、后一个结点,调用这个构造方法时将它们赋值给当前对象。
LinkedList是如何添加元素的呢?先看看add方法:
1
|
public boolean add(E e) {
|
进入到linkLast方法:
1
|
void linkLast(E e) {
|
假设刚开始new了一个LinkedList对象,first和last属性都为空,调用add进入到linkLast方法。
首先创建一个Node变量 l 将last(此时为空)赋给它,然后new一个newNode变量存储数据,并且它的前驱指向l,后继指向null;再把last指向newNode。如下图所示:
如果满足if条件,说明这是添加的第一个结点,将first指向newNode:
至此,LinkedList对象的第一个数据添加完毕。假设需要再添加一个数据,我们可以再来走一遍,过程同上不再赘述,图示如下:
ArrayList和LinkedList区别
- ArrayList:必须开辟连续空间,查询快,增删慢。
- LinkedList:无需开辟连续空间,查询慢,增删快。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现