# 集合进阶

# 单列集合顶层接口

# 集合体系结构

  • 单列集合:在添加元素时,一次只能添加一个元素。
  • 双列集合:在添加元素时,可以一次添加一对数据。

# 单列集合

  • 单列集合最顶层的为Collection接口,该接口定义了集合的最基本操作。

# Collection 接口可分为两种单列集合

  • list系列集合:ArrayListLinkedListVector
  • set系列集合: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的遍历方式

  1. 迭代器遍历
  2. 增强for遍历
  3. 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方法
        //在迭代器遍历时,不能用集合的方法进行增加或者删除
    }
}

注意

迭代器的四个细节

  1. 如果当前位置没有元素,还要强行获取,会报NoSuchElementException
  2. 如果迭代器遍历完毕,指针不会复位
  3. 循环中只能用一次next方法
  4. 迭代器遍历时,不能用集合的方法进行增加或者删除

# 增强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表达式"));
    }
}

# 三种遍历方式

  • 迭代器:在遍历过程中需要删除元素,请使用迭代器
  • 增强forlambda:如果在遍历过程中仅仅需要遍历,那么使用增强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));
        }
    }
}

# 五种遍历方式对比

  • 迭代器遍历:在遍历过程中需要删除元素,请使用迭代器
  • 在遍历过程中需要添加元素,请使用列表迭代器
  • 增强forlambda:如果在遍历过程中仅仅需要遍历,那么使用增强forlambda即可
  • 普通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里面的方法就可以
  • 特点:都是由键决定:无序、不重复、无索引
  • HashMapHashSet的底层原理一样,都是哈希表结构

# 练习

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

# 特点

  • TreeMapTreeSet一样,都是红黑树结构
  • 由键决定的特性:不重复、无索引、可排序
  • 可排序:对键进行排序,默认是升序,可以指定排序规则

# 代码书写的两种排序规则

  1. 实现Comparable接口,重写compareTo()方法
  2. 创建集合时传递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();
    }
}

# 细节

  1. 在方法的形参中,最多只能写一个可变参数
  2. 如果方法除了可变参数外,还有其他的参数,那么可变参数要写在最后

# 不可变集合

  • 不可变集合就是不可修改该集合的元素,及长度不可变,元素不可变,相当于常量

# 创建不可变集合的书写形式

listSetMap接口中,都存在静态的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);
        }
    }
}
上次更新: 12/8/2024, 7:48:40 PM