Java集合框架

集合的概念

Collection接口

List接口与实现类

泛型和工具类

Set接口与实现类

Map接口与实现类

1 集合

一、概念

对象的容器,定义了对对象操作的常用方法。类似数组的功能。

二、与数组的区别

(1)数组长度固定,集合长度不固定

(2)数组可以存储基本类型和引用类型,集合只能存储引用类型

三、集合所在位置

java.util.*;

2 Collection体系

image-20211220112728731

3 Collection父接口

一、特点

部分有序、部分无序;部分可重复、部分不可重复。

二、方法

1
2
3
4
5
6
7
8
9
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() // 将此集合转换成数组


4 Collection使用(1)

一、注意

由于没有下标,所以Collection中的元素遍历需要用foreach或者迭代器Iterator

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
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo1 {
public static void main(String[] args) {
// 创建集合
Collection collection = new ArrayList();

// 添加元素
collection.add("苹果");
collection.add("西瓜");
collection.add("榴莲");
System.out.println("元素个数:" + collection.size());
System.out.println(collection);

// // 删除元素
// collection.remove("榴莲");
// collection.clear();
// System.out.println(collection);

// 使用 foreach
for (Object o : collection) {
System.out.println(o);
}

// 迭代器
Iterator it = collection.iterator();
while (it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
it.remove(); // 不能使用collection.remove()
}

// 判断
System.out.println(collection.contains("西瓜"));
System.out.println(collection.isEmpty());
}
}

另外,在迭代过程中,应使用迭代器的删除方法而不应使用collection的删除方法。

5 Collection使用(2)

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
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo2 {
public static void main(String[] args) {
// 新建 Collection 对象
Collection collection = new ArrayList();
Student s1 = new Student("张三", 20);
Student s2 = new Student("张无忌", 18);
Student s3 = new Student("王二", 22);

// 添加数据
collection.add(s1);
collection.add(s2);
collection.add(s3);
System.out.println("元素个数:" + collection.size());
System.out.println(collection.toString());
// 删除
collection.remove(s1);
System.out.println("删除之后:" + collection.size());
// 遍历
for (Object o : collection) {
Student s = (Student)o;
System.out.println(s.toString());
}
// 迭代器
Iterator it = collection.iterator();
while (it.hasNext()) {
Student s = (Student)it.next();
System.out.println(s.toString());
}
}
}
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
package collection;

public class Student {
private String name;
private int age;
public Student() {

}
public Student(String name, int age) {
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;
}

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

6 List接口

一、方法

1
2
3
4
void add(int index, Object o)  // 在index位置插入对象o
boolean addAll(int index, Collection c) // 将一个集合中的元素添加到此集合中的index位置
Object get(int index) // 返回集合中指定位置的元素
List subList(int fromIndex, int toIndex) // 返回fromIndex和toIndex之间的集合元素

7 List使用(1)

一、ListIteratorIterator区别

(1)ListIterator可以任意方向遍历,Iterator只能向后遍历

(2)ListIterator可以添加和修改元素,Iterator只能删除元素

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
/*
* @author Vict0r
*/

package collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

public class Demo3 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("苹果");
list.add("小米");
list.add(0, "华为");
System.out.println("元素个数:" + list.size());
System.out.println(list.toString());

// 删除元素
// list.remove(0);
System.out.println(list.toString());

// 遍历
for (int i = 0; i < list.size(); ++i) {
System.out.println(list.get(i));
}

// foreach
for (Object o : list) {
System.out.println(o);
}

// 迭代器
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

// 列表迭代器 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ListIterator.html
ListIterator lit = list.listIterator();
System.out.println("列表迭代器从前向后");
while (lit.hasNext()) {
System.out.println(lit.nextIndex() + ":" + lit.next());
}

System.out.println("列表迭代器从后向前");
while (lit.hasPrevious()) {
System.out.println(lit.previousIndex() + ":" + lit.previous());
}

// 判断
System.out.println(list.contains("苹果"));
System.out.println(list.isEmpty());

// 获取位置
System.out.println(list.indexOf("华为"));
}
}

8 List使用(2)

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
/*
* @author Vict0r
*/

package collection;

import java.util.ArrayList;
import java.util.List;

public class Demo4 {
public static void main(String[] args) {
// 创建集合
List list = new ArrayList();
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.add(60);
System.out.println("元素个数" + list.size());
System.out.println(list.toString());

// 删除元素
list.remove(0); // 根据下标删除元素
// list.remove((Object)20); // 根据对象删除
// list.remove(new Integer(20)); // 根据值删除
System.out.println("删除元素:" + list.size());
System.out.println(list.toString());

// sublist 返回子集合 左闭右开区间
List sublist = list.subList(1,3);
System.out.println(sublist);
}
}

9 List 实现类

一、ArrayList【重点】

(1)数组结构实现,查询快、增删慢

(2)jdk1.2版本加入,运行效率快、线程不安全

二、Vector

(1)数组结构实现,查询快、增删慢

(2)jdk1.0版本,运行效率慢、线程安全

三、LinkedList

(1)链表结构实现,增删快、查询慢

10 ArrayList使用

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
/*
* @author Vict0r
*/

import collection.Student;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

public class Demo5 {
public static void main(String[] args) {
// 创建集合
ArrayList arrayList = new ArrayList<>();
Student s1 = new Student("刘德华", 20);
Student s2 = new Student("郭富城", 22);
Student s3 = new Student("梁朝伟", 18);
// 添加元素
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println("元素个数" + arrayList.size());
System.out.println(arrayList.toString());
// 删除元素
// arrayList.remove(new Student("刘德华", 20));
// System.out.println("删除之后" + arrayList.size());

// 迭代器
Iterator it = arrayList.iterator();
while (it.hasNext()) {
Student s = (Student)it.next();
System.out.println(s.toString());
}

// 前向迭代
ListIterator lit = arrayList.listIterator();
while (lit.hasNext()) {
Student s = (Student)lit.next();
System.out.println(s.toString());
}
// 列表迭代器
while (lit.hasPrevious()) {
Student s = (Student)lit.previous();
System.out.println(s.toString());
}

// 判断
System.out.println(arrayList.contains(new Student("梁朝伟", 18)));
System.out.println(arrayList.isEmpty());

// 查找
System.out.println(arrayList.indexOf(new Student("梁朝伟", 18)));
}
}

一、重写equals

如上代码中删除元素的部分,由于会心创建对象,所以实际上并不会删除原有的元素,因为remove会通过Objectequals方法,比较当前元素和集合中元素地址是否一样,一样就删除。如果想要通过这种方式删除元素,则需要重写Student类中的equals方法。

  • 如何重写?

直接输入方法名称即可看到提示

image-20211220171406074

image-20211220171548198

一直next即可,自动填充重写的函数代码

image-20211220171625244

11 ArrayList源码分析

一、变量

(1)默认容量大小10

1
2
3
4
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

注意:如果没有向集合中添加任何元素,容量为0,源码的体现如下;添加任意元素后,容量为10,参考add源码部分

1
2
3
4
5
6
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

(2)存放元素的数组

1
2
3
4
5
6
7
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access

(3)实际的元素个数

1
2
3
4
5
6
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;

二、方法

(1)add()添加元素,是如何添加的?

1
2
3
4
5
6
7
8
9
10
11
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

size初始为0,然后再看看ensureCapacityInternal()

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
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 初始时两者皆为空,必成立
// minCapacity就是size+1=1,10和1比,10大,返回10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
// 经过calculateCapacity后函数变成ensureExplicitCapacity(10)
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0) // 10-0显然大于0
grow(minCapacity); // 数组扩容,grow(10)
}

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; // 0
int newCapacity = oldCapacity + (oldCapacity >> 1); // 0
if (newCapacity - minCapacity < 0) // 0-10<0
newCapacity = minCapacity; // 10给到newCapacity
if (newCapacity - MAX_ARRAY_SIZE > 0) // MAX_ARRAY_SIZE=Integer.MAX_VALUE-8必不执行
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); // Arrays.copyOf(0,10)
// 因此添加任意元素后,容量为10
}

三、ArrayList扩容

元素加满后,1.5倍扩容

12 Vector使用

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Vector.html

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
import java.util.Enumeration;
import java.util.Vector;

public class Demo1 {
public static void main(String[] args) {
// 创建集合
Vector vector = new Vector<>();
// 添加元素
vector.add("草莓");
vector.add("芒果");
vector.add("西瓜");
System.out.println("元素个数" + vector.size());
// 删除
// vector.remove(0);
// System.out.println(vector.toString());
// 遍历
// for (Object o : vector) {
// System.out.println(o);
// }

// 使用枚举器
Enumeration en = vector.elements();
while (en.hasMoreElements()) {
Object o = en.nextElement();
System.out.println(o);
}

// 判断
System.out.println(vector.contains("西瓜"));
System.out.println(vector.isEmpty());

// 其他方法
System.out.println(vector.get(0));
System.out.println(vector.lastElement());
}
}

13 LinkedList使用

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
import collection.Student;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;

public class Demo2 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList<>();
Student s1 = new Student("刘德华", 20);
Student s2 = new Student("郭富城", 22);
Student s3 = new Student("梁朝伟", 18);

// 添加元素
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
System.out.println("元素个数:" + linkedList.size());
System.out.println(linkedList.toString());
// 删除
// linkedList.remove(0);
// linkedList.remove(s1);
// System.out.println(linkedList.toString());

// 遍历
for (int i = 0; i < linkedList.size(); ++i) {
System.out.println("普通for:" + linkedList.get(i));
}
for (Object o : linkedList) {
System.out.println("foreach:" + o);
}
// 迭代器
Iterator it = linkedList.iterator();
while (it.hasNext()) {
System.out.println("迭代器:" + it.next().toString());
}
// 列表迭代器
ListIterator lit = linkedList.listIterator();
while (lit.hasNext()) {
System.out.println("列表迭代器:" + lit.next().toString());
}
// 判断
System.out.println(linkedList.contains(s1));
System.out.println(linkedList.isEmpty());
//
}
}

14 LinkedList源码分析

(1)add方法

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}

主要通过linLast实现,所以看看linkLast,就是在尾部添加元素

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
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}

// 因为用到 Node,所以也看下Node
private static class Node<E> {
E item; // 当前元素,实际数据
Node<E> next; // 下一个节点
Node<E> prev; // 前一个节点

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

15 ArrayList与LinkedList区别

本质就是数组和链表的区别。

数组:必须开辟连续空间,查询快,增删慢。

链表:不用开辟连续空间,查询慢,增删快。

16 泛型概述

有点类似于C++的模板

一、概念

Java泛型是JDK1.5中引入的新特性,其本质是参数化类型,把类型作为参数传递。

二、常见形式

泛型类、泛型接口、泛型方法。

三、语法

<T,...> T表示类型占位符,表示一种引用类型

四、好处

(1)提高代码重用性

(2)防止类型转换异常,提高代码安全性

17 泛型类

一、编写泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 语法:类名<T>
* T是类型占位符,表示一种引用类型(不能是基本类型),如果编写多个,可使用逗号隔开
*/

public class MyGeneric<T> {
// 创建变量
T t;

// 泛型作为方法的参数
public void show(T t) {
// T t1 = new T();
System.out.println(t);
}

// 泛型作为返回值
public T getT() {
return t;
}
}

二、注意

使用泛型类时,可以创建变量,但不能进行实例化。

原因:T 代表一种数据类型,但这种类型是不确定的,那它的构造方法就不一定能用,比如万一它的构造方法是私有的。

三、使用泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestGeneric {
public static void main(String[] args) {
// 使用泛型类创建对象
MyGeneric<String> myGeneric = new MyGeneric<>(); // 后面尖括号中的String可写可不写
myGeneric.t = "hello";
myGeneric.show("大家好");
System.out.println(myGeneric.getT());

MyGeneric<Integer> myGeneric1 = new MyGeneric<>();
myGeneric1.t = 100;
myGeneric1.show(200);
Integer integer = myGeneric1.getT();
}
}

18 泛型接口

一、new一个interface

新家接口文件的方法

image-20211223163014162

image-20211223163026794

二、编写接口

1
2
3
4
5
6
7
8
9
10
11
/**
* 语法:接口名<T>
* 接口里可以包含抽象方法和静态常量
*/

public interface MyInterface<T> {
String name = "张三";

// 添加一个抽象方法
T server(T t);
}

三、注意

不能创建泛型静态常量,因为静态常量要求声明时进行初始化,但是又不知道T的类型,因此无法给它初始化。

四、接口的使用

A. 实现接口时明确类型

(1)说明

由于接口是不能实例化的,所以我们要添加一个实现类。在写实现类的时候给明确的引用类型。然后实现即可。

image-20211223163851262

image-20211223164032828

image-20211223164148318

(2)编写

1
2
3
4
5
6
7
8
public class MyInterfaceImpl implements MyInterface<String> {

@Override
public String server(String s) {
System.out.println(s);
return s;
}
}

(3)使用接口

1
2
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.server("xxxxxx");
B. 在实现时不确定的类型

(1)编写

1
2
3
4
5
6
7
public class MyInterfaceImpl2<T> implements MyInterface<T> {
@Override
public T server(T t) {
System.out.println(t);
return t;
}
}

(2)使用

1
2
MyInterfaceImpl2<Integer> impl2 = new MyInterfaceImpl2<>();
impl2.server(100);

19 泛型方法

参考《Java核心技术卷I》P313

泛型方法可以定义在普通类中,也可以定义在泛型类中。当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型。

注意:大多数情况,不标记具体类型,编译器也可以做出正确的推断,但是最好还是带上,避免不必要的麻烦。

(1)定义一个带有类型参数的简单泛型方法

1
2
3
4
5
6
7
8
9
10
/**
* 语法:<T> 返回值类型
*/

public class MyGenericMethod {
// 泛型方法
public <T> T show(T t) {
return t;
}
}

(2)使用

1
2
3
MyGenericMethod myGenericMethod = new MyGenericMethod();
String m = myGenericMethod.<String>show("azhe");
System.out.println(m);

20 泛型集合

一、概念

参数化类型、类型安全的集合,强制集合元素的类型必须一致。

二、 特点

(1)编译时即可检查,而非运行时抛出异常

(2)访问时,不必类型转换(拆箱)

(3)不同泛型之间引用不能相互赋值,泛型不存在多态

三、关于类型转换的问题说明

样例程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;

public class Demo3 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList<>();
arrayList.add("xxxx");
arrayList.add("xxxx");
arrayList.add(100);
arrayList.add(200);
for (Object o : arrayList) {
System.out.println(o);
}
}
}

在上述代码中,虽然我们往arrayList中存入两种类型的数据,但是在遍历时因为用的Object,所以一切正常。但是如果遍历时我们定义一个String临时变量存放元素数据,就会产生异常了。

image-20211224153818333

泛型集合就是可以解决这种问题。

四、泛型集合在创建集合对象时指定类型

在创建集合对象时指定类型,比如这里给的String,如果我们添加非String类型的元素就会报错了。

另外,也可以注意到,我们在遍历时可以直接用具体类型String而不再是Object,当然用Object遍历也是可以的。

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.ArrayList;

public class Demo3 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>(); // 在创建集合对象时指定类型
arrayList.add("xxxx");
arrayList.add("xxxx");

for (String s : arrayList) {
System.out.println(s);
}
}
}

21 Set集合

Set方法和Collection一模一样

22 Set接口使用

一、HashSet

(1)基于HashCode实现元素不重复

(2)当存入元素的HashCode相同,会调用equals确认,若结果为true,则拒绝后者存入

二、TreeSet(红黑树结构)

(1)基于排列顺序实现元素不重复

(2)实现了SortedSet接口,对集合元素自动排序

(3)元素对象的类型必须实现Comparable接口,指定排序规则

(4)通过CompareTo方法确定是否为重复元素

三、使用示例

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
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Demo1 {
public static void main(String[] args) {
// 创建集合
Set<String> set = new HashSet<>();
// 添加数据
set.add("苹果");
set.add("华为");
set.add("小米");
System.out.println("数据个数:" + set.size());
System.out.println(set.toString());
// 删除数据
// set.remove("小米");
// System.out.println(set.toString());
// 遍历
System.out.println("——————————foreach——————————");
for (String s : set) {
System.out.println(s);
}
// 迭代器遍历
System.out.println("——————————迭代器——————————");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 判断
System.out.println(set.contains("华为"));
System.out.println(set.isEmpty());

}
}

23 HashSet实现类的使用(1)

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
import java.util.HashSet;
import java.util.Iterator;

public class Demo2 {
public static void main(String[] args) {
// 新建集合
HashSet<String> hashSet = new HashSet<String>();
// 添加数据
hashSet.add("周杰伦");
hashSet.add("张国荣");
hashSet.add("张学友");
hashSet.add("胡歌");
System.out.println("数据个数" + hashSet.size());
System.out.println(hashSet.toString());
// 删除数据
hashSet.remove("胡歌");
System.out.println(hashSet.toString());
// foreach
System.out.println("-----foreach-----");
for (String s : hashSet) {
System.out.println(s);
}
// 迭代器
System.out.println("-----迭代器-----");
Iterator<String> it = hashSet.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 判断
System.out.println(hashSet.contains("周杰伦"));
System.out.println(hashSet.isEmpty());
}
}

24 HashSet实现类的使用(2)

一、Person类

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
public class Person {
private String name;
private int age;

public Person() {
}

public Person(String name, int age) {
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;
}

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

二、HashSet使用

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
/**
* 存储结构:Hash表(数组+链表+红黑树)
*/

import java.util.HashSet;

public class Demo3 {
public static void main(String[] args) {
// 创建结合
HashSet<Person> persons = new HashSet<>();
// 添加数据
Person p1 = new Person("人1",18);
Person p2 = new Person("人2",29);
Person p3 = new Person("人3",20);
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(new Person("人2", 29));
System.out.println("元素个数:" + persons.size());
System.out.println(persons.toString());
// 删除操作
persons.remove(p1);
System.out.println(persons.toString());
// 遍历
System.out.println("-----foreach-----");
for (Person p : persons) {
System.out.println(p.toString());
}
// 迭代器
System.out.println("-----迭代器-----");
Iterator<Person> it = persons.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 判断
System.out.println(persons.contains(p1));
System.out.println(persons.isEmpty());
}
}

一、注意

这里的persons.add(new Person("人2", 29));是可以成功添加元素的。因为这里new出来的对象和p2的地址是不一样的,如果添加p2就添加不进去。如果想实现名字和年龄相同时不允许加入,则需要重写hashCode()。这里需要了解HashSet的存储方式。

25 HashSet存储方式

一、存储结构

Hash表(数组+链表+红黑树)

二、存储过程

(1)根据hashcode,计算保存的位置。如果此位置为空,直接保存;不为空,则(2)

(2)再执行equals,如果equals为true,则认为重复;否则,形成链表

三、重写hashCode()

在Person类中,重写hashCode,方法同前文提到的重写equals

image-20211228153257768

自动生成,无需修改。

26 HashSet补充

一、重写的hashCode中,这个31是什么?

image-20211228153921624

(1)31是一个质数,用质数做运算,可以尽可能减少散列冲突。(也就是说通过hashCode计算出来的位置尽量不一样)

(2)31*i=(i<<5)-i,乘法操作可以换成位运算,提高执行的效率。

27 TreeSet使用

一、String类型

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
import java.util.Iterator;
import java.util.TreeSet;

public class Demo4 {
public static void main(String[] args) {
// 创建集合
TreeSet<String> treeSet = new TreeSet<>();
// 添加元素
treeSet.add("baa");
treeSet.add("aaa");
treeSet.add("ccc");
System.out.println(treeSet.size());
System.out.println(treeSet.toString());
// 删除元素
// treeSet.remove("ccc");
// System.out.println(treeSet.toString());
// foreach
for (String s : treeSet) {
System.out.println(s);
}
// 迭代器
Iterator<String> it = treeSet.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 判断
System.out.println(treeSet.contains("aaa"));
System.out.println(treeSet.isEmpty());
}
}

二、普通类

当存储的是Person类对象时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.TreeSet;

public class Demo5 {
public static void main(String[] args) {
TreeSet<Person> persons = new TreeSet<>();
Person p1 = new Person("人1",18);
Person p2 = new Person("人2",29);
Person p3 = new Person("人3",20);
persons.add(p1);
persons.add(p2);
persons.add(p3);
System.out.println(persons.size());
}
}

上述代码就会报错

image-20211228155936417

原因是:元素对象的类型必须实现Comparable接口,指定排序规则。否则程序无法确定需要比较的是什么。

因此需要改一下Person类。

image-20211228160345921

然后根据提示,实现CompareTo方法

image-20211228160416402

image-20211228160457829

添加逻辑,完善后的CompareTo方法如下:

1
2
3
4
5
6
@Override
public int compareTo(Person o) {
int n1 = this.name.compareTo(o.getName()); // 比姓名,这里的compareTo方法是字符串自带的方法
int n2 = this.age - o.getAge();
return n1 == 0 ? n2 : n1;
}

使用

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
/*
* @author Vict0r
*/

package chapter3;

import java.util.Iterator;
import java.util.TreeSet;

public class Demo5 {
public static void main(String[] args) {
// 创建集合
TreeSet<Person> persons = new TreeSet<>();
Person p1 = new Person("人1",18);
Person p2 = new Person("人2",29);
Person p3 = new Person("人3",20);
// 添加
persons.add(p1);
persons.add(p2);
persons.add(p3);
System.out.println(persons.size());
System.out.println(persons.toString());
// 删除
// persons.remove(p2);
// System.out.println(persons.toString());
// foreach
for (Person p : persons) {
System.out.println(p.toString());
}
// 迭代器
Iterator<Person> it = persons.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 判断
System.out.println(persons.contains(p1));
System.out.println(persons.isEmpty());
}
}

28 Comparator接口

实现定制比较

image-20211228163123323

image-20211228163137881

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
/*
* @author Vict0r
*/

package chapter3;

import java.util.Comparator;
import java.util.TreeSet;

public class Demo6 {
public static void main(String[] args) {
// 创建集合并指定比较规则
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int n1 = o1.getAge() - o2.getAge();
int n2 = o1.getName().compareTo(o2.getName()); // 比姓名,这里的compareTo方法是字符串自带的方法
return n1 == 0 ? n2 : n1;
}
});

Person p1 = new Person("人1",18);
Person p2 = new Person("人2",29);
Person p3 = new Person("人3",20);

persons.add(p1);
persons.add(p2);
persons.add(p3);
System.out.println(persons.toString());
}
}

29 TreeSet案例

使用TreeSet实现字符串按长度排序。(首先需要定制Comparable接口)

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
import java.util.Comparator;
import java.util.TreeSet;

public class Demo7 {
public static void main(String[] args) {
// 创建集合并指定规则
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length() - o2.length(); // n1==0则长度一样
int n2 = o1.compareTo(o2);
return n1 == 0 ? n2 : n1;
}
});
// 添加数据
treeSet.add("helloworld");
treeSet.add("pingguo");
treeSet.add("zhangsan");
treeSet.add("lisi");
treeSet.add("cat");
treeSet.add("beijing");
treeSet.add("nanjing");
treeSet.add("xian");
System.out.println(treeSet.toString());
}
}

30 Map集合概述

一、特点

(1)存键值对

(2)键值均无序无下标,键不允许重复,值允许重复。

31 Map接口使用

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
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo1 {
public static void main(String[] args) {
// 创建集合
Map<String, String> map = new HashMap<>();
// 添加元素
map.put("cn", "中国");
map.put("uk", "英国");
map.put("usa", "美国");
System.out.println("元素个数" + map.size());
System.out.println(map.toString());
// // 移除元素
// map.remove("usa");
// System.out.println(map.toString());
// 使用keySet遍历
for (String key : map.keySet()) {
System.out.println(key + "-------" + map.get(key));
}
// 使用entrySet()遍历
// Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String,String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "-----" + entry.getValue());
}
// 判断
System.out.println(map.containsKey("cn"));
System.out.println(map.containsValue("南朝鲜"));
}
}

32 HashMap使用(1)

jdk1.2后加入,线程不安全,运行效率快,允许null作为键或值

HashMap (Java SE 11 & JDK 11 ) (oracle.com)

image-20220117114006896

构造函数这里的加载因子0.75是指:比如容量为100时,当元素个数达到75就扩容。

一、案例

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
import java.util.HashMap;
import java.util.Map;

public class Demo2 {
public static void main(String[] args) {
// 创建集合
HashMap<Student, String> students = new HashMap<Student, String>();
// 添加元素
Student s1 = new Student("学生1", 123);
Student s2 = new Student("学生2", 111);
Student s3 = new Student("学生3", 546);
students.put(s1, "地址1");
students.put(s2, "地址2");
students.put(s3, "地址3");
students.put(new Student("学生3", 546), "地址3"); // 重写hashcode和equals后不会再新加入集合
System.out.println(students.size());
System.out.println(students);
// // 删除元素
// students.remove(s1);
// System.out.println(students.size());
// keySet遍历
for (Student key : students.keySet()) {
System.out.println(key.toString() + "-----" + students.get(key));
}
// entrySet遍历
for (Map.Entry<Student, String> entry : students.entrySet()) {
System.out.println(entry.getKey() + "-----" + entry.getValue());
}
// 判断
System.out.println(students.containsKey(s1));
System.out.println(students.containsValue("地址5"));
}
}
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
import java.util.Objects;

public class Student {
private String name;
private int stuNo;

public Student() {
}

public Student(String name, int stuNo) {
this.name = name;
this.stuNo = stuNo;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getStuNo() {
return stuNo;
}

public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return stuNo == student.stuNo && Objects.equals(name, student.name);
}

@Override
public int hashCode() {
return Objects.hash(name, stuNo);
}

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

33 Hashtable

Jdk1.0版本,线程安全,运行效率慢,不允许使用null作为键值

Properties

Hashtable的子类,要求键值均为String。通常用于配置文件的读取

34 TreeMap

实现了SortedMap接口(是Map的子接口),可以对key自动排序

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
package chapter12_4;

import java.util.Objects;

public class Student implements Comparable<Student>{
private String name;
private int stuNo;

public Student() {
}

public Student(String name, int stuNo) {
this.name = name;
this.stuNo = stuNo;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getStuNo() {
return stuNo;
}

public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return stuNo == student.stuNo && Objects.equals(name, student.name);
}

@Override
public int hashCode() {
return Objects.hash(name, stuNo);
}

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

@Override
public int compareTo(Student o) {
int n2 = this.stuNo - o.stuNo; // 根据学号从小到大排
return n2;
}
}
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
package chapter12_4;

import java.util.Map;
import java.util.TreeMap;

public class Demo3 {
public static void main(String[] args) {
// 新建集合
TreeMap<Student, String> treeMap = new TreeMap<>();
// 添加元素
Student s1 = new Student("学生1", 123);
Student s2 = new Student("学生2", 111);
Student s3 = new Student("学生3", 546);
treeMap.put(s1, "地址1");
treeMap.put(s2, "地址3");
treeMap.put(s3, "地址2");
System.out.println(treeMap.size());
System.out.println(treeMap.toString());
// // 删除
// treeMap.remove(new Student("学生2",111));
// System.out.println(treeMap.toString());
// keySet遍历
for (Student key : treeMap.keySet()) {
System.out.println(key.toString() + "----" + treeMap.get(key));
}
// entrySet遍历
for (Map.Entry<Student, String> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + "----" + entry.getValue());
}
// 判断
System.out.println(treeMap.containsKey(new Student("学生1", 123)));
}
}

35 Collections工具类



----------- 本文结束 -----------




0%