Java种的集合分为单列集合和双列集合,单列集合的最高层接口是Collection,双列集合的最高层是Map,这里先介绍单列集合
单列集合
体系结构:
注:红色框都为接口,蓝色框都为实现类(实现类Vector已经被淘汰)
Collection:
Collection是一个接口,不能直接创建他的对象,所以学习他的方法时,只能创建他的实现类对象。例如ArrayList。
代码演示:
public class CollectionDemo {
public static void main(String[] args) {
/*
public boolean add(E e) 添加
public void clear() 清空
public boolean remove(E e) 删除
public boolean contains(Object obj) 判断是否包含
public boolean isEmpty() 判断是否为空
public int size 集合长度
*/
Collection<String> coll = new ArrayList<>();
//1.添加
//注:添加方法会返回一个布尔类型的值
//如果我们往List系列集合中添加数据,一定会返回true,因为List系列集合是允许元素重复的
//如果往Set系列集合中添加数据,当元素不存在时才会返回true,否则返回false,因为Set集合时不允许重复的
coll.add("aa");
coll.add("bb");
coll.add("cc");
System.out.println(coll);
//2.清空
coll.clear();
System.out.println(coll);
//再添加回来
coll.add("aa");
coll.add("bb");
coll.add("cc");
//3.删除
coll.remove("bb");
System.out.println(coll);
//4.判断是否包含
boolean result1 = coll.contains("bb");
System.out.println(result1);
//5.判断是否为空
boolean result2 = coll.isEmpty();
System.out.println(result2);
//6.集合长度
int size = coll.size();
System.out.println(size);
}
}
运行结果:
(1)contains方法的拓展:
如果让contains方法判断自定义的类对象,需要重写equals方法才能得到正确结果,因为contains方法底层就调用了equals方法,如果没有重写,默认使用的就是Object类中的equals方法,比较的是地址值。
没有重写equals时
代码演示:
学生类Student:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
测试类Test:
public class CollectionContainsDemo {
public static void main(String[] args) {
Collection<Student> coll = new ArrayList<>();
Student s1 = new Student("xiaobai",21);
Student s2 = new Student("xiaohei",22);
Student s3 = new Student("xiaohei", 22);//与上面s2一样
coll.add(s1);
coll.add(s2);
boolean result = coll.contains(s3);
System.out.println(result);
}
}
运行结果:
重写equals时
代码演示:
Student类:
加入下面这段代码
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
测试类不改变
运行结果:
(2)Collection系列集合三种通用的遍历方式:
①迭代器遍历
②增强for遍历
③lambda表达式遍历
接下来进行代码演示:
①迭代器遍历:
迭代器遍历相关的三个方法:
Iterator<E> iterator() : 获取一个迭代器对象
boolean hasNext() : 判断当前指向的位置是否有元素
E next() : 获取当前指向的元素并移动指针
代码演示:
public class ErgodicDemo {
public static void main(String[] args) {
//迭代器遍历相关的三个方法:
//Iterator<E> iterator():获取一个迭代器对象
//boolean hasNext(): 判断当前指向的位置是否有元素
//E next():获取当前指向的元素并移动指针
//创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aa");
coll.add("bb");
coll.add("cc");
//获取迭代器对象
Iterator<String> it = coll.iterator();
while(it.hasNext()) {
String str = it.next();
System.out.println(str);
}
}
}
运行结果:
迭代器注意点:
1.指针指向没有元素的位置时再强行调用next方法,会报错NoSuchElementException
2.迭代器遍历完毕,指针不会复位
3.循环中只能用一次next方法
4.迭代器遍历时,不能用集合的方法进行增加或者删除(可以使用迭代器的remove方法删除)
②增强for遍历:
格式:
for(数据类型 变量名 : 集合/数组){
方法体
}
快速生成方式:集合名字.for 再按回车
代码演示:
public class EnhancedFor {
public static void main(String[] args) {
//创建集合并存储元素
Collection<String> coll = new ArrayList<>();
coll.add("aa");
coll.add("bb");
coll.add("cc");
//增强for遍历
//循环遍历中,下面的第三方变量s会依次表示集合中的每一个元素
for(String s : coll) {
System.out.println(s);
}
}
}
运行结果:
增强for注意点:
修改增强for中的变量,不会改变集合中原本的数据。因为增强for中使用了参数中设置的第三方变量存储,修改的是第三方变量的值。
③Lambda表达式:
代码演示:
public class LambdaDemo {
public static void main(String[] args) {
//default void forEach(Consumer<? super T> action):
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aa");
coll.add("bb");
coll.add("cc");
//2.使用匿名内部类遍历
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("----------------------");
//3.使用Lambda表达式遍历
coll.forEach(s -> System.out.println(s));
}
}
运行结果:
④三种遍历方式适用情况:
若在遍历过程中需要删除元素,则使用迭代器
若仅仅想遍历,则使用增强for或Lambda表达式
1.List
List系列集合特点:有序,可重复,有索引
虽然Collection的方法List都继承了,但List集合因为有索引,所以多了很多索引操作的方法,下面介绍一下List系列集合独有的方法。
List系列集合独有的方法
代码演示:
public class ListDemo {
public static void main(String[] args) {
/*
List系列集合独有的方法:
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素
*/
//1.创建一个集合并添加元素
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//2.void add(int index,E element) 在此集合中的指定位置插入指定的元素
list.add(1,"ee");
System.out.println(list);
System.out.println("----------------");
//3.E remove(int index) 删除指定索引处的元素,返回被删除的元素
/*remove注意点:当集合中存储的是int类型的数据,删除时输入int类型整数,
会首先认为是删除索引,但是输入一个Integer类型的变量,则会认为是删除元素*/
String remove = list.remove(1);
System.out.println("被删除的元素:" + remove);
System.out.println(list);
System.out.println("----------------");
//4.E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
String result = list.set(1, "ee");
System.out.println("被修改的元素:" + result);
System.out.println(list);
System.out.println("----------------");
//5.E get(int index) 返回指定索引处的元素
System.out.println(list.get(0));
}
}
运行结果:
List集合的遍历方式
List继承了Collection,所以Collection中的遍历方式在List中都可以使用,即:
①迭代器遍历
②增强for遍历
③Lambda表达式遍历
此外List系列集合还有自己的遍历方式,即:
④普通for遍历
代码演示:
public class OrdinaryFor {
public static void main(String[] args) {
//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//遍历
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
System.out.println(str);
}
}
}
运行结果:
⑤列表迭代器遍历
它可以在遍历时添加和删除元素
代码演示:
public class ListIteraterDemo {
public static void main(String[] args) {
//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
//创建列表迭代器
ListIterator<String> it1 = list.listIterator();
//遍历
while(it1.hasNext()) {
String str = it1.next();
System.out.println(str);
}
//它可以在遍历中添加或删除元素
ListIterator<String> it2 = list.listIterator();
while(it2.hasNext()) {
String str = it2.next();
if("aa".equals(str)) {
it2.add("ee");
}
if("cc".equals(str)) {
it2.remove();
}
}
System.out.println(list);
}
}
运行结果:
五种遍历方式总结:
在遍历过程中需要删除元素时,使用迭代器
在遍历过程中需要添加元素时,使用列表迭代器
如果仅仅想遍历,使用增强for或者Lambda表达式
如果遍历时想操作索引,可以使用普通for
(1)ArrayList
ArrayList底层是数组结构
底层原理:
①利用空参创建的集合,在底层创建一个默认长度为0的数组
②添加第一个元素时,底层会创建一个新的长度为10的数组
如图(elementDate是数组名,size是指针)
③存满时,会扩容1.5倍(即第一次存满后数组大小扩容到15)
④如果一次添加多个元素,扩容到1.5倍放不下,则新建数组的长度以实际为准(即一开始十 个数据存满数组的情况下,再一次性添加100个元素,那么新数组长度就为110)
迭代器底层原理图解:
(2)LinkedList
LinkedList底层数据结构是双向链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的
双向链表模型:
由于上述特点,LinkedList中多了很多首尾操作的特有API,例如:
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
添加元素原理图解:
2.Set
Set系列集合特点:无序,不可重复,无索引
Set中的方法:
Set是一个接口,这个接口中的方法基本上与Collection中的API一致,方法如下
public boolean add(E e) 添加
public void clear() 清空
public boolean remove(E e) 删除
public boolean contains(Object obj) 判断是否包含
public boolean isEmpty() 判断是否为空
public int size 集合长度
这部分方法在前文Collection模块中已经演示,这里不再演示。
Set的遍历方法:
也可以使用Collection中的遍历方法,如:
①迭代器遍历
②增强for遍历
③Lambda表达式遍历
这三种方法前文已经演示,注意因为Set系列集合没有索引,所以不能使用普通for遍历
(1)HashSet
特点:
无序,不可重复,无索引
HashSet底层原理概要:
HashSet集合底层采取哈希表存储数据
哈希表是一种对于增删查改数据性能都较好的结构
接下来我们先介绍一下哈希表
哈希表:
组成:
JDK8之前:数组 + 链表
JDK8开始:数组 + 链表 + 红黑树
哈希表中有一个非常重要的值:哈希值
哈希值:
可以理解为对象的整数表现形式
·根据hashCode方法算出来的int类型的整数
·该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
·一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
对象哈希值的特点:
·如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
·如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值也一样
·在小部分情况下,不同的属性值或者地址值计算出来的哈希值有可能一样(哈希碰撞)
未重写hashCode情况代码演示
编写学生类来创建对象
Student:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
编写测试类:
HashSetDemo1:
public class HashSetDemo1 {
public static void main(String[] args) {
//创建两个属性一样的学生对象
Student s1 = new Student("xiaobai", 21);
Student s2 = new Student("xiaobai", 21);
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
}
}
运行结果
未重写hashCode情况代码演示
在Student类中加入了重写的hashCode方法
Student:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
HashSetDemo1中的代码不变
运行结果
HashSet的底层原理
注意:
1.如果元素应存入位置已经有多个元素形成链表,需要调用equals方法比较每个元素,若都不一样,则挂在链表最后。
2.默认加载因子:默认加载因子为0.75表示当数组里面存了16 * 0.75 = 12个元素时,数组就会扩容成原来的两倍。
3.当链表长度大于8而且数组长度大于等于64时,这个链表将会自动转成红黑树,从而提高查找效率。
4.如果集合中存储的是自定义对象,必须要重写hashCode和equals方法。
HashSet小练习:
需求:创建一个存储学生对象的集合,存储多个学生对象 使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
代码演示:
Student类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
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 age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
测试类HashSetTest1:
public class HashSetTest1 {
public static void main(String[] args) {
/*
需求:创建一个存储学生对象的集合,存储多个学生对象
使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象
*/
HashSet<Student> hashSet = new HashSet<>();
//创建学习对象 三个xiaobai 两个xiaohei 一个xiaohuang
Student s1 = new Student("xiaobai",21);
Student s2 = new Student("xiaobai",21);
Student s3 = new Student("xiaobai",21);
Student s4 = new Student("xiaohei",20);
Student s5 = new Student("xiaohei",20);
Student s6 = new Student("xiaohuang",20);
//添加到集合中
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
hashSet.add(s5);
hashSet.add(s6);
//遍历
Iterator<Student> it = hashSet.iterator();
while(it.hasNext()) {
Student str = it.next();
System.out.println(str);
}
}
}
运行结果:
注意:
如果未在Student类中重写hashCode和equals方法,属性值相等的对象也是会存入集合的。
LinkedHashSet:
特点:
LinkedHashSet是有序(存储和取出的元素顺序一致),无重复,无索引的
代码演示:
public class LinkedHashSetDemo1 {
public static void main(String[] args) {
LinkedHashSet<Student> lhs = new LinkedHashSet<>();
Student s1 = new Student("xiaobai", 20);
Student s2 = new Student("xiaohei", 21);
Student s3 = new Student("xiaohuang", 22);
Student s4 = new Student("xiaohei", 21);
lhs.add(s1);
lhs.add(s2);
lhs.add(s3);
lhs.add(s4);
System.out.println(lhs);
}
}
运行结果:
原理:
底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序
如下图红色双线表示双链表存储
适用情况:
如果只需要数据去重,那么默认使用HashSet
如果要求去重且存取有序,就使用LinkedHashSet
(2)TreeSet
特点:
不重复,无索引,可排序(按照元素的默认规则从小到大排序)
TreeSet集合底层是基于红黑树的数据结构(不了解红黑树可以查看作者的另一篇文章《Java基合进阶——数据结构》)实现排序的,增删改查性能都较好
代码演示:
public class TreeSetDemo1 {
public static void main(String[] args) {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3);
ts.add(5);
ts.add(2);
ts.add(1);
ts.add(4);
System.out.println(ts);
}
}
运行结果:
若使用Collection中的那三种遍历方式遍历,也会得到这个顺序的结果
排序规则:
对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序
(字符串中从第一个字母挨个比较,并且若前若干个字母一样,后面没有字母的那个字符串排在后边还有字母的字符串前面,例如:aaa,ab,aba 这样排序)
TreeSet的两种比较方式
TreeSet有两种比较方式,为默认排序/自然排序和比较器排序
使用原则:
默认使用第一种,如果第一种不能满足当前需求(比如字符串中默认的规则需要修改,或者Integer默认排序规则为从小到大若不是用这个规则则使用第二种),就使用第二种
1.默认排序/自然排序:
对数据类型可直接使用,对自定义对象使用需要先在自定义的JavaBean中实现Comparable接口,并重写其中的compareTo方法,在这个方法中可以定义根据什么排序。
下面举例一个重写后的compareTo方法,它的作用是只看年龄,按照年龄的升序进行排列
@Override
public int compareTo(Student o){
return this.getAge() - o.getAge();
}
this:表示当前要添加的元素 o:表示已经在红黑树存在的元素
返回值:
负数:认为要添加的元素是小的,存左边
正数:认为要添加的元素是大的,存右边
0 :认为要添加的元素已经存在,舍弃
当添加某元素后TreeSet的数据结构不满足红黑树的规则了,它就会自己通过调整节点颜色或左旋右旋调整来满足红黑树的规则(想要详细了解可以看作者的另一篇文章《Java集合进阶——数据结构》)
2.比较器排序
创建TreeSet对象时候,传递比较器Comparator指定规则
比较器排序小练习1:
需求:存入四个字符串:"c","ab","df","qwer"
按照长度排序,如果一样长则按照首字母排序
代码演示:
public class TreeSetTest1 {
public static void main(String[] args) {
//需求:存入四个字符串:"c","ab","df","qwer"
//按照长度排序,如果一样长则按照首字母排序
//创建TreeSet对象
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
//o1为要添加的元素
//o2为已经在红黑树中存在的元素
//返回值的规则跟之前一样
@Override
public int compare(String o1, String o2) {
//根据长度排序
int i = o1.length() - o2.length();
//根据字母排序
//如果i == 0即长度相等,那么就使用默认的比较规则,按字母排序,
//如果长度不相等,那么直接根据长度排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
//添加元素
ts.add("c");
ts.add("ad");
ts.add("df");
ts.add("qwer");
//输出
System.out.println(ts);
}
}
运行结果:
比较器排序小练习2:
需求:创建5个学生对象
属性:姓名,年龄,语文成绩,数学成绩,英语成绩
按照总分从高到低输出到控制台
如果总分一样,按照语文成绩排
如果语文成绩一样,按照数学成绩排
如果数学成绩一样,按照英语成绩排
如果英语成绩一样,按照年龄排
如果年龄一样,按照姓名的字母顺序排
如果都一样,认为是同一个学生,不存
代码演示:
Student2:
public class Student2 implements Comparable<Student2>{
private String name;
private int age;
private int chineseGrade;
private int mathGrade;
private int englishGrade;
public Student2() {
}
public Student2(String name, int age, int chineseGrade, int mathGrade, int englishGrade) {
this.name = name;
this.age = age;
this.chineseGrade = chineseGrade;
this.mathGrade = mathGrade;
this.englishGrade = englishGrade;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return chineseGrade
*/
public int getChineseGrade() {
return chineseGrade;
}
/**
* 设置
* @param chineseGrade
*/
public void setChineseGrade(int chineseGrade) {
this.chineseGrade = chineseGrade;
}
/**
* 获取
* @return mathGrade
*/
public int getMathGrade() {
return mathGrade;
}
/**
* 设置
* @param mathGrade
*/
public void setMathGrade(int mathGrade) {
this.mathGrade = mathGrade;
}
/**
* 获取
* @return englishGrade
*/
public int getEnglishGrade() {
return englishGrade;
}
/**
* 设置
* @param englishGrade
*/
public void setEnglishGrade(int englishGrade) {
this.englishGrade = englishGrade;
}
public String toString() {
int sum = chineseGrade + mathGrade + englishGrade;
return "Student2{name = " + name + ", age = " + age + ", chineseGrade = " + chineseGrade + ", mathGrade = " + mathGrade + ", englishGrade = " + englishGrade + ", sum = " + sum + "}";
}
@Override
public int compareTo(Student2 o) {
int score1 = this.getChineseGrade() + this.getMathGrade() + this.getEnglishGrade();
int score2 = o.getChineseGrade() + o.getMathGrade() + o.getEnglishGrade();
int i = score1 - score2;
i = i == 0 ? this.getChineseGrade() - o.getChineseGrade() : i;
i = i == 0 ? this.getMathGrade() - o.getMathGrade() : i;
i = i == 0 ? this.getEnglishGrade() - o.getEnglishGrade() : i;
i = i == 0 ? this.getAge() - o.getAge() : i;
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
测试类TreeSetTest2:
public class TreeSetTest2 {
public static void main(String[] args) {
//需求:创建5个学生对象
//属性:姓名,年龄,语文成绩,数学成绩,英语成绩
//按照总分从高到低输出到控制台
//如果总分一样,按照语文成绩排
//如果语文成绩一样,按照数学成绩排
//如果数学成绩一样,按照英语成绩排
//如果英语成绩一样,按照年龄排
//如果年龄一样,按照姓名的字母顺序排
//如果都一样,认为是同一个学生,不存
//创建集合并定义排序规则
TreeSet<Student2> ts = new TreeSet<>();
//创建对象
Student2 s1 = new Student2("laoda",20,65,66,67);
Student2 s2 = new Student2("laoer",21,67,66,67);
Student2 s3 = new Student2("laosan",22,65,66,67);
Student2 s4 = new Student2("laosi",21,65,68,67);
Student2 s5 = new Student2("laowu",22,65,80,69);
//添加对象
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
for (Student2 stu : ts) {
System.out.println(stu);
}
}
}
运行结果:
集合工具类Collections
常用API:
public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素 public static void shuffle(List<?> list) 打乱List集合元素的顺序 public static <T> void sort(List<T> list, T key) 排序 public static <T> void sort(List<T> list, Comparator<T> c) 根据指定的规则进行排序 public static <T> int binarySearch(List<T> list, T key) 以二分查找法查找元素 public static <T> void copy(List<T> dest, List<T> src) 拷贝集合中的元素 public static <T> int fill(List<T> list, T obj) 使用指定的元素填充集合 public static <T> void max/min(Collection<T> coll) 根据默认的自然排序获取最 大/最小值 public static <T> void swap(List<?> list, int i, int j) 交换集合中指定位置的元素
代码演示:
public class CollectionsDemo1 {
public static void main(String[] args){
/*
public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素
public static void shuffle(List<?> list) 打乱List集合元素的顺序
public static <T> void sort(List<T> list, T key) 排序
public static <T> void sort(List<T> list, Comparator<T> c) 根据指定的规则进行排序
public static <T> int binarySearch(List<T> list, T key) 以二分查找法查找元素
public static <T> void copy(List<T> dest, List<T> src) 拷贝集合中的元素
public static <T> int fill(List<T> list, T obj) 使用指定的元素填充集合
public static <T> void max/min(Collection<T> coll) 根据默认的自然排序获取最大/最小值
public static <T> void swap(List<?> list, int i, int j) 交换集合中指定位置的元素
*/
//创建集合
ArrayList<String> list = new ArrayList<>();
//1.public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素
System.out.println("----------批量添加元素----------");
//注意只能添加Collection下的类
Collections.addAll(list,"aaa","bbb","ccc","123");
System.out.println(list);
//2.public static void shuffle(List<?> list) 打乱List集合元素的顺序
System.out.println("----------打乱List集合元素的顺序----------");
Collections.shuffle(list);
System.out.println(list);
//3.public static <T> void sort(List<T> list, T key) 以二分查找法查找元素
System.out.println("----------排序----------");
Collections.sort(list);
System.out.println(list);
//4.public static <T> void sort(List<T> list, Comparator<T> c) 根据指定的规则进行排序
System.out.println("----------根据指定的规则进行排序----------");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//按默认字符串比较规则排序
return o1.compareTo(o2);
}
});
System.out.println(list);
//5.public static <T> int binarySearch(List<T> list, T key) 以二分查找法查找元素
System.out.println("----------以二分查找法查找元素----------");
System.out.println(list);
int i = Collections.binarySearch(list, "bbb");
System.out.println(i);
//6.public static <T> void copy(List<T> dest, List<T> src) 拷贝集合中的元素
System.out.println("----------拷贝集合中的元素----------");
//新建一个集合
ArrayList<String> listCopy = new ArrayList<>();
//注意被拷贝的集合长度要与拷贝集合长度一致
Collections.addAll(listCopy,"","","","");
Collections.copy(listCopy,list);
System.out.println(listCopy);
//7.public static <T> int fill(List<T> list, T obj) 使用指定的元素填充集合
System.out.println("----------使用指定的元素填充集合----------");
Collections.fill(list,"666");
System.out.println(list);
//8.public static <T> void max/min(Collection<T> coll) 根据默认的自然排序获取最大/最小值
System.out.println("----------根据默认的自然排序获取最大/最小值----------");
String max = Collections.max(listCopy);
String min = Collections.min(listCopy);
System.out.println(max);
System.out.println(min);
//9.public static <T> void swap(List<?> list, int i, int j) 交换集合中指定位置的元素
System.out.println("----------交换集合中指定位置的元素----------");
System.out.println(listCopy);
Collections.swap(listCopy,1,2);
System.out.println(listCopy);
}
}
运行结果:
单列集合使用场景
1.ArrayList
如果想要集合中的元素可重复,(用的最多)用ArrayLst集合,基于数组的。
2.LinkedList
如果想要集合中的元素可重复,而且当前的增删操作明显多于查询,用LinkedList,基于链表的
3.HashSet
如果想对集合中的元素去重,(用的最多)用HashSet,基于哈希表的
4.LinkedHashSet
如果相对集合中的元素去重,而且保证存取顺序,用LinkedHashSet,基于哈希表和双链表,效率低于HashSet
5.TreeSet
如果相对集合中的元素进行排序,用TreeSet,基于红黑树。后续也可使用List集合实现排序
双列集合
介绍:
双列集合每次添加添加两个(或者说一对)元素
如图:
左边这列称为键,不可以重复,右边这列称为值,可以重复,键跟值是一一对应的,每一个键只能找到自己的值。
一对一一对应的键和值统称为一个键值对或者键值对对象,Java中也叫Entry对象。
体系结构
由于Hashtable和Properties与IO有关,所以先不讲解
MAP
常用API
V put(K key,V value) 添加/覆盖元素 V remove(Object key) 根据键删除键值对元素 void clear() 移除所有的键值对元素 boolean containsKey(Object key) 判断集合是否包含指定的键 boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,即集合中键值对的个数
代码演示:
public class MapDemo1 {
public static void main(String[] args) {
/*
V put(K key,V value) 添加/覆盖元素
V remove(Object key) 根据键删除键值对元素
void clear() 移除所有的键值对元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containsValue(Object value) 判断集合是否包含指定的值
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,即集合中键值对的个数
*/
//创建集合对象
//因为Map为接口,所以利用HashMap创建对象
Map<String,String> m1 = new HashMap<>();
//添加/覆盖元素
System.out.println("----------添加/覆盖元素----------");
//若要添加的键不存在,则直接添加到集合中并返回null
//若要添加的键存在,则将原来的键值对对象覆盖,并返回被覆盖的值
String result1 = m1.put("灰太狼", "红太狼");
m1.put("美羊羊","沸羊羊");
m1.put("懒羊羊","小灰灰");
System.out.println(m1);
System.out.println(result1);
//新添加一个已经存在的键
String result2 = m1.put("美羊羊","喜羊羊");
System.out.println(result2);
System.out.println(m1);
//根据键删除键值对元素
System.out.println("----------根据键删除键值对元素----------");
String result3 = m1.remove("灰太狼");
System.out.println(result3);
System.out.println(m1);
//移除所有的键值对元素
System.out.println("----------移除所有的键值对元素----------");
m1.clear();
System.out.println(m1);
//重新添加元素
m1.put("灰太狼", "红太狼");
m1.put("懒羊羊","小灰灰");
m1.put("美羊羊","喜羊羊");
//判断集合是否包含指定的键
System.out.println("----------判断集合是否包含指定的键----------");
boolean result4 = m1.containsKey("懒羊羊");
boolean result5 = m1.containsKey("烤全羊");
System.out.println(result4);
System.out.println(result5);
//判断集合是否包含指定的值
System.out.println("----------判断集合是否包含指定的值----------");
boolean result6 = m1.containsValue("小灰灰");
boolean result7 = m1.containsValue("烤全羊");
System.out.println(result6);
System.out.println(result7);
//判断集合是否为空
System.out.println("----------判断集合是否为空----------");
boolean result8 = m1.isEmpty();
System.out.println(result8);
//集合的长度,即集合中键值对的个数
System.out.println("----------集合的长度,即集合中键值对的个数----------");
int size = m1.size();
System.out.println(size);
}
}
运行结果:
三种遍历方式:
1.通过键找值遍历
下面我来使用键找值遍历方式,通过迭代器、增强for、Lambda表达式遍历集合。
代码演示:
public class MapDemo2 {
public static void main(String[] args) {
//创建集合对象
Map<String,String> m = new HashMap<>();
//添加对象
m.put("喜羊羊","灰太狼");
m.put("沸羊羊","美羊羊");
m.put("懒羊羊","小灰灰");
//通过键找值遍历
//将双列集合中的键提另出来放入一个单列集合
Set<String> keys = m.keySet();
//1.迭代器
System.out.println("----------迭代器----------");
//创建迭代器对象
Iterator<String> it = keys.iterator();
//使用迭代器遍历
while(it.hasNext()) {
String key = it.next();
//键找值
String value = m.get(key);
System.out.println(key + " = " + value);
}
//2.增强for
System.out.println("----------增强for----------");
for (String key : keys) {
String value = m.get(key);
System.out.println(key + " = " + value);
}
//3.Lambda表达式
System.out.println("----------Lambda表达式----------");
keys.forEach(key -> {
String value = m.get(key);
System.out.println(key + " = " + value);
}
);
}
}
运行结果:
2.通过键值对遍历
下面我来使用键值对遍历方式,通过迭代器、增强for、Lambda表达式遍历集合。
代码演示
public class MapDemo3 {
public static void main(String[] args) {
//创建集合对象
Map<String,String> m = new HashMap<>();
//添加元素
m.put("喜羊羊","灰太狼");
m.put("沸羊羊","美羊羊");
m.put("懒羊羊","小灰灰");
//通过键值对遍历
//将键值对对象放到一个单列集合中
Set<Map.Entry<String, String>> entries = m.entrySet();
//1.迭代器
System.out.println("----------增强for----------");
Iterator<Map.Entry<String, String>> it = entries.iterator();
while(it.hasNext()) {
Map.Entry<String, String> entry = it.next();
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
//2.增强for
System.out.println("----------增强for----------");
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
//3.Lambda表达式
System.out.println("----------Lambda表达式----------");
entries.forEach(entry -> {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
);
}
}
运行结果:
3.Lambda表达式
default void forEach(BiConsumer<? super K, ? super V> action) 结合Lambda遍历Map集合
代码演示:
public class MapDemo4 {
public static void main(String[] args) {
//创建集合对象
Map<String,String> m = new HashMap<>();
//添加元素
m.put("喜羊羊","灰太狼");
m.put("沸羊羊","美羊羊");
m.put("懒羊羊","小灰灰");
//使用Lambda表达式遍历
//1.匿名内部类
m.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key + " = " + value);
}
});
System.out.println("---------------------");
//2.转换成Lambda表达式
m.forEach((key, value) -> System.out.println(key + " = " + value)
);
}
}
运行结果:
1.HashMap
特点:
1.HashMap是Map里面的一个实现类
2.没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
3.特点都是由键决定的:无序、不重复、无索引(都是指键)
4.HashMap跟HashSet底层原理是一模一样的,都是哈希表结构(JDK8之前:数组+链 表,JDK8之后:数组+链表+红黑树)计算哈希值时也是通过键计算
5.存入元素时,以hashCode方法和equals方法来保证键的唯一
6.如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储自定义对 象,不需要重写hashCode和equals方法
小练习1
需求:创建一个HashMap集合, 键是学生对象(Student),值是籍贯(String) 存储三个键值对元素,并遍历 要求:同姓名、同年龄认为是同一个学生
核心点:HashMap的键位置如果存储的是自定义对象,需要重写hashCode和equals方法。
代码演示:
public class HashMapTest1 {
public static void main(String[] args) {
/*
需求:创建一个HashMap集合,
键是学生对象(Student),值是籍贯(String)
存储三个键值对元素,并遍历
要求:同姓名、同年龄认为是同一个学生
*/
//创建学生对象
Student s1 = new Student("han",21);
Student s2 = new Student("ma",22);
Student s3 = new Student("zhao",21);
Student s4 = new Student("zhao",21);
//创建HashMap集合
HashMap<Student,String> hm = new HashMap<>();
//存储键值对元素
hm.put(s1,"石家庄");
hm.put(s2,"邯郸");
hm.put(s3,"保定");
hm.put(s4,"天津");
//遍历
//1.键找值遍历
System.out.println("----------键找值遍历----------");
Set<Student> keys = hm.keySet();
for (Student key : keys) {
String value = hm.get(key);
System.out.println(key + " = " + value);
}
//2.键值对遍历
System.out.println("----------键值对遍历----------");
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
Student key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
//3.Lambda表达式遍历
System.out.println("----------Lambda表达式遍历----------");
hm.forEach((student, s) ->System.out.println(student + " = " + s));
}
}
运行结果:
小练习2
需求:某个班级30名学生,现在需要组成秋游活动,班长提供了四个景点依次是A、B、C、D,每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多
代码演示
public class HashMapTest2 {
public static void main(String[] args) {
/*
需求:某个班级30名学生,现在需要组成秋游活动,
班长提供了四个景点依次是A、B、C、D,
每个学生只能选择一个景点,
请统计出最终哪个景点想去的人数最多
*/
//创建集合
HashMap<String,Integer> hm = new HashMap<>();
//生成三十个随机的景点表示学生选择的景点,并存入集合
ArrayList<String> list = new ArrayList<>();
String[] arr = {"A","B","C","D"};
Random r = new Random();
for (int i = 0; i < 30; i++) {
int index = r.nextInt(4);
String name = arr[index];
list.add(name);
}
//要统计的东西较多,使用Map集合进行统计
for (int i = 0; i < list.size(); i++) {
//判断当前的元素在HashMap中是否存在
if(hm.containsKey(list.get(i))) {
//如果存在,这个key的value加1
int newValue = hm.get(list.get(i)) + 1;
hm.put(list.get(i),newValue);
} else {
//如果不存在,那么添加进去value置为1
hm.put(list.get(i),1);
}
}
//遍历
System.out.println(hm);
//找到选择次数的最大值
int max = 0;
Set<Map.Entry<String, Integer>> entries = hm.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
int times = entry.getValue();
if(max < times) {
max = times;
}
}
//输出次数等于最大次数的景点
System.out.print("想去的人最多的景点为:");
for (Map.Entry<String, Integer> entry : entries) {
int times = entry.getValue();
if(max == times) {
System.out.print(entry.getKey() + " ");
}
}
}
}
运行结果:
2.LinkedHashMap
特点:
由键决定:有序、无重复、无索引
这里的有序指的是保证存储和取出的元素顺序一致
原理:
底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储的顺序(详细了解可通过前文LinkedHashSet内容)
3.TreeMap
特点:
由键决定:不重复、无索引、可排序
这里的可排序是指对键进行排序(默认按照键的大小从小到大进行排序,也可以自己规定键的排序规则)
原理:
与TreeSet一样,底层数据结构都为红黑树,增删改查性能都较好
代码书写两种排序规则(同TreeSet):
1.实现Comparable接口,指定比较规则。
2.创建集合时传递Comparator比较器对象,指定比较规则。
小练习1:
需求:
键:整数表示id
值:字符串表示商品名称
要求1:按照id的升序排列(默认情况下TreeMap对Integer类型的键值就从小到大排序)
要求2:按照id的降序排列
代码演示:
public class TreeMapDemo1 {
public static void main(String[] args) {
/*
需求:
键:整数表示id
值:字符串表示商品名称
要求1:按照id的升序排列
要求2:按照id的降序排列
*/
//要求1:升序
//需要排序,创建TreeMap对象
TreeMap<Integer,String> tm1 = new TreeMap<>((o1, o2) -> o1 - o2);
//添加元素
tm1.put(2,"phone");
tm1.put(3,"computer");
tm1.put(1,"pen");
//输出结果
System.out.println(tm1);
//要求2:降序
//创建TreeMap对象
TreeMap<Integer,String> tm2 = new TreeMap<>((o1, o2) -> o2 - o1);
//添加元素
tm2.put(1,"pen");
tm2.put(3,"computer");
tm2.put(2,"phone");
//输出结果
System.out.println(tm2);
}
}
运行结果:
小练习2:
需求2:
键:学生对象
值:籍贯
要求:按照学生年龄的升序排列,
年龄一样按照姓名的字母排列,
同姓名年龄视为同一个人
代码演示:
Student类:
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public int compareTo(Student o) {
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
测试类TreeMapDemo2:
public class TreeMapDemo2 {
public static void main(String[] args) {
/*
需求2:
键:学生对象
值:籍贯
要求:按照学生年龄的升序排列,
年龄一样按照姓名的字母排列,
同姓名年龄视为同一个人
*/
//创建学生对象
Student s1 = new Student("zhao",21);
Student s2 = new Student("han",21);
Student s3 = new Student("ma",22);
Student s4 = new Student("zhao",21);
//创建TreeMap对象
TreeMap<Student,String> tm = new TreeMap<>();
//添加学习对象
tm.put(s1,"bd");
tm.put(s2,"sjz");
tm.put(s3,"hd");
tm.put(s4,"tj");
//输出结果
System.out.println(tm);
}
}
运行结果:
小练习3:
需求: 字符串“aababcabcdabcde”
统计字符串中每一个字符出现的次数,
并按照以下格式输出
输出结果:a(5)b(4)c(3)d(2)e(1)
代码演示:
public class TreeMapDemo3 {
public static void main(String[] args) {
/*
需求:
字符串“aababcabcdabcde”
统计字符串中每一个字符出现的次数,并按照以下格式输出
输出结果:a(5)b(4)c(3)d(2)e(1)
*/
String str = "aababcabcdabcde";
//需要同时存储字符和其出现的次数,采用双列集合
//需要按照次数从大到小排序,采用TreeMap
//创建TreeMap
TreeMap<Character,Integer> tm = new TreeMap<>();
//添加元素
for (int i = 0; i < str.length(); i++) {
if(tm.containsKey(str.charAt(i))) {
//若已存在 值+1
tm.put(str.charAt(i),tm.get(str.charAt(i)) + 1);
} else {
//若不存在,put 值为1
tm.put(str.charAt(i),1);
}
}
//遍历
StringBuilder sb = new StringBuilder();
tm.forEach((key, value) -> sb.append(key).append("(").append(value).append(")"));
System.out.println(sb);
}
}
运行结果:
双列集合思考:
可变参数
可变参数本质上是一个数组
作用:
在形参中接收多个数据
格式:
数据类型...参数名称(例如:int...args)
代码演示:
public class Args {
public static void main(String[] args) {
int sum1 = getSum();
int sum2 = getSum(1,2,3,4);
int sum3 = getSum(1,2,3,4,5);
System.out.println(sum1);
System.out.println(sum2);
System.out.println(sum3);
}
public static int getSum(int...args) {
int sum = 0;
for (int i = 0; i < args.length; i++) {
sum = sum + args[i];
}
return sum;
}
}
运行结果:
注意:
1.在方法的形参中最多只能写一个可变参数
2.在方法中,如果除了可变参数以外,还有其他参数,那么可变参数要写最后
综合练习:
练习一:
自动点名器1:
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
代码演示:
public class Test1 {
public static void main(String[] args) {
/*
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
*/
//第一种方法
System.out.println("第一种方法:");
ArrayList<Student> list = new ArrayList<>();
Collections.addAll(list,new Student("han",21,"m"),
new Student("ma",22,"m"),new Student("zhao",21,"m"),
new Student("wang",22,"m"),new Student("hu",22,"m"),
new Student("wu",21,"f"));
Random r = new Random();
int index = r.nextInt(list.size());
System.out.println(list.get(index).getName());
//第二种方法
System.out.println("第二种方法:");
Collections.shuffle(list);
System.out.println(list.get(0).getName());
}
}
运行结果:
练习二:
自动点名器2:
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
要求:百分之70概率随机到男生,百分之30概率随机到女生
代码演示:
public class Test2 {
public static void main(String[] args) {
/*
自动点名器2:
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
要求:百分之70概率随机到男生,百分之30概率随机到女生
*/
//存储学生信息
ArrayList<Student> list = new ArrayList<>();
Collections.addAll(list,new Student("han",21,"m"),
new Student("ma",22,"m"),new Student("zhao",21,"m"),
new Student("wang",22,"m"),new Student("hu",22,"m"),
new Student("wu",21,"f"), new Student("xiaomei",21,"f"),
new Student("xiaofang",22,"f"));
//创建集合来控制概率
ArrayList<Integer> probabilityList = new ArrayList<>();
Collections.addAll(probabilityList,1,1,1,1,1,1,1);
Collections.addAll(probabilityList,0,0,0);
Collections.shuffle(probabilityList);
int number = probabilityList.get(0);
if(number == 1) {
System.out.print("男生:");
} else {
System.out.print("女生:");
}
//创建集合分别存储男生和女生
ArrayList<Student> boyList = new ArrayList<>();
ArrayList<Student> girlList = new ArrayList<>();
if(number == 1) {
//如果要选男生
//将所有男生存入一个集合中
for (Student student : list) {
if(student.getGender() == "m") {
boyList.add(student);
}
}
//并随机抽取
Collections.shuffle(boyList);
System.out.println(boyList.get(0).getName());
} else {
//如果要选女生
//将所有女生存入一个集合中
for (Student student : list) {
if(student.getGender() == "f") {
girlList.add(student);
}
}
//并随机抽取
Collections.shuffle(girlList);
System.out.println(girlList.get(0).getName());
}
}
}
运行结果:
练习三:
自动点名器3:
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
要求:被点到的学生不会再被点到
如果班级中所有的学生都点完了,需要重新开启第二轮点名
代码演示:
public class Test3 {
public static void main(String[] args) {
/*
自动点名器3:
班级里有N个学生,学生属性:姓名,年龄,性别。
实现随机点名器
要求:被点到的学生不会再被点到
如果班级中所有的学生都点完了,需要重新开启第二轮点名
*/
//存储学生信息
ArrayList<Student> list = new ArrayList<>();
Collections.addAll(list,new Student("han",21,"m"),
new Student("ma",22,"m"),new Student("zhao",21,"m"),
new Student("wang",22,"m"),new Student("hu",22,"m"),
new Student("wu",21,"f"), new Student("xiaomei",21,"f"),
new Student("xiaofang",22,"f"));
//创建新集合存储数据
ArrayList<Student> copyList = new ArrayList<>();
copyList.addAll(list);
Scanner sc = new Scanner(System.in);
while (true) {
//点名控制界面
System.out.println("点名请输入:1,退出请输入:2");
int num = sc.nextInt();
//判断
if(num == 1) {
if(copyList.size() != 0){
//新集合长度不为0时
} else {
//新数组长度为0
copyList.addAll(list);
}
//打乱集合
Collections.shuffle(copyList);
//获取随机姓名
System.out.println(copyList.get(0).getName());
//删除被点到的学生对象
copyList.remove(0);
}
else {
//退出
break;
}
}
}
}
运行结果:
不可变集合
不可改变的集合
特点:
不可添加、不可删除、不可修改、只能查询
代码演示:
(利用List、Set、Map接口创建不可变集合,并展示查询及遍历方法)
List:(Set与List方法一致)
public class ImmutableDemo1 {
public static void main(String[] args) {
//创建集合
List<String> list = List.of("han", "ma", "zhao", "hu", "wang", "wu");
//遍历集合
//逐个遍历
System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));
System.out.println(list.get(3));
System.out.println(list.get(4));
System.out.println(list.get(5));
//普通for遍历
System.out.println("----------------------");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//迭代器
System.out.println("----------------------");
Iterator<String> it = list.listIterator();
while(it.hasNext()) {
String str = it.next();
System.out.println(str);
}
//增强for
System.out.println("----------------------");
for (String str : list) {
System.out.println(str);
}
//lambda表达式
System.out.println("----------------------");
list.forEach(str -> System.out.println(str));
}
}
运行结果:
Map:
public class ImmutableDemo2 {
public static void main(String[] args) {
//Map系列不可变集合
//JDK10以上
//先创建一个可变集合
HashMap<String,String> hm = new HashMap<>();
hm.put("111","aaa");
hm.put("222","bbb");
hm.put("333","ccc");
hm.put("444","ddd");
hm.put("555","eee");
//转为不可变集合
Map<String, String> map1 = Map.copyOf(hm);
System.out.println(map1);
//JDK10以下
Map<Object, Object> map2 = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
System.out.println(map2);
}
}
运行结果:
注意:
创建Set和Map的不可变集合时,要注意不能包含重复元素,并且按照各自集合的要求存储元素。
Map不可变集合中最多只能存储20个元素,即10个键值对,超过十个用ofEntries方法