集合
文章目录
前言
早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合
一、认识集合框架
1.1 区分集合与数组存储
集合的特点:
1、提供一种存储空间可变的存储模型, 存储的数据容量可以发生改变
数组存储的特点:
①数组初始化以后,长度就确定了
②数组声明类型,就决定了进行元素初始化的类型
数组存储的弊端:
①一旦初始化以后,长度就不可以改变
②数组存储都是有序,可重复的,对于我们的无序,不可以重复的需求不够友好
③获取数组中实际元素的个数需求,数组没有现成的属性或方法使用
④提供的方法使用有限
集合存储的优点:
解决数组存储数据方面的弊端
1.2 Java集合中的两种体系
从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等
二、ArrayList集合
1、ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制
2、ArrayList 继承了 AbstractList ,并实现了 List 接口
3、.ArrayList 类位于 java.util 包中,使用前需要引入它
4、ArrayList底层基于数组实现
ArrayList<E> objectName =new ArrayList<>();
常用方法
方法名称 | 说明 |
---|---|
public boolean add(E e) | 将元素插入到指定位置的 arraylist 中 |
public E remove(int index) | 删除 arraylist 里的单个元素 |
public E set(int index, E element) | 替换 arraylist 中指定索引的元素 |
public E get(int index) | 通过索引值获取 arraylist 中的元素 |
public int size() | 返回 arraylist 里元素数量 |
List<String> arrayList = new ArrayList<String>();
//arrayList 对象名称
arrayList.add("apple");
arrayList.add("banana");
arrayList.add("pear");
arrayList.add("watermelon");
System.out.println("集合中存入的元素有" + arrayList.size() + "个");
arrayList.remove(1);
arrayList.set(0,"peach");
//根据index 获取元素
System.out.println(arrayList.get(0)); //apple
System.out.println(arrayList.get(1)); //banana
System.out.println(arrayList.get(2)); //pear
System.out.println(arrayList.get(3)); //watermelon
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
练习:
创建一个存储学生对象的集合, 存储5个学生对象,遍历学生对象
package com.gance.xyz.day13;
/**
* @author 杰仔正在努力
* @create 2022-11-23 18:02
*/
public class Student {
private String userName;
private int age;
/**
* 1、构造方法
* 2、set方法
*/
public Student(String userName, int age) {
this.userName = userName;
this.age = age;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
package com.gance.xyz.day13;
import java.util.ArrayList;
import java.util.Scanner;
/**
* @author 杰仔正在努力
* @create 2022-11-23 18:13
*/
public class Test03 {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第" + i + "位学生的名称:");
String userName = scanner.nextLine();
System.out.println("请输入第" + i + "位学位的年龄:");
int age = scanner.nextInt();
students.add(new Student(userName,age));
}
for (int i = 0; i <students.size(); i++) {
Student student = students.get(i);
System.out.println("姓名:" + student.getUserName() + student.getAge());
}
}
}
三、Collection 接口
Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合
add(Object e);//将元素e添加到集合中
size();//获取添加的元素的个数
addAll();//将集合中的元素添加到当前的集合中
isEmpty();//判断当前集合是否为空
clear();//清空集合元素
contains(Object obj);//判断当前集合中是否包含obj
containsAll(Collection coll): //判断形参coll中所有的元素是否都存在于当前集合中
remove(Object obj): //从当前集合中移除obj元素
removeAll(Collection coll): //差集:从当前集中移除coll中的所有元素
retainAll(Collection coll): //交集:获取当前集合和coll集合的交集,并返回当前集合
equals(Object obj): //要想返回true,需要判当前集合和形参相同
hashCode(): //返回当前对象的哈希值
Collection集合与数组间的转换
//集合 --->数组:toArray()
Object[] arr = coll.toArray();
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//数组 --->集合:调用Arrays类的静态方法asList(T ... t)
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
System.out.println(list);
练习
package com.gance.xyz.day16;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* @author 杰仔正在努力
* @create 2022-11-26 18:39
*/
public class Test02 {
public static void main(String[] args) {
//多态 由上图可知
Collection<String> collection = new ArrayList<>();
collection.add("apple1");
collection.add("apple2");
collection.add("apple3");
//get方法 是在list接口中
/**
* 在Collection接口是没有个体方法
*/
List list = (List) collection;
list.add(1,"apple");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
/**
* Collection 接口提供 集合的基本方法
* 具体实现 List ArrayList 需要重写实现 List接口
* list集合下都是有序的接口 都是存放顺序 可以允许存放重复数据 set集合不允许
*/
}
四、迭代器(Iterator)
Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合
方法 | 说明 |
---|---|
it.next() | 返回迭代器的下一个元素,并且更新迭代器的状态 |
it.hasNext() | 用于检测集合中是否还有元素 |
it.remove() | 将迭代器返回的元素删除 |
迭代器本身用于遍历集合,并不存放对象
// 引入 ArrayList 和 Iterator 类
import java.util.ArrayList;
import java.util.Iterator;
public class RunoobTest {
public static void main(String[] args) {
// 创建集合
ArrayList<String> sites = new ArrayList<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
// 获取迭代器
Iterator<String> it = sites.iterator();
// 输出集合中的第一个元素
System.out.println(it.next());
}
}
手写迭代器的hasNext和next方法
快捷键小知识:
快速生成–> while (iterator.hasNext()) {
Object next = iterator.next();
}
只需要输入itit 然后再输入回车即可
package com.gance.xyz.day16;
import java.util.List;
/**
* @author 杰仔正在努力
* @create 2022-11-26 16:31
*/
public class IteratorTest {
private List list;
/**
* 有参list集合
*/
public IteratorTest(List list) {
this.list = list;
}
/**
* next底层会使用计数器 每次调用.next方法时 计数+1
* 迭代器 计数器 初始值=0
*/
private int count = 0;
public Object next() {
if (list == null) {
throw new IteratorTestException("list is null");
}
if (count >= list.size()) {
//说明集合中 没有继续可以获取到元素
//访问下标越界 抛出异常 我们就可以自定义异常
throw new IteratorTestException("无法继续获取元素");
}
return list.get(count++);
}
public boolean hashNext() {
//hashNext 判断集合中 是否话可以继续获取元素 如果能够获取到元素则返回true
// if (count == list.size()) {
// //取次数==list.size() 集合个数 相等
//
// }
/**
* 例如 集合中存放了三个元素 list.size()==3
* 分析场景:
* 1、第一次调用 next 方法=count=1 count=1!=list.size()(3)=true 可以继续调用.next方法
* 2、第二次调用 next 方法=count=2 2!=list.size()(3)=true 可以继续调用.next方法
* 3、第三次调用 next 方法=count=3 3!=list.size() false 不可以继续调用.next方法
*/
return count != list.size();
}
}
抛出异常
package com.gance.xyz.day16;
import javax.crypto.interfaces.PBEKey;
import java.security.PublicKey;
/**
* @author 杰仔正在努力
* @create 2022-11-26 16:46
*/
public class IteratorTestException extends RuntimeException{
public IteratorTestException(String errMsg) {
super(errMsg);
}
}
4.1 foreach循环的使用
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作
增强for循环
基本语法
for(元素类型 元素名(自己取的变量名称):集合名或数组名){
访问元素
}
package com.gance.xyz.day16;
import java.util.ArrayList;
/**
* @author 杰仔正在努力
* @create 2022-11-26 19:15
*/
public class Test04 {
public static void main(String[] args) {
//增强for循环 for each 遍历 数组和集合
int[] arrInt = {23, 43, 56, 76};
for (int i = 0;i<arrInt.length;i++) {
System.out.println(arrInt[i]);
}
System.out.println("---------------------------");
//增强for循环
/**
* for(类型 变量名称:集合或数组名){
* 使用变量名称 访问集合
* }
*/
for (int j:arrInt) {
System.out.println(j);
}
System.out.println("----增强for循环 遍历 String[]-----");
String[] strs={"apple1","apple2","apple3"};
for (String str : strs) {
System.out.println(str);
}
System.out.println("----增强for循环 遍历 ArrayList<String>----");
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("apple1");
arrayList.add("apple2");
arrayList.add("apple3");
for (String str : arrayList) {
System.out.println(str);
}
}
}
三种循环遍历
package com.gance.xyz.day16;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author 杰仔正在努力
* @create 2022-11-27 15:12
* //使用三种不同方式实现循环
*/
public class Test05 {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("张三",22));
students.add(new Student("李四",22));
students.add(new Student("赵五",22));
//方式一 for循环遍历 集合
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
System.out.println(student.getName() + student.getAge());
}
System.out.println("----------------------------------");
//方式二 迭代器遍历
Iterator<Student> iterator = students.iterator();
while (iterator.hasNext()) {
Student next = iterator.next();
System.out.println(next.getName() + next.getAge());
}
System.out.println("----------------------------------");
//方式三 增强for循环
for (Student stu : students) {
System.out.println(stu.getName() + stu.getAge());
}
}
}
五、泛型
①Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
②泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
泛型标记符 | 说明 |
---|---|
E | Element (在集合中使用,因为集合中存放的是元素) |
T | Type(Java 类) |
V | value (值) |
K | key (键) |
N | number (数值类型) |
? | 代表不确定的Java类型 |
1、泛型底层原理就是使用擦除机制
2、泛型是在编译阶段限制传递的类型
package com.gance.xyz.day17;
import java.util.ArrayList;
import java.util.List;
/**
* @author 杰仔正在努力
* @create 2022-11-28 11:44
*/
public class Test07 {
public static void main(String[] args) {
ArrayList<Object> strs = new ArrayList<>();
//说明:将一个list集合 泛型赋值给一个没有使用到泛型list集合 直接去除泛型
List list = strs;
strs.add("aaple");
strs.add(1);
}
}
5.1 泛型类
1、静态方法中不能使用类的泛型
2、泛型的类型必须是类,不能是基本数据类型,需要用到基本数据类型的位置,拿包装类替换
3、如果实例化时,没有指明泛型的类型,默认类型为java.lang.Object类型
//定义的格式
修饰符 class 类名<类型> {}
ege: public class Student<E> {}
public class Gener<T> {
/**
* 泛型类格式: public class Gener<T>
* private T t;
* 在类中定义的成员属性 泛型T 类型 与我们在类上定义泛型类类型相同
*/
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-27 15:44
*/
public class Test01 {
public static void main(String[] args) {
/**
*泛型类
*/
Gener<String> stringGener = new Gener<>();
stringGener.setT("gener");
System.out.println(stringGener.getT());
Gener<Integer> integerGener = new Gener<>();
integerGener.setT(10);
System.out.println(integerGener.getT());
Gener<Double> doubleGener = new Gener<>();
doubleGener.setT(10.00);
System.out.println(doubleGener.getT());
}
}
5.2 泛型方法
泛型方法使用的规则:
①所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(例如: < E >)。
②每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
③类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
④泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)
//格式
修饰符<类型>返回值类型 方法名(类型 变量名){…}
ege:public<T> void show(T t){…}
public class GenerMain {
/**
* public 修饰符
* <T> 该方法使用泛型T
* void 返回
* 方法的名称
* 参数列表(T t)
* @param t
* @param <T>
*/
public <T> T show(T t) {
return t;
}
}
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-27 16:17
*/
public class Test02 {
public static void main(String[] args) {
GenerMain generMain = new GenerMain();
String string = generMain.show("apple");
System.out.println(string);
Integer integer = generMain.show(12);
System.out.println(integer);
Double d = generMain.show(12.00);
System.out.println(d);
}
}
5.3 泛型接口
//格式:
修饰符 interface 接口名<类型>{…}
ege:public interface MayiktInterface <T>{…}
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-27 16:36
*/
public interface GenerInterface<T> {
T show(T t);
}
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-27 16:37
*/
public class GenerImpl<T> implements GenerInterface<T>{
@Override
public T show(T t) {
return t;
}
}
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-27 16:42
*/
public class Test03 {
public static void main(String[] args) {
GenerImpl<String> stringGener = new GenerImpl<>();
String string = stringGener.show("23");
System.out.println(string);
}
}
5.4 类型通配符
1、类型通配符<?> 一般用于接受使用
2、List<?>:表示元素类型未知的list,它的元素可以匹配任何类型
3、 List<?>只能够用于接收,可以接收所有的泛型类型 但是不能添加
4、可以做get操作,获取object类型
import java.util.*;
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
//输出结果
data :icon
data :18
data :314
注意点:
注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){ }
注意点2:编译错误:不能用在泛型类的声明上
class GenericTypeClass<?>{ }
注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象 ArrayList<?> list2 = new ArrayList<?>()
上限通配符:<? extends 类型>
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
? extends A:
List<? extends A> List表示A或者其子类
下限通配符: <? super 类型>
下限super:使用时指定的类型不能小于操作的类,即>=
? super A:
List<? super A> List表示A或其父类型
5.5 可变参数
可变参数 又称 参数个数可变,用作方法的形参出现,那么方法参数个数就是 可变 的了
//格式:
修饰符 返回值类型 方法名(数据类型... 量名){ }
ege:public static int sum(int... a) { }
注意事项:
1、这里的 可变参数变量 其实是一个数组
2、如果一个方法 有多个参数,包含可变参数,可变参数要放在最后
package com.gance.xyz.day17;
/**
* @author 杰仔正在努力
* @create 2022-11-28 11:21
* 可变参数
*/
public class Test05 {
public static void main(String[] args) {
int sum1 = sum(1,1); //arr[0] = 1
System.out.println("sum:" + sum1);
int sum2 = sum(2,2,2);
System.out.println("sum:" + sum2);
int sum3 = sum(3,3,3,3);
System.out.println("sum:" + sum3);
}
/**
* int ... a 可变参数 底层基于 数组实现
* @param a
* @return
*/
public static int sum(int ... a) {
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
return sum;
}
}
sum:2
sum:6
sum:12
使用Arrays.asList() 定义好的 元素个数 是不能够发生变化
package com.gance.xyz.day17;
import java.util.Arrays;
import java.util.List;
/**
* @author 杰仔正在努力
* @create 2022-11-28 11:40
* 可变参数的使用
*/
public class Test06 {
public static void main(String[] args) {
List<String> strings = Arrays.asList("apple", "banana", "water");
//如果使用 Arrays.asList() 方法 创建的集合是不可以使用添加和删除 可以修改
strings.set(0,"6666");
System.out.println(strings);
}
}
六、LinkedList接口
1、 linkedList底层基于链表实现 增加 删除的效率非常高 查询效率非常低
2、linkedList底层基于双向链表实现
3、定义了Node类型的first和last,用于记录首末元素,默认值都是为null
Node类型源码
prev变量:记录前一个元素的位置
next变量:记录下一个元素的位置
package com.gance.xyz.day18;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @author 杰仔正在努力
* @create 2022-11-30 11:27
* LinkedList实现
*/
public class Test01 {
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("apple1");
linkedList.add("apple2");
linkedList.add("apple3");
System.out.println(linkedList.size());
Iterator<String> iterator1 = linkedList.iterator();
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
linkedList.remove(0);
System.out.println("删除之后遍历的结果");
Iterator<String> iterator2 = linkedList.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
linkedList.get(0);
}
}
七、set接口
1、存储无序的、不可重复的数据
2、Set接口是Collection的子接口,set接口没有提供额外的方法
3、 Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
7.1 HashSet
特点:
1、HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合
2、HashSet 允许有 null 值
3、.HashSet 是无序的,即不会记录插入的顺序
4、HashSet 没有Get方法,所以不能使用普通for循环遍历
底层数据结构为:Node[]数组 + 单链表 + 红黑树
元素添加实现过程
添加元素
package com.gance.xyz.day19;
import java.util.HashSet;
import java.util.Iterator;
/**
* @author 杰仔正在努力
* @create 2022-11-30 17:02
*/
public class Test03 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("apple");
hashSet.add("banana");
hashSet.add("pear");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
判断元素是否存在
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重复的元素不会被添加
System.out.println(sites.contains("Taobao"));
}
}
删除元素
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重复的元素不会被添加
sites.remove("Taobao"); // 删除元素,删除成功返回 true,否则为 false
System.out.println(sites);
}
}
删除所有元素
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重复的元素不会被添加
sites.clear();
System.out.println(sites);
}
}
计算大小
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重复的元素不会被添加
System.out.println(sites.size());
}
}
7.2 LinkedHashSet
1、LinkedHashSet 是 HashSet 的子类
2、LinkedHashSet 不允许集合元素重复
3、LinkedHashMap 有序的
package com.gance.xyz.day20;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 杰仔正在努力
* @create 2022-12-02 15:19
*/
public class Test04 {
public static void main(String[] args) {
/**
* LinkedHashMap与HashMap集合的区别
* 唯一区别:
* LinkedHashMap 有序的 HashMap无序的
*/
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
for (int i = 0;i <= 100; i++) {
linkedHashMap.put(i+"",i+"");
}
//hashMap 无序存放 底层基于散列
for (Map.Entry<String,String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey()+ "," + entry.getValue());
}
}
}
7.3 TreeSet
1、TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态
2、TreeSet底层使用红黑树结构存储数据
两种排序:
自然排序、定制排序
自然排序:
比较两个对象上是否相同的标准为:compareTo()返回0,不是equals()
定制排序:
比较两个对象上是否相同的标准为:compare()返回0,不是equals()
说明:
1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口 和 定制排序(Comparator)
自然排序
public void test1(){
TreeSet set = new TreeSet();
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
定制排序
public void test2(){
Comparator com = new Comparator() {
//照年龄从小到大排列
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("输入的数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
八、Map接口
1、Map与Collection并列存在。用于保存具有映射关系的数据:key-value
2、Map 中的 key 和 value 都可以是任何引用类型的数据
3、Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
4、 key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
8.1 HashMap
1、HashMap 是一个散列表,它存储的内容是键值对(key-value)映射
2、HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步
3、HashMap 是无序的,即不会记录插入的顺序
4、HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口
JDK 1.8之前:
HashMap的内部存储结构其实是数组和链表的结合
JDK 1.8之后:
HashMap的内部存储结构其实是数组+链表+树的结合
何为hash冲突:
根据key(键)即经过一个函数f(key)得到的结果的作为地址去存放当前的key value键值对(这个是hashmap的存值方式),但是却发现算出来的地址上已经被占用了。这就是所谓的hash冲突
HashMap中是如何解决hash冲突?
使用链地址法进行解决
优点
1、 处理冲突简单,无堆积现象。即非同义词决不会发生冲突,因此平均查找长度较短;
2、适合总数经常变化的情况。(因为拉链法中各链表上的结点空间是动态申请的)
3、占空间小。装填因子可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计
4、删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
缺点
1、 查询时效率较低。(存储是动态的,查询时跳转需要更多的时间)
2、在key-value可以预知,以及没有后续增改操作时候,开放定址法性能优于链地址法。
3、 不容易序列化
HashMap底层源码常量
HashMap集合中键值对实现封装:
通过Map接口封装的Entry对象交给子类实现
数组和链表的特点:
数组:具有随机访问的特点,能达到o(1)的时间复杂度,查询速度很快,但是插入和删除慢,需要移动数组元素的位置
链表:链表插入和删除不需要移动位置,只需改变next指针指向的对象引用,但是链表的时间复杂度为o(n),只能顺着节点往下查找,查询速度慢
方法名称 | 作用 |
---|---|
V get(Object key) | 根据键获取值 |
Set< K > keySet() | 获取所有键的集合(返回Set集合) |
Collection< V > values() | 获取所有值的集合(返回Collection集合) |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对象的集合 |
default V getOrDefault(Object key,V defaultValue) | 如果存在相应的key则返回其对应的value,否则返回给定的默认值defaultValue |
添加元素
import java.util.HashMap;
public class RunoobTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites);
}
}
访问元素
import java.util.HashMap;
public class RunoobTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
System.out.println(Sites.get(3));
}
}
删除元素
import java.util.HashMap;
public class RunoobTest {
public static void main(String[] args) {
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
Sites.remove(4);
System.out.println(Sites);
}
}
获取所有键的集合
public class Test05 {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("daxue","zhangsan");
hashMap.put("gaozhong","lisi");
hashMap.put("chuzhong","wangwu");
Set<String> strings = hashMap.keySet();
for (String str : strings) {
System.out.println(str);
}
}
}
获取所有值的集合(返回Collection集合)
public class Test05 {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("daxue","zhangsan");
hashMap.put("gaozhong","lisi");
hashMap.put("chuzhong","wangwu");
Collection<String> values = hashMap.values();
for (String str : values) {
System.out.println(str);
}
}
}
获取所有键值对象的集合
public class Test05 {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("daxue","zhangsan");
hashMap.put("gaozhong","lisi");
hashMap.put("chuzhong","wangwu");
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry);
}
}
}
练习:
1.定义学生Student 实体类 成员属性 姓名、年龄;
2.定义HashMap集合存入Student对象,key存放Student对象 value存放手机号码;
3.要求保证key的唯一性,也就是学生对象的成员变量值相同,我们就应该认为是同一个对象
package com.gance.xyz.day20;
import java.util.Objects;
/**
* @author 杰仔正在努力
* @create 2022-12-01 20:47
*/
public class Student {
private String name;
private Integer age;
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.gance.xyz.day20;
import java.util.HashMap;
/**
* @author 杰仔正在努力
* @create 2022-12-01 20:47
*/
public class Test01 {
public static void main(String[] args) {
HashMap<Student, String> hashMap = new HashMap<>();
/**
* key:存放学生对象
* value:学生对应的手机号码
*/
hashMap.put(new Student("zhangsan",23),"1234567");
hashMap.put(new Student("zhangsan",23),"1234568");
hashMap.put(new Student("zhangsan1",23),"1234568");
for (Student student : hashMap.keySet()) {
String phone = hashMap.get(student);
System.out.println("key:" + student.toString()+"value:"+phone);
}
System.out.println(hashMap.size());
}
}
8.2 LinkedHashMap
1、LinkedHashMap 是 HashMap 的子类
2、在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
3、与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
package com.gance.xyz.day20;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 杰仔正在努力
* @create 2022-12-02 15:19
*/
public class Test04 {
public static void main(String[] args) {
/**
* LinkedHashMap与HashMap集合的区别
* 唯一区别:
* LinkedHashMap 有序的 HashMap无序的
*/
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
for (int i = 0;i <= 100; i++) {
linkedHashMap.put(i+"",i+"");
}
//hashMap 无序存放 底层基于散列
for (Map.Entry<String,String> entry : linkedHashMap.entrySet()) {
System.out.println(entry.getKey()+ "," + entry.getValue());
}
}
}
8.3 TreeMap
1、TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序,可以保证所有的 Key-Value 对处于有序状态
2、TreeSet底层使用红黑树结构存储数据
3、TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0
九、Collections工具类
作用:操作Collection和Map的工具类
Collection与Collections的区别:
Collections是单列集合操作的工具类 ,Collection 单列集合
方法 | 作用 |
---|---|
reverse(List) | 反转 List 中元素的顺序 |
shuffle(List) | 对 List 集合元素进行随机排序 |
sort(List) | 根据元素的自然顺序对指定 List 集合元素按升序排序 |
sort(List,Comparator) | 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 |
swap(List,int, int) | 将指定 list 集合中的 i 处元素和 j 处元素进行交换 |
Object max(Collection) | 根据元素的自然顺序,返回给定集合中的最大元素 |
Object max(Collection,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最大元素 |
Object min(Collection) | 根据元素的自然顺序,返回给定集合中的最小元素 |
Object min(Collection,Comparator) | 根据 Comparator 指定的顺序,返回给定集合中的最小元素 |
int frequency(Collection,Object) | 返回指定集合中指定元素的出现次数 |
void copy(List dest,List src) | 将src中的内容复制到dest中 |
boolean replaceAll(List list,Object oldVal,Object newVal) | 使用新值替换List 对象的所有旧值 |