java 集合框架

集合框架的介绍

我们在超市买东西的时候,如果没有购物车是不是会很麻烦呢?Java 中集合类是一种工具类,就是像购物车一样的容器,存储任意数量的具有共同属性的对象。

我们为什么要用集合呢?一个类的内部有许多相同类型的属性,并且他们的作用与意义是一样的,我们最好用一个类似容器的东西去盛放他们,在类的内部就变得井然有序。所以集合便是在类的内部,对数据进行组织的作用。这样我们便可以简单而快速地搜索大量的条目。有的集合接口,提供了一系列排列有序的元素,并且可以在序列中快速地插入或者删除有关元素。还有一些集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型。

集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何集合框架都包含三大内容:对外的接口、接口的实现和对集合运算的算法。

Collection 接口

因为集合框架中的很多类功能是相似的,所以我们用接口来规范类。

Collection接口是java集合框架里的一个根接口。他也是List、Set和Queue 接口的父接口。Collection接口中定义了可用于操作List、Set和Queue的方法–增删改查

方法 返回值 说明
add(E e) boolean 向collection的尾部追加指定的元素(可选操作)
addAll(Collection<? extend E> c) boolean 将指定collection 中的所有元素都添加此collection中(可选操作)
clear() void 移除此collection中的所有元素(可选操作)
contains(Object o) boolean 如果此collection包含指定元素,则返回true
containsAll(Collection<?> c) boolean 如果此collection包含指定collection的所有元素,则返回true
equals(Object o) boolean 比较此collection与指定对象是否相等
hashCode() int 返回此collection的哈希码值
isEmpty() boolean 如果此collection不包含元素 则返回true
iterator() Iterator 返回在此collection的元素上进行迭代的迭代器
remove(Object o) boolean 移除此collection中出现的首个指定元素(可选操作)
removeAll(Collection<?> c) boolean 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)
retainAll(Collection<?> c) boolean 仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)
size() int 返回此collection中的元素数
toArray() Object[] 返回包含此collection中所有元素的数组
toArray(T[] a) T[] 返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同

List 接口 与 ArrayList 类

List是一个接口,不能实例化,需要一个具体类来实现实例化。List集合中的对象按照一定的顺序排放,里面的内容可以重复。List接口实现的类有:ArrayList(实现动态数组),Vector(实现动态数组), LinkedList(实现链表),Stack(实现堆栈).

List在Collection 基础上的方法

方法 返回值 说明
add(int index, E element) void 在列表的指定位置插入指定元素(可选操作)
addAll(int index, Collection<? extends E> c) boolean 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)
get(int index) E 返回列表中指定位置的元素
indexOf(Object o) int 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1
lastIndexOf(Object o) int 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1
listIterator() ListIterator 返回此列表元素的列表迭代器(按适当顺序)
listIterator(int index) ListIterator 返回此列表元素的列表迭代器(按适当顺序),从列表的指定位置开始
remove(int index) E 移除列表中指定位置的元素(可选操作)
set(int index, E element) E 用指定元素替换列表中指定位置的元素(可选操作)
subList(int fromIndex, int toIndex) List 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图

今天我们主要来学习  java.util.ArrayList,ArrayList  类实现一个可增长的动态数组,它可以存储不同类型的对象,而数组则只能存放特定数据类型的值。

我们通过实际的例子来学习 ArrayList 吧!学校的教务系统会对学生进行统一的管理,每一个学生都会有一个学号和学生姓名,我们在维护整个系统的时候,大多数操作是对学生的添加、插入、删除、修改等操作。

先创建一个学生类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* 学生类
*/

public class Student {
public String id;
public String name;
public Student(String id, String name){
this.id = id;
this.name = name;
}

@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
'}';
}
}

管理学生类:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import java.util.*;

public class ListTest {

//集合后面的<>代表泛型的意思
//泛型是规定了集合元素的类型
//我们以后会详细讲到
/**
* 用于存放学生的 List
*/
public List<Student> students;

public ListTest() {
this.students = new ArrayList<Student>();
}

/**
* 用于往 students 中添加学生
*/
public void testAdd() {
// 创建一个学生对象,并通过调用 add 方法,添加到学生管理 List 中
Student st1 = new Student("1", "张三");
students.add(st1);

// 取出 List 中的 Student 对象
Student temp = students.get(0);
System.out.println("添加了学生:" + temp.id + ":" + temp.name);

Student st2 = new Student("2", "李四");
students.add(0, st2);
Student temp2 = students.get(0);
System.out.println("添加了学生:" + temp2.id + ":" + temp2.name);

// 对象数组的形式添加
Student[] student = {new Student("3", "王五"), new Student("4", "马六")};

// Arrays 类包含用来操作数组(比如排序和搜索)的各种方法,asList() 方法用来返回一个受指定数组支持的固定大小的列表
students.addAll(Arrays.asList(student));
Student temp3 = students.get(2);
Student temp4 = students.get(3);
System.out.println("添加了学生:" + temp3.id + ":" + temp3.name);
System.out.println("添加了学生:" + temp4.id + ":" + temp4.name);
Student[] student2 = {new Student("5", "周七"), new Student("6", "赵八")};
students.addAll(2, Arrays.asList(student2));
Student temp5 = students.get(2);
Student temp6 = students.get(3);
System.out.println("添加了学生:" + temp5.id + ":" + temp5.name);
System.out.println("添加了学生:" + temp6.id + ":" + temp6.name);
}

/**
* 取得 List 中的元素的方法
*/
public void testGet() {
int size = students.size();
for (int i = 0; i < size; i++) {
Student st = students.get(i);
System.out.println("学生:" + st.id + ":" + st.name);

}
}

/**
* 通过迭代器来遍历
*/
// 迭代器的工作是遍历并选择序列中的对象,Java 中 Iterator 只能单向移动
public void testIterator() {
// 通过集合的 iterator 方法,取得迭代器实例
Iterator<Student> it = students.iterator();
System.out.println("有如下学生(通过迭代器访问):");
while (it.hasNext()) {

Student st = it.next();
System.out.println("学生" + st.id + ":" + st.name);
}
}

/**
* 通过 for each 方法访问集合元素
*
*/
public void testForEach() {
System.out.println("有如下学生(通过 for each):");
for (Student obj : students) {
Student st = obj;
System.out.println("学生:" + st.id + ":" + st.name);
}
//使用 java8 Steam 将学生排序后输出
students.stream()//创建 Stream
//通过学生 id 排序
.sorted(Comparator.comparing(x -> x.id))
//输出
.forEach(System.out::println);
}

/**
* 修改 List 中的元素
*
*/
public void testModify() {
students.set(4, new Student("3", "吴酒"));
}

/**
* 删除 List 中的元素
*
*/
public void testRemove() {
Student st = students.get(4);
System.out.println("我是学生:" + st.id + ":" + st.name + ",我即将被删除");
students.remove(st);
System.out.println("成功删除学生!");
testForEach();

}

public static void main(String[] args) {
ListTest lt = new ListTest();
lt.testAdd();
lt.testGet();
lt.testIterator();
lt.testModify();
lt.testForEach();
lt.testRemove();

}
}

上面的代码中,用到了 Arrays 类, Arrays 包含用来操作数组(比如排序和搜索)的各种方法,asList() 方法用来返回一个受指定数组支持的固定大小的列表。

List 有两种基本的类型,除了ArrayList外,还有LinkedList,LinkedList类用于创建链表数据结构,两者的对比如下:

  • ArrayList:它擅长于随机访问元素,但是插入和移除元素很慢
  • LinkedList: 它通过代价较低的在List中进行插入和删除操作,提供了优化的顺序访问,它在随机访问方面相对较慢,但是它的特性集较ArrayList更大。

Set接口 和 HashSet 类

Set接口也是Collection接口的子接口,它有一个很重要也是很常用的实现类————HashSet,Set是元素无序并且不包含重复元素的collection(List可以重复),被称为集。

HashSet由哈希表(实际上是一个HashMap实例)支持。它不保证set的迭代顺序;特别是它不保证该顺序恒久不变。

接下来我们同样通过代码的形式来详细看一看吧!

在上面我们实现了学生的管理,现在学生要做项目,每一个项目有一个组长,由组长来组织组员,我们便来实现项目组的管理

因为项目组的组长由一个老师担任,首先我们来创建一个 PD 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.HashSet;
import java.util.Set;
/*
* 项目组长类
*/
public class PD {

public String id;
public String name;
//集合后面的<>代表泛型的意思
//泛型是规定了集合元素的类型
//我们以后会详细讲到
public Set<Student> students;
public PD(String id, String name){
this.id = id;
this.name = name;
this.students = new HashSet<Student>();
}
}

接下来我们便创建一个 SetTest 类,用来管理项目成员

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class SetTest {

public List<Student> students;

public SetTest() {
students = new ArrayList<Student>();
}

/*
* 用于往 students 中添加学生
*/
public void testAdd() {
//创建一个学生对象,并通过调用 add 方法,添加到学生管理 List 中
Student st1 = new Student("1", "张三");
students.add(st1);

//添加到 List 中的类型均为 Object,所以取出时还需要强转

Student st2 = new Student("2","李四");
students.add(st2);

Student[] student = {new Student("3", "王五"),new Student("4", "马六")};
students.addAll(Arrays.asList(student));

Student[] student2 = {new Student("5", "周七"),new Student("6", "赵八")};
students.addAll(Arrays.asList(student2));

}

/**
* 通过 for each 方法访问集合元素
* @param args
*/
public void testForEach() {
System.out.println("有如下学生(通过 for each):");
for(Object obj:students){
Student st = (Student)obj;
System.out.println("学生:" + st.id + ":" + st.name);
}
}

public static void main(String[] args){
SetTest st = new SetTest();
st.testAdd();
st.testForEach();
PD pd = new PD("1","张老师");
System.out.println("请:" + pd.name + "选择小组成员!");
//创建一个 Scanner 对象,用来接收从键盘输入的学生 ID
Scanner console = new Scanner(System.in);

for(int i = 0;i < 3; i++){
System.out.println("请输入学生 ID");
String studentID = console.next();
for(Student s:st.students){
if(s.id.equals(studentID)){
pd.students.add(s);
}
}
}
st.testForEachForSer(pd);
// 关闭 Scanner 对象
console.close();
}
//打印输出,老师所选的学生!Set 里遍历元素只能用 foreach 和 iterator
//不能使用 get() 方法,因为它是无序的,不能想 List 一样查询具体索引的元素
public void testForEachForSer(PD pd){
for(Student s: pd.students) {
System.out.println("选择了学生:" + s.id + ":" + s.name);
}
}

}

HashMap类

HashMap 是基于哈希表的 Map 接口的一个重要实现类。HashMap 中的 Entry 对象是无序排列的,Key 值和 value 值都可以为 null,但是一个 HashMap 只能有一个 key 值为 null 的映射(key 值不可重复)。

下面我们通过代码来学习 Map 中的方法吧。都有过选课经历吧,我们就用 Map 来管理课程吧

创建一个Course类

1
2
3
4
5
6
7
8
public class Course {
public String id;
public String name;
public Course(String id, String name){
this.id = id;
this.name = name;
}
}

创建一个MapTest类:

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;

public class MapTest {

/**
* 用来承装课程类型对象
*/
public Map<String, Course> courses;

/**
* 在构造器中初始化 courses 属性
* @param args
*/
public MapTest() {
this.courses = new HashMap<String, Course>();
}

/**
* 测试添加:输入课程 ID,判断是否被占用
* 若未被占用,输入课程名称,创建新课程对象
* 并且添加到 courses 中
* @param args
*/
public void testPut() {
//创建一个 Scanner 对象,用来获取输入的课程 ID 和名称
Scanner console = new Scanner(System.in);

for(int i = 0; i < 3; i++) {
System.out.println("请输入课程 ID:");
String ID = console.next();
//判断该 ID 是否被占用
Course cr = courses.get(ID);
if(cr == null){
//提示输入课程名称
System.out.println("请输入课程名称:");
String name = console.next();
//创建新的课程对象
Course newCourse = new Course(ID,name);
//通过调用 courses 的 put 方法,添加 ID-课程映射
courses.put(ID, newCourse);
System.out.println("成功添加课程:" + courses.get(ID).name);
}
else {
System.out.println("该课程 ID 已被占用");
continue;
}
}
}

/**
* 测试 Map 的 keySet 方法
* @param args
*/

public void testKeySet() {
//通过 keySet 方法,返回 Map 中的所有键的 Set 集合
Set<String> keySet = courses.keySet();
//遍历 keySet,取得每一个键,在调用 get 方法取得每个键对应的 value
for(String crID: keySet) {
Course cr = courses.get(crID);
if(cr != null){
System.out.println("课程:" + cr.name);
}
}
}

/**
* 测试删除 Map 中的映射
* @param args
*/
public void testRemove() {
//获取从键盘输入的待删除课程 ID 字符串
Scanner console = new Scanner(System.in);
while(true){
//提示输出待删除的课程 ID
System.out.println("请输入要删除的课程 ID!");
String ID = console.next();
//判断该 ID 是否对应的课程对象
Course cr = courses.get(ID);
if(cr == null) {
//提示输入的 ID 并不存在
System.out.println("该 ID 不存在!");
continue;
}
courses.remove(ID);
System.out.println("成功删除课程" + cr.name);
break;
}
}

/**
* 通过 entrySet 方法来遍历 Map
* @param args
*/
public void testEntrySet() {
//通过 entrySet 方法,返回 Map 中的所有键值对
Set<Entry<String,Course>> entrySet = courses.entrySet();
for(Entry<String,Course> entry: entrySet) {
System.out.println("取得键:" + entry.getKey());
System.out.println("对应的值为:" + entry.getValue().name);
}
}

/**
* 利用 put 方法修改 Map 中的已有映射
* @param args
*/
public void testModify(){
//提示输入要修改的课程 ID
System.out.println("请输入要修改的课程 ID:");
//创建一个 Scanner 对象,去获取从键盘上输入的课程 ID 字符串
Scanner console = new Scanner(System.in);
while(true) {
//取得从键盘输入的课程 ID
String crID = console.next();
//从 courses 中查找该课程 ID 对应的对象
Course course = courses.get(crID);
if(course == null) {
System.out.println("该 ID 不存在!请重新输入!");
continue;
}
//提示当前对应的课程对象的名称
System.out.println("当前该课程 ID,所对应的课程为:" + course.name);
//提示输入新的课程名称,来修改已有的映射
System.out.println("请输入新的课程名称:");
String name = console.next();
Course newCourse = new Course(crID,name);
courses.put(crID, newCourse);
System.out.println("修改成功!");
break;
}
}

public static void main(String[] args) {
MapTest mt = new MapTest();
mt.testPut();
mt.testKeySet();
mt.testRemove();
mt.testModify();
mt.testEntrySet();

}
}

参考链接

在线文档-jdk-zh