p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解
集合基础看这里
目录
- 集合体系结构
- 单列集合
- Collection
- 各个方法的注意事项
- add()
- remove()
- contains()
- Collection三种遍历方式
- 迭代器遍历
- 增强for遍历
- lambda表达式遍历
- 匿名内部类遍历
- List系列集合
- List集合的特有方法
- remove()方法注意点
- List集合五种遍历方式
- 普通for循环遍历(因为其存在索引)
- 列表迭代器遍历
- ArrayList集合
- LinkedList集合
- Set系列集合
- 哈希表
- 哈希值
- HashSet
- HashSet的三个特点
- LinkedHashSet
- TreeSet
- Collections工具类
- 数据结构
- 双列集合
- Map
- Map的三种遍历方式
- 键找值
- 键值对
- lambda表达式遍历
- 利用Map集合进行统计
- HashMap
- 利用HashMap存储自定义对象
- 利用HashMap统计投票数
- LinkedHashMap
- TreeMap
- 利用TreeMap进行自定义对象的排序
- 利用TreeMap集合进行统计
- 可变参数
- 不可变集合
集合体系结构
集合可以分为两大类:Collection(代表单列集合)、Map(代表双列集合)
单列集合,即每次添加元素的时候只能添加一个数据
双列集合,即每次添加元素的时候添加一对(两个)数据
.
单列集合最顶层的是Collection,下面又分为两种单列集合:List、Set
List系列集合:添加的元素有序(存和取的顺序是一样的)、可重复、有索引
Set系列集合:添加的元素无序(存和取的顺序有可能一样)、不可重复、无索引
.
双列集合
单列集合
Collection
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
其方法如下
各个方法的注意事项
举例说明
add()
//使用接口的多态来创建对象
Collection<String> coll = new ArrayList<>();
System.out.println(coll.add("aaa"));
注意:如果我们往List系列集合里添加数据,那么方法永远返回true,因为List系列集合里是允许元素重复的
而如果我们往Set系列集合里添加数据,那么方法不一定返回true,因为Set系列集合里是不允许元素重复的。如果添加一个已有的数据,则会返回false,表示添加失败
想要一次添加一个集合的数据,可以使用addAll()方法
remove()
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
System.out.println(coll.remove("aaa"));
注意:由于Collection里面定义的是共性的方法,所以此时不能通过索引进行删除,
只能通过元素的对象进行删除
contains()
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
System.out.println(coll.contains("aaa"));
注意:由于contains()方法底层是依赖equals()方法来判断元素是否存在的, 如果集合里存储的是自定义对象,那么则会根据对象的地址值来判断(不会用对象的属性值来判断)
所以,如果集合里存储的是自定义对象,也想通过由于contains()方法来判断是否存在,
那么在javabean类中,一定要重写equals()方法.此时比较的就不是对象的地址值而是属性值了
Collection三种遍历方式
不能使用普通for循环是因为普通for循环需要索引,但 Set系列集合没有索引,其只能用于list系列集合
迭代器遍历
迭代器在java中的类是
Iterator,迭代器是集合专用的迭代方式
迭代器不依赖索引
获取迭代器的方法为**iterator()**方法,其会返回一个迭代器对象(默认指向当前回合的0索引)
Iterator类中有两个常用的方法
以下为一种迭代器遍历方式
import java.util.ArrayList;
import java.util.Iterator;
public class D {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
//创建一个迭代器对象,此时指针对应的索引为0
Iterator<String> it = list.iterator();
while (it.hasNext()){
//it.hasNext()判断当前指针位置是否还有元素,有为true否则为false
//it.next()获取当前指针对应元素并将指针向右移动一次
String str = it.next();
System.out.print(str+", ");
}
}
}
注意点
- 当指针指向不存在的元素时再调用next()方法的时候(即当前位置没有元素还要强行获取),会出现
NoSuchElementException
异常(即没有这个元素异常)- 迭代器遍历后,指针不会复位。想要在遍历一次的话就只能重新创建一个新的迭代器的对象
- 循环中只能用一次next方法(原因为第一点)
- 迭代器遍历时,不能用集合的方法进行增加和删除,只能调用迭代器的remove()方法进行删除,且暂时没有方法进行添加
增强for遍历
增强for的底层就是迭代器,是为了简化迭代器的代码书写的
它是在JDK5以后出现的,其内部原理就是一个Iterator迭代器
只有单列集合和数组才能用增强for进行遍历
格式
for(集合或数组的数据类型 变量名: 集合名/数组名)
System.out.println(变量名);
};
举例
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for (String s: list) {
System.out.println(s);
}
s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
所以增强for循环不改变集合或数组原来的数据
快捷键:list.for
然后回车
lambda表达式遍历
匿名内部类遍历
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
底层原理:
其实也会自己遍历集合,一次得到每一个元素
把得到的每一个元素传递给上面的accept方法
s依次表示集合中的每一个数据
通过将匿名内部类遍历简化后得到 lambda表达式遍历
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.forEach( s -> System.out.println(s));
总结:如果只想遍历的话可以用增强for和lambda表达式遍历
如果想在遍历的过程中删除元素,则使用迭代器遍历
List系列集合
List系列集合:添加的元素有序(存和取的顺序是一样的)、可重复、有索引
List集合的特有方法
由于List集合中有索引,所以其有很多特定的需要使用索引的方法
remove()方法注意点
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//此时删除的不是1这个元素,而是1索引对应的元素
list.remove(1);
//这是因为在调用方法的时候,如果方法出现了重载现象的时候
//优先调用实参和新参类型一致的那个方法
//如果想根据元素删除的话
//使用手动装箱的办法:手动将基本数据类型的1,变成Integer类型
//原理:在调用方法的时候,如果方法出现了重载现象的时候,
//优先调用实参和新参类型一致的那个方法
Integer i = Integer.valueOf(1);
list.remove(i);
//这时删除的就是元素1了
List集合五种遍历方式
由于List集合继承于Collection,所以Collection的三种遍历方式List集合都能使用
下面为List集合特有的两种遍历方式
普通for循环遍历(因为其存在索引)
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.print(list+" ");
列表迭代器遍历
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
ListIterator<String> it = list.listIterator();
while (it.hasNext()){
String str = it.next();
//在遍历的过程中添加元素
if ("b".equals(str)){
it.add("x");
}
}
System.out.println(list);
注意事项: 列表迭代器是迭代器的子接口。其在迭代器的基础上额外添加了一个方法,就是在遍历的过程中可以添加元素
总结:如果只想遍历的话可以用增强for和lambda表达式遍历
如果想在遍历的过程中删除元素,则使用迭代器遍历
如果想在遍历的过程中添加元素,则使用列表迭代器遍历
如果想在遍历的过程中操作索引,则使用普通for遍历
ArrayList集合
底层原理:ArrayList是利用空参创建的集合,在底层创建一个默认长度为0的数组,其名字为elementData。有一个成员变量size用来记录成员个数
当添加第一个元素的时候,底层会创建一个新的长度为10的数组。size不仅记录了元素个数,还指定了元素的下次存入位置
当数组存满的时候,会扩容1.5倍,即创建一个新的长度为原数组1.5倍的数组,然后再将所有元素拷贝到新数组当中
如果一次添加多个元素,1.5倍还放不下,则新创建的数组以实际情况为准
LinkedList集合
底层数据结构是双链表,查询慢,增删快。
如果操作的是首尾元素,速度也是极快的。所以其多了很多操作首尾数据的API
Set系列集合
Set系列集合:添加的元素无序(存和取的顺序有可能一样)、不可重复、无索引
无序:存取顺序不一致
不重复:可以去除重复
无索引:没有带索引的方法。所以不能通过普通for循环遍历,也不能通过索引获取元素
.
Set系列的实现类
HashSet:无序、不重复、无索引
LinkHashSet:有序、不重复、无索引
TreeSet:可排序、不重复、无索引
.
Set接口的方法基本上与Collection的API一致 。因此其方法的注意事项与上方一致
由于其没有索引,所以其遍历方式只有增强for遍历、迭代器遍历、lambda表达式遍历三种方式
哈希表
在学HashSet集合以前我们要先学习哈希表
哈希表是一种对于增删改查数据性能都较好的结构
JDK8以前哈希表是以数组+链表组成的
JDK8开始哈希表是以数组+链表+红黑树组成的
.
哈希值
哈希表有一个很重要的值:哈希值
哈希值,即对象的整数表现形式
哈希表的底层是以数组形式存在的,存储数据对应的索引是根据int index = (数组长度-1) & 哈希值;
计算出数据在数组中对应的索引。因此想要存储一个对象,我们就要获取其整数形式才能计算出它对应的索引,而这个整数形式即为哈希值
哈希值是对象根据hashCode方法算出来的int类型的整数
hashCode方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
一般情况下我们会重写hashCode方法,利用对象内部的属性值计算哈希值
.
注意:
如果没有重写hashCode方法,不同对象计算出来的哈希值是不一样的
如果已经重写hashCode方法,不同对象只要属性值相同,计算出来的哈希值就是一样的
在小部分情况下,不同的属性值或地址值计算出来的哈希值也有可能一样,这种情况称为哈希碰撞
举例说明
import java.util.Objects;
public class Demo1 {
public static void main(String[] args) {
//创建对象
Student student1 = new Student("张三",18);
Student student2 = new Student("张三",18);
//如果没有重写hashCode方法,不同对象计算出来的哈希值是不一样的
//如果已经重写hashCode方法,不同对象只要属性值相同,计算出来的哈希值就是一样的
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
//在小部分情况下,不同的属性值或地址值计算出来的哈希值也有可能一样,这种情况称为哈希碰撞
System.out.println("abc".hashCode());
System.out.println("acD".hashCode());
}
}
//==============================================
class Student{
private String name;
private int age;
//重写equals()方法和hashCode()方法
@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 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;
}
}
HashSet
HashSet集合的底层是采取哈希表来存储数据的
新元素挂在老元素下面形成链表
当数组存储的元素等于加载因子*数组长度的数量时,数组会扩容成原来的两倍
当链表的长度大于8且数组长度大于等于64的时候,那么该链表则会自动转换为红黑树,从而提高了查找效率
如果集合中存储的是自定义对象,则必须要重写hashCode()和equals()方法,通过属性值来计算哈希值或者进行比较
HashSet的三个特点
- HashSet为什么存或取的顺序不一样(即无序)?
解释:HashSet在遍历的时候时从0索引开始遍历的,而存储数据则是通过哈希值来存储的
- HashSet为什么没有索引
因为其底层是由数组、链表、红黑树组成的,于是不好规定索引
- HashSet是利用什么机制保证去重的?
hashCode()方法和equals()方法。先利用hashCode()方法获得哈希值,再用equals()方法来比较对象内部的属性值是否相同
LinkedHashSet
LinkedHashSet是HashSet的子类,其特点为有序、不重复、无索引
这里的有序是保证存储和取出的数据顺序是一致的
其底层数据结构依然是哈希表,只是每个元素又额外多了一个双向链表的机制记录存储顺序
红色双向箭头即为双向链表
在以后要数据去重时,默认使用HashSet。如果要求去重且存取有序,这时才使用LinkedHashSet(因为HashSet用来去重效率更高)
TreeSet
TreeSet:可排序、不重复、无索引
可排序:按照元素的默认规则排序
其底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
TreeSet集合排序的默认规则
整数:从小到大
字符、字符串:根据字符在ASCII码表中的数字升序进行排序,字符串则是从首字符开始比较然后依次向后比较,而且长度较小的在前面
自定义对象:
//存储整数并排序
TreeSet<Integer> ts = new TreeSet<>();
ts.add(1);ts.add(3);ts.add(2);
ts.add(6);ts.add(4);ts.add(5);
ts.add(9);
System.out.println(ts);
对于自定义对象,TreeSet有两种排序方式
第一种:默认排序、自然排序:使javabean类实现Comparable接口指定比较规则
如下
package study.TreeSet;
import java.util.TreeSet;
public class Student implements Comparable<Student>{
//Comparable<Student>:指明比较的数据类型
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
//指定排序的规则
//只看年龄,想要通过年龄的升序进行排列
int result = this.age - o.age;
//this表示当前要添加的元素
//o表示已经在红黑树中的元素
return result;
//返回值若为负数:认为当前要添加的元素是较小的,存在左边
//返回值若为负数:认为当前要添加的元素是较大的,存在右边
//返回值若为0:认为当前要添加的元素存在的,舍弃
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//=====================================================
class Test{
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>();
Student student1 = new Student("a", 18);
Student student2 = new Student("b", 25);
Student student3 = new Student("c", 15);
ts.add(student1);
ts.add(student2);
ts.add(student3);
System.out.println(ts);
}
}
输出
[Student{name='c', age=15}, Student{name='a', age=18}, Student{name='b', age=25}]
第二种排序方式:比较器排序。即在创建TreeSet方向的时候,传递比较器Comparator制定规则
使用原则:默认使用第一种,如果无法满足需求就用第二种(比如想要自定义先比较长度在比较首字母)
import java.util.Comparator;
import java.util.TreeSet;
public class Demo1 {
public static void main(String[] args) {
//存储整数并排序
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
//创建了一个比较器对象
@Override
//o1:当前要添加的元素
//o2:已经在红黑树中存在的元素
public int compare(String o1, String o2) {
//按照长度排序
int i = o1.length()-o2.length();
i = i==0 ? o1.compareTo(o2):i;
//返回值若为负数:认为当前要添加的元素是较小的,存在左边
//返回值若为负数:认为当前要添加的元素是较大的,存在右边
//返回值若为0:认为当前要添加的元素存在的,舍弃
return i;
}
});
ts.add("aaa");ts.add("aba");ts.add("aa");
ts.add("cd");ts.add("cbd");
System.out.println(ts);
}
}
Collections工具类
Collections是一个集合工具类
其有如下API
数据结构
要想分清楚底层的实现类有什么不同,我们先要了解底层的数据结构
数据结构是计算机存储、组织数据的方式
数据结构是计算机底层存储、组织数据的方式,是指数据之间是以什么方式排列在一起的
数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择
使用合理的数据结构可以带来更高的运行效率或者存储效率
常见的数据结构包括栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树
双列集合
双列集合,即每次添加元素的时候添加一对(两个)数据
一对数据的左边数据称为键(不可重复),右边称为值(可以重复)
键和值之间是一一对应的关系。这一对数据称为键值对或键值对对象或Entry对象
Map
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
所有Map集合的特点都是由键决定的,与值无关
常用方法
Put()方法
//创建Map集合的对象
Map<String,String> map = new HashMap<>();
//添加元素
map.put("张三","学生");
map.put("李四","老师");
//put方法的细节
//put有两个作用:添加/覆盖
//在添加数据的时候,如果键不存在,那么直接把键值对对象添加到集合中,方法返回null
//在添加数据的时候,如果键存在,那么会把原有的键值对对象覆盖,并把被覆盖的值进行返回
String str = map.put("李四","辅导员");
System.out.println(str);
System.out.println(map);
remove()方法
//创建Map集合的对象
Map<String,String> map = new HashMap<>();
//添加元素
map.put("张三","学生");
map.put("李四","老师");
//成功删除返回true,否则返回false
Boolean b = map.remove("张三","学");
System.out.println(b);
System.out.println(map);
Map的三种遍历方式
键找值
思路:把所有的键都获取出来放到一个单列集合中,再遍历单列集合依次得到每一个键,再通过get()方法依次获取每一个键所对应的值
//创建Map集合的对象
Map<String,String> map = new HashMap<>();
//添加元素
map.put("张三","学生");
map.put("李四","老师");
//获取所有的键,把这些键放到一个单列集合中
Set<String> keys = map.keySet();
//遍历单列集合,得到每一个键
for (String key : keys) {
//利用键获取对应的值
System.out.println(key + "="+map.get(key));
}
键值对
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo2 {
public static void main(String[] args) {
//创建Map集合的对象
Map<String,String> map = new HashMap<>();
//添加元素
map.put("张三","学生");
map.put("李四","老师");
//通过键值对对象进行遍历
//通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//遍历entries这个集合,去得到里面的每一个键值对对象
for (Map.Entry<String, String> entry : entries) {
//利用entry调用get()方法获取键和值
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
lambda表达式遍历
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class Demo3 {
public static void main(String[] args) {
//创建Map集合的对象
Map<String,String> map = new HashMap<>();
//添加元素
map.put("张三","学生");
map.put("李四","老师");
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System.out.println(key+" "+value);
}
});
System.out.println("=============================");
map.forEach((String key, String value)-> {
System.out.println(key+" "+value);
}
);
System.out.println("=============================");
//lambda表达式遍历
map.forEach(( key, value)-> System.out.println(key+" "+value));
}
}
利用Map集合进行统计
当我们想统计一些数据时,最简便的可以使用计数器思想
但是当要统计的数据比较多的时候,使用计数器思想会非常低效率
这时我们便可以用HashMap或TreeMap进行统计
两种集合的使用情况有所不同
如果题目没有要求对结果进行排序:那么优先使用HashMap(效率更高)
如果题目要求对结果进行排序:那么使用TreeMap
HashMap
HashMap是Map里面的一个实现类。其没有特有的方法,只继承了Map里面的方法
特点:无序、不重复、无索引
其底层与HashSet一样,都是哈希表结构
其哈希值的计算与键有关,与值无关
利用HashMap存储自定义对象
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class Demo4 {
public static void main(String[] args) {
//创建一个HshMap集合,键是学生对象,值是籍贯
//存储后遍历
//要求:同姓名、同年龄的被认为是一个学生
//核心:HashMap的键位置如果存储的是自定义对象,需要重写hasnCode和equals方法
Student student1 = new Student("张三",18);
Student student2 = new Student("李四",23);
Student student3 = new Student("张三",18);
HashMap<Student,String> hm = new HashMap<>();
hm.put(student1,"四川");
hm.put(student2,"上海");
hm.put(student3,"北京");
//--------------------------------------
Set<Student> keys = hm.keySet();
for (Student key : keys) {
System.out.println(key+" "+hm.get(key));
}
System.out.println("=========================");
Set<Map.Entry<Student, String>> entries = hm.entrySet();
for (Map.Entry<Student, String> entry : entries) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
System.out.println("=========================");
hm.forEach(( student, s)-> System.out.println(student+" "+s));
}
}
//==================================================
class Student{
private String name;
private int age;
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", 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;
}
}
利用HashMap统计投票数
import java.util.*;
public class Demo5 {
public static void main(String[] args) {
//一个班级80名学生,现在需要组成秋游活动,班长提供了四个景点(A、B、C、D)
//每个学生只能选择一个景点,请统计最终哪个经典想去的人数最多
//定义一个数组,存储4个景点
String[]arr = {"A","B","C","D"};
//利用随机数模拟88个同学的投票,并把投票的结果存储起来
ArrayList<String> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 80; i++) {
int index = random.nextInt(arr.length);
list.add(arr[index]);
}
//如果要统计的东西比较多,此时不方便使用计数器思想
//这时我们可以定义map集合,利用集合进行统计
HashMap<String,Integer> hm = new HashMap<>();
for (String name : list) {
//判断当前景点在map集合里是否存在
if (hm.containsKey(name)){
//存在
//先获取当前景点已经被投票的次数
int count = hm.get(name);
//表示当前景点又被投了一次
count++;
//把投的次数再次添加到集合
hm.put(name,count);
}else {
hm.put(name,1);
}
}
System.out.println(hm);
//求最大值
int max = 0;
Set<Map.Entry<String, Integer>> entries = hm.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
int count = entry.getValue();
max = Math.max(count,max);
}
//判断哪个景点的次数和最大值一样,若一样则打印出来
for (Map.Entry<String, Integer> entry : entries) {
int count = entry.getValue();
if (count==max){
System.out.println(entry.getKey());
}
}
}
}
LinkedHashMap
LinkedHashMap是HashMap的子类
特点:有序、不重复、无索引
有序是指元素取出的顺序和存储的顺序一致
其底层数据结构依然是哈希表,只是每个键值对元素之间有额外多了一个双链表的机制记录存储的顺序
TreeMap
TreeMap和TreeSet集合一样,底层都是红黑树结构
特点:可排序、不重复、无索引
可排序:对键进行排序
其排序规则和TreeSet一致
利用TreeMap进行自定义对象的排序
import java.util.*;
public class Demo6 {
public static void main(String[] args) {
//键:学生对象
//值:籍贯
//需求:按照学生年龄的升序排列,年龄一样的按照姓名的字母排列
//同姓名年龄的视为同一个人
TreeMap<Student1,String> tm = new TreeMap<>();
Student1 student1 = new Student1(18,"张三");
Student1 student2 = new Student1(20,"李四");
Student1 student3 = new Student1(20,"王五");
Student1 student4 = new Student1(18,"张三");
tm.put(student1,"北京");
tm.put(student2,"上海");
tm.put(student3,"四川");
tm.put(student4,"广东");
Set<Map.Entry<Student1, String>> entries = tm.entrySet();
for (Map.Entry<Student1, String> entry : entries) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
class Student1 implements Comparable<Student1>{
private int age;
private String name;
public Student1() {
}
@Override
public String toString() {
return "Student1{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student1 student1 = (Student1) o;
return age == student1.age && Objects.equals(name, student1.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
public Student1(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Student1 o) {
//根据学生的年龄的升序排列,年龄一样的按照姓名的字母排列,同姓名同年龄的视为同一个人
//this:表示当前要添加的元素
//o:表示已经在红黑树中存在的元素
//返回值
//负数:表示当前要添加的元素是小的,在左边
//正数:表示当前要添加的元素是大的,在右边
//0:表示当前要添加的元素已经存在,舍去
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
利用TreeMap集合进行统计
import java.util.ArrayList;
import java.util.StringJoiner;
import java.util.TreeMap;
import java.util.function.BiConsumer;
public class Demo7 {
public static void main(String[] args) {
//字符串"aababcabcdabcde"
//统计字符串中每一个字符出现的次数,并进行排序
String str = "aababcabcdabcde";
TreeMap<Character,Integer> tm = new TreeMap<>();
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (tm.containsKey(ch)){
//存在
//先获取当前字符出现的次数
int count = tm.get(ch);
//表示当前景点又被投了一次
count++;
//把投的次数再次添加到集合
tm.put(ch,count);
}else {
tm.put(ch,1);
}
}
//遍历集合,并按照指定格式进行拼接
//第一种
StringBuilder sb = new StringBuilder();
tm.forEach(( key, value)-> sb.append(key).append("(").append(value).append(") "));
System.out.println(sb);
System.out.println();
//第二种
StringJoiner sj = new StringJoiner("","","");
tm.forEach(((key, value) -> sj.add(key+"").add("(").add(value+"").add(") ")));
System.out.println(sj);
}
}
可变参数
可变参数,即参数的数量是不固定的
格式:数据类型...参数名
举例
public class Demo8 {
public static void main(String[] args) {
System.out.println(getSum(1,2));
System.out.println(getSum(1,2,3,5));
System.out.println(getSum());
}
public static int getSum(int...num){
int sum = 0;
for (int i : num) {
sum = sum +i;
}
return sum;
}
}
注意事项
在方法的形参处最多只能写一个形参变量
在方法当中如果除了可变参数外还有别的形参,那么可变参数要写在最后
.
可变参数其实就是一个数组
其作用为在形参中接收多个数据
不可变集合
顾名思义,不可变集合就是不可以被修改的集合。
一旦创建完毕后,长度不能改、内容也不能改
应用场景
1.如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是一个很好的实践
2.当集合对象被不可信的库调用的时候,不可变形式是安全的
说白了,就是我不相信其他人,我不想让别人修改集合的内容,别人只能进行只读操作
书写格式
注意:
当我们要获取一个不可变的Set集合的时候,里面的参数要保证唯一性,否则对该集合进行只读操作会报错
Map的不可变集合的键是不能重复的。并且Map里面的of方法是有参上限的,最多只能传递20个参数,10个键值对(因为一个方法的形参里面只能有两个可变参数)
当我们想传递多于10个键值对对象的时候,我们可以使用ofEntries方法
我们也可以使用copyof方法将一个可变集合变成不可变集合