# 集合进阶
# 单列集合顶层接口
# 集合体系结构
- 单列集合:在添加元素时,一次只能添加一个元素。
- 双列集合:在添加元素时,可以一次添加一对数据。
# 单列集合
- 单列集合最顶层的为Collection接口,该接口定义了集合的最基本操作。
# Collection 接口可分为两种单列集合
list系列集合:ArrayList、LinkedList、Vectorset系列集合:HashSet(LinkedHashSet)、TreeSet
# list系列集合
- 添加的元素是有序的、可重复的、有索引的
# set系列集合
- 添加的元素是无序的、不可重复的(可以进行数组去重)、无索引的(不能通过索引获取值)
# Collection是单列集合的祖宗接口,他的功能是全部单列集合都可以继承使用的
| 方法名称 | 描述 |
|---|---|
public boolean add(E e) | 添加元素 |
public void clear() | 清空集合中的所有元素 |
public boolean remove(Object o) | 把给定的对象在当前集合中删除 |
public boolean contains(Object o) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 获取当前集合中元素的个数 |
注意
Collection是一个接口,我们不能直接创建它的实现类对象
所以我们现在学习他的方法时只能创建他的实现类的对象
package ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class CollectionText {
public static void main(String[] args) {
//目的:为了学习他的方法
//自己在做一些练习的时候,还是按照之前的方式去创建对象
Collection<String> c = new ArrayList<>();
//1.添加元素
//细节1:如果我们要往list系列集合中添加数据,那么方法永远返回true,因为list系列的是允许元素重复的
//细节2:如果我们要往set系列集合中添加数据,如果要添加的元素在集合中不存在,那么方法返回true,表示添加成功
// 如果当前要添加的元素已经存在,方法返回false,表示添加失败
c.add("hello");
System.out.println(c);//[hello]
//2.清空
// c.clear();
System.out.println(c);
//3.删除
//注意:因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除,只能通过元素的对象进行删除
//方法会有一个共性的返回值,如果删除成功返回true,删除失败返回false
//如果要删除的元素不存在,就会删除失败
c.remove("hello");
System.out.println(c);//[]
//4.判断元素是否包含
boolean result = c.contains("hello");
System.out.println(result);//false
//细节: 底层是依靠Equals方法进行判断是否存在的
//所以:如果集合中存储的是自定义对象,也想通过contains方法判断元素是否包含,那么在javaBean中,一定要重写equals方法
Collection<Student> students = new ArrayList<>();
students.add(new Student("张三", 18));
students.add(new Student("李四", 19));
//姓名和年龄一样我们就认为是同一个人
//javabean类中重写了equals方法后
System.out.println(students.contains(new Student("张三", 18)));//true
System.out.println(students.contains(new Student("李四", 19)));//true
//判断集合长度
System.out.println(students.size());//2
}
}
class Student {
String name;
int age;
public Student(String name, int 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 age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
# collection的遍历方式
- 迭代器遍历
- 增强
for遍历 lambda表达式遍历
# 迭代器遍历
迭代器在java中的类是Iterator,迭代器是集合专用的遍历方式
# Collection集合获取迭代器
| 方法名称 | 描述 |
|---|---|
Iterator<E> iterator() | 返回迭代器对象 ,默认指向当前集合的0索引 |
# Iterator迭代器常用方法
| 方法名称 | 说 明 |
|---|---|
boolean hasNext() | 判断当前位置是否有元素,有元素返回true,否则返回false |
E next() | 返回当前位置的元素,并将迭代器指向下一个位置 |
public class IteratorText {
public static void main(String[] args) {
//获取迭代器对象
Iterator<Student> iterator = students.iterator();
//迭代器就好比是一个箭头,默认指向集合的0索引处
//利用循环获取集合中的每一个元素
while (iterator.hasNext()) {
//next方法的两件事情 获取元素并移动指针
Student student = iterator.next();
System.out.println(student.name + " " + student.age);
}
//当循环结束之后,迭代器的指针已经指向了最后没有元素的位置
// System.out.println(iterator.next());//java.util.NoSuchElementException: NoSuchElementException
//迭代器遍历完毕后,指针不会复位
//循环中只能使用一次next方法
//在迭代器遍历时,不能用集合的方法进行增加或者删除
}
}
注意
迭代器的四个细节
- 如果当前位置没有元素,还要强行获取,会报
NoSuchElementException - 如果迭代器遍历完毕,指针不会复位
- 循环中只能用一次
next方法 - 迭代器遍历时,不能用集合的方法进行增加或者删除
# 增强for遍历
- 增强for的底层就是迭代器,为了简化迭代器的代码书写的
- 他是jdk5之后出现的,其内部原理就是一个
iterator迭代器 - 所有的单列集合和数组才能使用增强for进行遍历
public class ForEachText {
public static void main(String[] args) {
//增强for遍历
Collection<String> c1 = new ArrayList<>();
c1.add("hello");
c1.add("world");
//注意
//s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
//增强for的快捷生成方式 集合名字 + for 回车
for (String s : c1) {
System.out.println(s);
}
//修改增强for中的变量,不会改变集合中原本的数据
for (String s : c1) {
s = "java";
}
//因为s是一个第三方变量,修改了s,不会影响集合中的数据
System.out.println(c1);//[hello, world]
}
}
# lambda表达式遍历
得益于jdk8开始的新技术lambda表达式,提供了一种更简单、更直接的遍历的和的方式
| 方法名称 | 描 述 |
|---|---|
default void forEach(Consumer<? super T> action): | 结合lambda遍历集合 |
public class LambdaText {
public static void main(String[] args) {
Collection<String> c2 = new ArrayList<>();
c2.add("hello");
c2.add("world");
//利用匿名内部类的方式进行遍历
//方法的底层也会自己遍历集合,依次得到集合中的每一个元素
//把得到的每一个元素,传递给下面的accecp方法
//s依次表示集合中的每一个数据
c2.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s + "匿名内部类");
}
});
//利用lambda表达式进行遍历
c2.forEach(s -> System.out.println(s + "lambda表达式"));
}
}
# 三种遍历方式
- 迭代器:在遍历过程中需要删除元素,请使用迭代器
- 增强
for、lambda:如果在遍历过程中仅仅需要遍历,那么使用增强for或lambda即可
# list集合
- 有序:存和取的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
# list集合特有的方法
Collection的方法List都继承了List集合因为有索引,所以多了很多索引操作的方法
| 方法名称 | 说明 |
|---|---|
void add (int index, E element) | 在指定位置插入元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E get(int index) | 返回指定索引处的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public class ListText {
public static void main(String[] args) {
//list系列集合
List<String> list = new ArrayList<>();
//1.添加元素
list.add("hello");
list.add("world");
//在指定位置添加元素
//细节:原来索引上的元素会依次往后移
list.add(1, "java");
//2.删除指定索引位置的元素 返回被删除的元素
//细节:删除时优先删除实参和形参类型一致的那个方法
list.remove(1);
System.out.println(list);//[hello, java]
//3.修改指定索引位置的元素 返回被修改的元素
list.set(1, "修改");
System.out.println(list);//[hello, 修改]
//4.返回指定索引位置的元素
System.out.println(list.get(1));//修改
}
}
# list集合的遍历
public class ListText {
public static void main(String[] args) {
//list集合的遍历
List<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
list1.add("java");
//1.通过迭代器遍历
Iterator<String> iterator = list1.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
System.out.println(s);
}
//2.列表迭代器
ListIterator<String> listIterator = list1.listIterator();
while (listIterator.hasNext()) {
String s = listIterator.next();
System.out.println(s);
}
//3.增强for遍历
for (String s : list1) {
System.out.println(s);
}
//4.lambda表达式
list1.forEach(s -> System.out.println(s));
//普通for循环
for (int i = 0; i < list1.size(); i++) {
System.out.println(list1.get(i));
}
}
}
# 五种遍历方式对比
- 迭代器遍历:在遍历过程中需要删除元素,请使用迭代器
- 在遍历过程中需要添加元素,请使用列表迭代器
- 增强
for、lambda:如果在遍历过程中仅仅需要遍历,那么使用增强for或lambda即可 - 普通
for循环:在遍历过程中,想操作索引,可以用普通for循环
# linkList集合
- 底层数据结构是双链表,查询慢、增删快,但是如果操作的是首尾元素,速度也是极快的
linkLIst本身多了很多直接操作首尾元素特有的API
| 特有方法 | 说明 |
|---|---|
public void addFirst(E e) | 在链表头部添加元素 |
public void addLast(E e) | 在链表尾部添加元素 |
public E getFirst() | 获取链表第一个元素 |
public E getLast() | 获取链表最后一个元素 |
public E removeFirst() | 删除链表第一个元素 |
public E removeLast() | 删除链表最后一个元素 |
# 数据结构的特点和作用
- 栈:后进先出,先进后出
- 队列:先进先出,后进后出
- 数组:内存连续区域,查询快,增删慢
- 链表:元素是游离的,查询慢,首位操作极快
# 泛型深入
- 泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
- 泛型格式:<数据类型>
注意
泛型中只能写引用数据类型
# 没有泛型的时候 集合如何存储数据
public class GenericText {
public static void main(String[] args) {
//泛型
//没有泛型的时候,集合如何存储数据
//如果我们没有给集合指定类型,默认会认为所有的子类类型都是Object
//此时可以往集合中添加任意的数据类型
//带来一个坏处 我们在获取数据的时候 无法使用他的特有行为
//创建集合对象
List list2 = new ArrayList();
//添加数据
list2.add("hello");
list2.add(123);
list2.add(new Student("张三", 18));
//遍历集合中的每一个元素
for (Object o : list2) {
System.out.println(o);
}
}
}
# 泛型的细节
- 泛型中不能写基本数据类型
- 指定泛型的基本数据类型后,传递数据时,可以传入该类类型或者其子类类型
- 如果不写泛型,类型默认是Object
# 泛型类
- 当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类
格式:修饰符 class 类名<泛型变量>{} - 泛型变量可以理解为变量,但是不是用来记录数据的,而是用来记录类型的
package ArrayList;
import java.util.Arrays;
//泛型类
public class MyArrayList<E> {
//创建一个长度为10的数组
Object[] data = new Object[10];
int size;
public Boolean add(E e) {
data[size++] = e;
return true;
}
public E get(int index) {
return (E) data[index];
}
@Override
public String toString() {
return Arrays.toString(data);
}
}
# 泛型类使用
public class GenericText {
public static void main(String[] args) {
//泛型类
MyArrayList<String> myArrayList = new MyArrayList<>();
myArrayList.add("hello");
System.out.println(myArrayList.toString());
}
}
# 泛型方法的使用
方法中形参类型不确定时,可以使用类名后面定义的泛型<E>
- 使用类名后面定义的泛型(所有方法都能用)
- 在方法申明上定义自己的泛型(只有本方法能用)
package ArrayList;
public class MyArrayList {
public <E> boolean add(E e) {
data[size++] = e;
return true;
}
}
# 泛型接口
格式:修饰符 interface 接口名<泛型变量>{}
//泛型接口的两种使用方式
//1.实现类给出具体的类型
MyArrayList2<String> myArrayList2 = new MyArrayList2<>();
myArrayList2.
add("hello");
//myArrayList2.add(123);
System.out.
println(myArrayList2);
//2.实现类延续泛型 创建实现类对象时再确定类型
public static class MyArrayList2<E> implements List<E> {
//...
}
# 泛型的继承和通配符
泛型不具备继承性,但是数据具备继承性
泛型里面写的是什么类型,那么只能传递什么类型的数据
泛型方法有一个弊端 此时他可以接收任意的数据类型
如果希望方法只能接收A、B、C类型,此时就可以使用泛型的通配符
?表示不确定的类型
也可以进行类型限定
?extends E:表示可以传递E或者E所有的子类类型?super E:表示可以传递E或所有E的父类类型
# 应用场景
- 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口
- 如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型通配符
# 泛型通配符
- 关键点:可以限定类型的范围
public class GenericText {
public static void main(String[] args) {
//要求1:该方法能养所有品种的猫,但不能养狗
//猫类
ArrayList<LHMCat> list3 = new ArrayList<>();
ArrayList<PersianCat> list4 = new ArrayList<>();
keepPet1(list3);
keepPet1(list4);
//狗类
ArrayList<HSDog> list5 = new ArrayList<>();
keepPet1(list5);//报错
//要求2:该方法能养所有品种的狗,但不能养猫
ArrayList<HSDog> list6 = new ArrayList<>();
keepPet2(list6);
ArrayList<LHMCat> list7 = new ArrayList<>();
keepPet2(list7);//报错
//要求3:该方法能养所有的动物,但是不能传递其他类型
ArrayList<Student> list8 = new ArrayList<>();
keepPet3(list8);//报错
ArrayList<Cat> list9 = new ArrayList<>();
keepPet3(list9);
}
}
class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("动物在吃东西");
}
}
//猫类
abstract class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
//方法重写
public abstract void eat();
}
//波斯猫类
class PersianCat extends Cat {
public PersianCat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("我是一只叫做" + this.name + "的波斯猫,正在吃小饼干");
}
}
//狸花猫类
class LHMCat extends Cat {
public LHMCat(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("我是一只叫做" + this.name + "的狸花猫,正在吃鱼");
}
}
//狗类
abstract class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
//方法重写
public abstract void eat();
}
//泰迪类
class TeddyDog extends Dog {
public TeddyDog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("我是一只叫做" + this.name + "的泰迪,正在吃泰迪狗粮");
}
}
//哈士奇类
class HSDog extends Dog {
public HSDog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("我是一只叫做" + this.name + "的哈士奇,正在拆家");
}
}
# 数据结构(树)
- 度:每一个节点的子节点数量(二叉树中,任意节点的度<=2)
- 树高:树的总层数
- 根节点:树的总层数
- 根节点:最顶层的节点
- 左子节点:左下方的节点
- 右子节点:右下方的节点
# 二叉查找树(又称二叉排序树或者二叉搜索树)
特点:
- 每个节点上最多有两个子节点
- 任意节点左子树上的值都小于当前节点
- 任意节点右子树上的值都大于当前节点
- 添加节点规则:小的存左边,大的存右边,一样的不存
# 二叉查找树的遍历方式
- 前序遍历:先访问根节点,再访问左子树,最后访问右子树(根左右)
- 中序遍历:先访问左子树,再访问根节点,最后访问右子树(左根右)
- 后序遍历:先访问左子树,再访问右子树,最后访问根节点(左右根)
- 层序遍历:按层遍历,从上到下,从左到右
# 二叉查找树的弊端
- 添加元素时有可能会出现左右节点不平衡的情况
# 平衡二叉树
- 规则:任意节点左右子树高度差不超过1
# 平衡二叉树的旋转机制
- 规则一:左旋
- 规则二:右旋
- 触发时机:当添加一个节点以后,该树不再是一颗平衡二叉树
# 平衡二叉树旋转机制
- 确定支点:从添加的节点开始,不断的往父节点找不平衡的节点 步骤
- 以不平衡的点作为支点
- 把支点左旋降级,变成左子节点
- 晋升原来的右子节点
# 双列集合
特点:
- 双列集合一次需要存储一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键+值这个整体我们称之为键值对或者键值对对象,在java中叫做“Entry”对象
# 双列集合map
map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
# 方法
| 方法名称 | 描述 |
|---|---|
V put(K key,V value) | 添加键值对 |
V remove(Object key) | 删除键对应的键值对 |
void clear() | 移除所有键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 获取集合中键值对的个数 |
package MapList;
import java.util.HashMap;
import java.util.Map;
public class MapList {
public static void main(String[] args) {
//双列集合
//创建map集合对象
Map<String, String> map = new HashMap<>();
//添加元素
//在添加数据的时候,如果键不存在,那么直接把键值添加到map集合当中
//在添加数据的时候,如果键是存在的,那么会把原有键值对对象覆盖,会把覆盖的值进行返回
map.put("张三", "男");
map.put("李四", "女");
map.put("王五", "男");
//3。打印集合
System.out.println(map);
//按照键删除元素
String RemoveResult = map.remove("张三");
//删除结果
//返回删除的值
System.out.println("删除结果" + RemoveResult);//删除结果男
System.out.println(map);//{李四=女, 王五=男}
//清空集合
map.clear();
System.out.println(map);//{}
//判断是否包含(依据键)
System.out.println(map.containsKey("李四"));//false
//判断是否包含(依据值)
System.out.println(map.containsValue("男"));//false
//获取集合长度
System.out.println(map.size());//0
}
}
# map的遍历方式
# 通过键获取值
public class MapList {
public static void main(String[] args) {
//map的遍历方式
//创建集合对象
Map<String, String> map2 = new HashMap<>();
map2.put("张三", "男");
map2.put("李四", "女");
map2.put("王五", "男");
//获取集合中所有的键
Set<String> keySet = map2.keySet();
System.out.println(keySet);//[李四, 张三, 王五]
//循环获取键的值
keySet.forEach(key -> System.out.println(map2.get(key)));
}
}
# 通过键值对的形式
public class MapList {
public static void main(String[] args) {
//通过键值对的形式遍历双列集合
Map<String, String> map3 = new HashMap<>();
map3.put("zhangsan", "男");
map3.put("lisi", "女");
map3.put("wangwu", "男");
//通过双列集合的键值对对象进行遍历
//通过entrySet方法获取所有的键值对对象,并返回一个set集合
Set<Map.Entry<String, String>> entries = map3.entrySet();
System.out.println(entries);//[lisi=女, zhangsan=男, wangwu=男]
//遍历set集合获取每一个键值对对象
entries.forEach(entry -> System.out.println(entry.getKey() + ":" + entry.getValue()));
}
}
# 利用lambda表达式进行遍历
| 方法名称 | 描述 |
|---|---|
default void forEach(BiConsumer<? super K, ? super V> action) | 遍历集合,并对集合中的每一个元素进行操作 |
public class MapList {
public static void main(String[] args) {
//利用`lambda`表达式进行遍历
Map<String, String> map4 = new HashMap<>();
map4.put("zhangsan123", "男");
map4.put("lisi123", "女");
map4.put("wangwu123", "男");
map4.forEach((key, value) -> System.out.println(key + ":" + value));
}
}
# HashMap的特点
HashMap是Map里面的一个实现类- 没有额外需要学习的特有方法,直接使用map里面的方法就可以
- 特点:都是由键决定:无序、不重复、无索引
HashMap跟HashSet的底层原理一样,都是哈希表结构
# 练习
public class MapList {
public static void main(String[] args) {
//需求:创建一个HashMap集合,键是学生对象,值是籍贯
//存储三个键值对元素,并遍历
//要求:同姓名,同年龄 认为是同一个学生
//核心点 HashMap的键位置如果存储的是自定义对象 那么需要重写hashCode和equals方法
//创建存储学生对象的集合
Map<Student, String> map5 = new HashMap<>();
map5.put(new Student("张三", 18), "北京");
map5.put(new Student("李四", 19), "上海");
map5.put(new Student("张三", 18), "广州");
//遍历集合
map5.forEach((student, address) -> System.out.println(student.toString() + ":" + address));
//需求:统计投票人数
//某班级80名学生现在需要组成秋游活动,班长提供了四个景点分别是(A、B、C、D)每个学生只能选择一个景点,请统计出最终那个景点去的人多
Map<String, String> map6 = new HashMap<>();
String[] placeList = {"A", "B", "C", "D"};
for (int i = 0; i < 80; i++) {
//随机获取景点
String place = placeList[(int) (Math.random() * placeList.length)];
map6.put(Objects.toString(i), place);
}
Map<String, Integer> map7 = new HashMap<>();
map6.forEach((key, value) -> {
if (map7.containsKey(value)) map7.put(value, map7.get(value) + 1);
else map7.put(value, 1);
});
String finalPlace = "";
int max = 0;
Set<Map.Entry<String, Integer>> entrySet = map7.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
if (entry.getValue() > max) {
finalPlace = entry.getKey();
max = entry.getValue();
}
}
;
System.out.println(map7);
System.out.println("最终景点:" + finalPlace + ",人数:" + max);//最终景点:B,人数:25
}
}
# LinkHashMap
# 特点
- 由键决定:有序,不重复,无索引
- 这里的有序指的是:保证存储和取出的元素顺序一致
public class MapList {
public static void main(String[] args) {
//linkHashMap可以保证元素在存和取的时候顺序一致
Map<String, Integer> map8 = new LinkedHashMap<>();
map8.put("lisi", 19);
map8.put("zhangsan", 18);
map8.put("wangwu", 20);
System.out.println(map8);//{lisi=19, zhangsan=18, wangwu=20}
Map<String, Integer> map9 = new HashMap<>();
map9.put("zhangsan", 18);
map9.put("lisi", 19);
map9.put("wangwu", 20);
System.out.println(map9);//{lisi=19, zhangsan=18, wangwu=20}
}
}
# TreeMap
# 特点
TreeMap跟TreeSet一样,都是红黑树结构- 由键决定的特性:不重复、无索引、可排序
- 可排序:对键进行排序,默认是升序,可以指定排序规则
# 代码书写的两种排序规则
- 实现
Comparable接口,重写compareTo()方法 - 创建集合时传递
Comparator比较器对象,指定比较规则(优先)
public class MapList {
public static void main(String[] args) {
//需求1
//键:整数表示id
//值:字符串表示商品名称
//要求:按照id升序排序,如果id相同,按照商品名称升序排序
Map<Integer, String> map10 = new TreeMap<>(new Comparator<Integer>() {
//实现接口降序排序
@Override
public int compare(Integer o1, Integer o2) {
//o1:当前要添加的元素
//o2:当前已经在红黑树中存在的元素
return o2 - o1;
}
});
map10.put(1, "苹果");
map10.put(2, "香蕉");
map10.put(3, "橘子");
System.out.println(map10);
//需求
//键:学生对象
//值:籍贯
//要求:按照学生年龄升序排列,年龄一样按照姓名字母排列,同姓名年龄视为同一个人
Map<Student, String> map11 = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//返回值
//负数:表示当前要当前要添加的元素是小的,存左边
//正数:表示当前要当前要添加的元素是大的,存右边
//0: 表示当前要当前要添加的元素和已经存在的元素一样,不存
//年龄不同 按照年龄升序排列
if (o1.getAge() != o2.getAge()) return o1.getAge() - o2.getAge();
else {
//姓名相同 舍弃 视为同一个人
// if (o1.getName().equals(o2.getName())) return 0;
//姓名不同 按照姓名首字母排列
//compareTo方法 它是先比较对应字符的大小(ASCII码顺序)
// 如果参数字符串等于此字符串,则返回值 0;
// 如果此字符串小于字符串参数,则返回一个小于 0 的值;
// 如果此字符串大于字符串参数,则返回一个大于 0 的值。
System.out.println(o1.getName().compareTo(o2.getName()));
return o1.getName().compareTo(o2.getName());
}
}
});
map11.put(new Student("张三", 19), "香蕉");
map11.put(new Student("张三", 19), "苹果");
map11.put(new Student("王五", 20), "橘子");
System.out.println(map11);
//需求:字符串“aababcabcdabcde”请统计字符串中每个字符出现的个数
String str = "bbaababcabcdabcde";
char[] chars = str.toCharArray();
Map<String, Integer> map12 = new TreeMap<>();
for (Character aChar : chars) {
String aCharStr = aChar.toString();
if (map12.containsKey(aCharStr)) map12.put(aCharStr, map12.get(aCharStr) + 1);
else map12.put(aCharStr, 1);
}
System.out.println(map12);
StringBuilder finalStr = new StringBuilder();
for (Map.Entry<String, Integer> entry : map12.entrySet()) {
finalStr.append(entry.getKey()).append(":").append(entry.getValue()).append(" ");
}
System.out.println(finalStr);
}
}
# 可变参数
- 方法形参的个数是可以发生变化的
- 格式
...名字 - 可变参数的底层就是一个数组,只不过不需要我们创建了,java会帮我们创建好
public class VarArgs {
public static void main(String[] args) {
System.out.println(getSun(1, 2, 3, 4));
}
public static int getSun(int... args) {
System.out.println(Arrays.toString(args));//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
return Arrays.stream(args).sum();
}
}
# 细节
- 在方法的形参中,最多只能写一个可变参数
- 如果方法除了可变参数外,还有其他的参数,那么可变参数要写在最后
# 不可变集合
- 不可变集合就是不可修改该集合的元素,及长度不可变,元素不可变,相当于常量。
# 创建不可变集合的书写形式
在list、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合
| 方法名称 | 说明 |
|---|---|
static <E> List<E> of(E ,..Elements) | 创建一个具有指定元素的List集合 |
static <E> Set<E> of(E ,..Elements) | 创建一个具有指定元素的Set集合 |
static <K,V> Map<K,V> of(K k1,V v1,K k2,V v2,...) | 创建一个具有指定元素的Map集合 |
注意
这个集合不能添加,不能删除,不能修改
package ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class StaticList {
public static void main(String[] args) {
//不可变集合
//该集合一旦创建完毕之后不可以修改
List<Integer> list = List.of(1, 2, 3);
System.out.println(list);
list.forEach(System.out::println);
//修改 报错
//list.set(1, 4);
//list.add(4);
System.out.println(list);
Set<Integer> set = Set.of(41, 42);
System.out.println(set);
//map中的可变参数是有上限的 最多20个 也就是10个键值对
Map<Integer, Integer> map = Map.of(1, 2, 3, 4);
System.out.println(map);
//超过10个元素 用ofEntries方法
Map<Object, Object> objectObjectMap = Map.ofEntries(map1.entrySet().toArray(new Map.Entry[0]));
System.out.println(objectObjectMap);
}
}
注意
- set中的元素不可重复,如传递的是引用数据类型,此时比较的是地址值
- set中如果传递引用类型时需重写
equals方法 才能对引用类型的值进行比较
public class StaticList {
public static void main(String[] args) {
Set<StaticStudent> students = Set.of(new StaticStudent("张三", 20), new StaticStudent("张三", 20));//报错
System.out.println(students);
}
class StaticStudent {
String name;
int age;
public StaticStudent(String name, int 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;
StaticStudent that = (StaticStudent) o;
return age == that.age && Objects.equals(name, that.name);
}
}
}