Java 集合


目录
  • Java 集合
    • 一、 Collection集合
      • 1、 集合概述
      • 2、 常用API
      • 3、 集合的遍历
        • 3.1 迭代器
        • 3.2 for
        • 3.3 lambda
      • 4、 Collection工具类
        • 4.1 排序
        • 4.2 常用API
    • 二、 常见数据结构
      • 1、 概述、栈、队列
        • 1.1 概述
        • 1.2 栈
        • 1.3 队列
      • 2、 数组
      • 3、 链表
      • 4、 二叉树
      • 5、 平衡二叉树
      • 6、 红黑树
    • 三、 List系列集合
      • 1、 集合特点、特有API
        • 1.1 集合特点
        • 1.2 特有API
      • 2、 遍历
      • 3、 LinkedList
    • 四、 泛型深入
      • 1、 泛型概述
      • 2、 泛型的定义
        • 2.1 泛型类
        • 2.2 泛型方法
        • 2.3 泛型接口
      • 3、 通配符
    • 五、 Set系列集合
      • 1、 概述
      • 2、 常用API
      • 3、 HashSet底层原理
      • 4、 LinkedHashSet
      • 5、 TreeSet
      • 6、 可变参数
    • 六、 Map系列集合
      • 1、 概述
      • 2、 常用API
      • 3、 遍历
        • 3.1 键找值
        • 3.2 键值对
        • 3.3 Lambda表达式
      • 4、 HashMap
      • 5、 LinkedHashMap
      • 6、 TreeMap
      • 7、 不可变集合
    • 七、 Stream流
      • 1、 概述
      • 2、 获取
      • 3、 常用API
      • 4、 收集数据

Java 集合

一、 Collection集合

1、 集合概述

集合和数组都是容器

数组的特点:

  • 数组定义完成并启动后,类型确定,长度固定

  • 在进行增删数据操作的时候,数组是不太适合的,增删数据都需要放弃原有数组或者移位

集合的特点:

  • 集合时存储对象数据的一种容器
  • 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定,集合更像气球
  • 集合非常适合做元素的增删操作
  • 注意:集合中只能存储引用类型数据,如果要存储基本类型数据可以选择包装类

Collection集合体系

Collection 集合特点:

  • List
    • 添加元素是有序的、可重复、有索引
  • Set
    • 添加的元素是无序的、不重复、无索引

集合对于泛型的支持:

  • 集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型

    // 存储基本数据类型使用包装类
    Collection list = new ArrayList<>();
    

集合和泛型不支持基本类型,只支持引用数据类型

2、 常用API

Collection是单例集合的祖宗接口,它的功能是全部单例集合都可以继承使用的

package collection;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        // HashSet:添加的元素都是无序,不重复,无索引的
        Collection list = new ArrayList<>();
        
        // 添加元素:添加成功返回true
        list.add("java");
        list.add("python");
        list.add("C");
        System.out.println(list);
        
        // 清空集合的元素
        // list.clear();
        
        // 判断集合是否为空,是则返回true
        System.out.println(list.isEmpty());
        
        // 获取集合的大小
        System.out.println(list.size());
        
        // 判断集合中是否包含某个元素
        System.out.println(list.contains("python"));
        
        // 删除某个元素:如果有多个重复元素,默认删除前面的第一个
        System.out.println(list.remove("C"));
        
        // 把集合转换成数组
        Object[] arrs = list.toArray();  // 注意不能集合里面不能完全确定一种数据类型
        System.out.println("数组内容:" + Arrays.toString(arrs));
        
        System.out.println("-------------------扩展-----------------------");
        Collection c1 = new ArrayList<>();
        Collection c2 = new ArrayList<>();
        c2.addAll(c1);  // 把C1中的元素全部导入c2中
    }
}

3、 集合的遍历

3.1 迭代器

迭代器遍历的概述:

  • 遍历就是一个一个的把容器中的元素访问一遍
  • 迭代器在Java中的代表是Iterator,迭代器是集合的专用遍历方式

集合获取迭代器

方法名称 说明
Iterator iterator() 返回集合中的迭代器对象,该迭代器默认指向当前集合的首地址

迭代器常用方法

方法名称 说明
boolean hasNext() 询问当前位置是否有元素存在,存在返回true
E next() 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        Collection lis = new ArrayList<>();
        lis.add("李华");
        lis.add("赵华");
        lis.add("张华");
        lis.add("小华");
        System.out.println(lis);

        // 得到当前集合的迭代器对象
        Iterator it = lis.iterator();
        while (it.hasNext()) {
            // 遍历迭代器对象
            System.out.println(it.next());
        }
    }
}

3.2 for

增强for循环

  • 增强for循环:既可以遍历集合也可以遍历数组
  • 它是JDK5之后出现的,其内部是一个Iterator迭代器,遍历集合相当于是迭代器的简化写法
  • 实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口

格式:

for (元素数据类型 变量名: 数组或者Collection集合) {
	// 在此处使用变量即可,该变量就是元素
}
package collection;

import java.util.ArrayList;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        Collection lis = new ArrayList<>();
        lis.add("李华");
        lis.add("赵华");
        lis.add("张华");
        lis.add("小华");
        System.out.println(lis);

        for (String ele:
             lis) {
            // 在遍历内部修改元素值是无意义的,其不会改变原来的值
            System.out.println(ele);
        }
    }
}

3.3 lambda

Lambda表达式遍历集合:

  • 得益于JDK8开始的新技术Lambda表达式,提供了一种更简单。更直接的遍历集合的方式

Collection结合Lambda遍历API:forEach()

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Collection lis = new ArrayList<>();
        lis.add("李华");
        lis.add("赵华");
        lis.add("张华");
        lis.add("小华");
        System.out.println(lis);
    
        lis.forEach(new Consumer() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        
        lis.forEach(s -> {
            System.out.println(s);
        });
    }
}

4、 Collection工具类

4.1 排序

方法名称 说明
public static void sort(List list) 将集合中元素按照默认规则排序
public static void sort(List list, Comparator<? super T> c) 将集合中的元素指定规则进行排序

4.2 常用API

方法名称 说明
public static boolean addAll(Collection<? super T> c, T...elements) 给集合对象批量添加元素
public static void shuffle(List<?> list) 打乱List集合元素的顺序

二、 常见数据结构

1、 概述、栈、队列

1.1 概述

数据结构是计算机底层存储,组织数据的方式。是指数据相互之间是以什么方式排列在一起的

通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率

常见的数据结构:

  • 队列
  • 数组
  • 链表
  • 二叉树
  • 二叉查找树
  • 平衡二叉树
  • 红黑树

1.2 栈

栈数据结构的执行特点:

  • 后进先出,先进后出

数据进入栈模型的过程称为:压栈、进栈

数据离开栈模型的过程称为:弹栈、出栈

1.3 队列

队列数据结构的执行特点:

  • 先进先出,后进后出

数据从后端进入队列模型的过程称为:入队列

数据从前端离开队列模型的过程称为:出队列

2、 数组

内存中的连续区域

特点:

  • 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
  • 删除效率低:要将原始数据删除,同时后面每个数据前移
  • 添加效率低:添加位置后的每个数据后移,再添加元素

数组是一种查询快,增删慢的模型

3、 链表

特点:

  • 链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址
  • 链表查询慢,无论查询哪个数据都要从头开始找
  • 链表增删相对快,只需要修改地址的指向即可

链表是一种查询慢、增删快的模型(对比数组)

单向链表

双向链表

4、 二叉树

二叉树里面存储了:父节点地址、值、左子节点地址、右子节点地址

特点:

  • 只能有一个根节点,每个节点最多支持2个直接子节点
  • 节点的度:节点拥有的子树的个数,二叉树的度不能大于2叶子节点,度为0的节点,也称之为终端结点
  • 高度:叶子节点的高度为1,叶子节点的父节点高度为2,以此类推,根节点的高度最高
  • 层:根节点在第一层,以此类推
  • 兄弟节点:拥有共同父节点的节点互成为兄弟节点

二叉查找树又称二叉排序树或者二叉搜索树

特点:

  • 每一个节点上最多有两个子节点
  • 左子树上所有节点的值都小于根节点的值
  • 右子树上所有节点的值都大于根节点的值

目的:提高检索数据的性能

存储规则:

? 小的存左边

? 大的存右边

? 一样的不存

5、 平衡二叉树

平衡二叉树是在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能

要求:

  • 任意节点的左右子树的高度差不超过1,节点的左右两个子树都是一颗平衡二叉树

平衡二叉树在添加元素后可能导致不平衡

  • 基本策略是左旋或者右旋保证平衡

平衡二叉树旋转的四种情况

  • 左左
    • 当根节点左子树的左子树又节点插入,导致二叉树不平衡
  • 左右
    • 当根节点左子树的右子树有节点插入,导致二叉树不平衡
  • 右右
    • 当根节点右子树有节点插入,导致二叉树不平衡
  • 右左
    • 当根节点右子树的左子树有节点插入,导致二叉树不平衡

6、 红黑树

红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构

每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的

红黑规则:

  • 每一个节点或者是红色的,或者是黑色的,根节点必须是黑色的。
  • 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,叶节点是黑色的
  • 如果一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑节点

添加节点:

  • 添加的节点的颜色,可以是红色,也可以是黑色的
  • 默认是用红色效率高

红黑树增删改查的性能都很好

三、 List系列集合

1、 集合特点、特有API

1.1 集合特点

ArrayList、 LinkedList:有序、可重复、有索引

  • 有序:存储和去除的元素顺序一致
  • 有索引:可以通过索引操作元素
  • 可重复:存储的元素可以重复
  • 底层是基于数组实现的,查询元素快,增删相对慢

1.2 特有API

List集合因为支持索引,所以多了解索引操作的独特API,其他Collection的功能List也都继承了

方法名称 说明
void add(int index, E element) 在此集合中的指定索引插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E get(int index) 返回指定索引处的元素
E set(int index, E element) 修改指定索引处的元素,返回被修改的元素

2、 遍历

  1. 迭代器
  2. 增强for循环
  3. Lambda表达式
  4. for循环(存在索引)
package collection;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        List lis = new ArrayList<>();
        lis.add("李华");
        lis.add("赵华");
        lis.add("张华");
        lis.add("小华");
        System.out.println(lis);

        // 索引遍历
        for (int i = 0; i < lis.size(); i++) {
            System.out.println(lis.get(i));
        }
        
        // foreach遍历
        lis.forEach(s -> {
            System.out.println(s);
        });
        // 还有其他的遍历方法
    }
}

3、 LinkedList

  • 底层数据结构是双链表、查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API
方法名称 说明
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 在指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素

LinkedList 可以完成队列结构和栈结构(双链表)

四、 泛型深入

1、 泛型概述

泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查

泛型的格式:<数据类型>;注意:泛型只能支持引用数据类型

集合体系的全部接口和实现类都是支持泛型的使用的

泛型的好处:

  • 统一数据类型
  • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来

2、 泛型的定义

泛型可以在很多地方进行定义:

  • 类后面:-> 泛型类
  • 方法声明上:-> 泛型方法
  • 接口后面:-> 泛型接口

2.1 泛型类

定义类时,同时定义了泛型的类就是泛型类

泛型类的格式:修饰符 class 类名<泛型变量>{}

class A {
    // 此处泛型变量T可以随便写为任意标识,常见的如E/T/K/V等
}

作用:编译阶段可以指定数据类型,类似于集合的作用

模拟ArrayList集合自定义一个集合:

package collection;
import java.util.ArrayList;

public class MyArrayList {
    // 需求,模拟ArrayLIst定义一个泛型设计
    private ArrayList lis = new ArrayList<>();
    
    public void add(E e) {
        // 添加数据功能
        lis.add(e);
    }
    
    public void remove(E e) {
        // 移除数据功能
        lis.remove(e);
    }

    @Override
    public String toString() {
        // 输出功能
        return lis.toString();
    }
}

2.2 泛型方法

定义方法的同时定义了泛型方法就是泛型方法

泛型方法的格式:修饰符 <泛型变量> 方法返回值类型 方法名称(形参列表){}

package collection;

public class Main {
    public static void main(String[] args) {
        show("hello");  // 自动判断数据类型
    }
    public static  void show(T t) {
        System.out.println(t);
    }
}

泛型方法的核心思想:

  • 把出现泛型变量的地方全部替换成传输的真实数据类型

作用:

  • 方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性

2.3 泛型接口

使用了泛型定义的接口就是泛型接口

泛型接口的格式:修饰符 interface 接口名称 <泛型变量>{}

作用:泛型接口可以让实现类选择当前功能需要操作的数据类型

package collection;

public interface Data {
    // 对数据进行操作
    void add(T s);
    void delete(T s);
    T queryById(int id);
}

3、 通配符

?可以在“使用泛型”的时候代表一切类型

泛型的上下限:

  • ? extend Car:必须是Car或者其子类,泛型上限
  • ? super Car:必须是Car或者其父类,泛型下限

五、 Set系列集合

1、 概述

特点:

  • 无序:存取顺序不一致
  • 不重复:可以去除重复
  • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

实现类特点:

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:排序、不重复、无索引

2、 常用API

Set系列的API大部分继承自Collection,几乎没有新的API

package set_;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        // 创建
        Set sets = new HashSet<>();
        // 添加数据
        sets.add("hello");
        sets.add("world");
        sets.add("python");
        // 移除数据
        sets.remove("hello");
        System.out.println(sets);

        // 遍历数据
        for (String s:
            sets) {
            System.out.println(s);
        }
        sets.forEach(s -> {
            System.out.println(s);
        });
        Iterator it = sets.iterator();  // 转换成迭代器
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

3、 HashSet底层原理

HashSet集合底层原理采取哈希表存储的数据

哈希表是一种对于增删改查数据性能都较好的结构

组成:

  • JDK8以前,底层使用数组+链表组成
  • JDK8之后,底层采用数组+链表+红黑树组成
    • 当挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树

哈希值:

  • 是JDK根据对象的地址,按照某种规则算出来的int类型的数值
  • public int hashCode():返回对象的哈希值

哈希值特点:

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
  • 默认情况下,不同对象的哈希值是不同的

Set集合去重原理,先判断哈希值,再判断equals

如果是要进行自定义数据类型以及复杂数据类型的去重,需要我们重写equalshashCode方法

4、 LinkedHashSet

特点:有序、不重复、无索引

  • 这里的有序指的是保证存储和去除的元素顺序一致

原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序

5、 TreeSet

特点:不重复、无索引、可排序

可排序:按照元素的大小默认升序(由小到大)排序

TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好

集合默认排序规则:

  • 对于数值类型:默认安装大小进行升序排序

  • 对于字符串类型:默认按照首字符的编号升序排序

  • 对于自定义类型:TreeSet无法直接排序,需要制定排序规则

    1. 让自定义的类实现Comparable接口,重写里面的compareTo方法来定制比较规则

      package set_;
      
      import org.jetbrains.annotations.NotNull;
      
      public class Student implements Comparable{
          public Integer id;
          public String name;
          public Integer age;
      
          /**
           * 方式一:类自定义比较规则
           * @param o
           * @return
           */
          @Override
          public int compareTo(@NotNull Student o) {
              // 根据id升序排序,两个相等返回0,前面大于后面返回正数
              return this.id - o.id;  // 会去掉重复的元素
              // return this.id - o.id >= 0 ? 1 : -1;  // 保留id重复的元素
          }
      }
      
    2. TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则

      Set stus = new TreeSet<>(new Comparator() {
          @Override
          public int compare(Student o1, Student o2) {
              return o1.id - o2.id;  // 根据id值升序
          }
      });
      Set stus = new TreeSet<>((Student o1, Student o2) -> {
          return o1.id - o2.id;  // 也可以结合匿名函数,浮点型建议直接使用Double.Compare();方法
      });
      

      如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序

6、 可变参数

可变参数用在形参中可以接收多个数据

可变参数的格式:数据类型...参数名称

作用:

  • 传输参数非常灵活,方便。可以不传输参数,可以传输一个或多个参数,也可以传输一个数组
  • 可变参数在方法内部本质上就是一个数组

可变参数的注意事项:

  • 一个参数列表中可变参数只能有一个
  • 可变参数必须放在形参列表的最后面
package set_;

import java.util.Arrays;

public class SetDemo {
    public static void main(String[] args) {
        sum(1);
        sum(1, 2, 3);
        sum(new int[]{1, 3, 6, 2});

    }
    public static void sum(int...sum_) {
        System.out.println(Arrays.toString(sum_));  // 输出整型数组的内容
    }
}

六、 Map系列集合

1、 概述

Map集合是一种双列集合,每个元素包含两个数据

Map集合的每个元素的格式:key=value(键值对元素)

Map集合也被称为:“键值对集合”

Map集合体系特点:

  • Map集合的特点都是由键决定的
  • Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)
  • Map集合后面重复的键对应的值会覆盖前面重复键的值

Map集合实现类特点:

  • HashMap:元素按照键是无序,不重复,无索引,值不做要求。(与Map体系一致)

    • // 创建一个Map集合对象
      Map maps = new HashMap<>();
      maps.put("笔记本", 23);
      System.out.println(maps);
      

2、 常用API

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() 集合的长度,也就是集合中键值对的个数

3、 遍历

3.1 键找值

先获取Map集合的全部键的Set集合

遍历键的Set集合,然后通过键提取对应值

方法名称 说明
Set keySet() 获取所有键的集合
V get(Object key) 根据键获取值
package set_;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        // 创建一个Map集合对象
        Map maps = new HashMap<>();
        maps.put("笔记本", 23);
        maps.put("IPad", 234);
        maps.put("IPhone", 345);
        System.out.println(maps);
        // 遍历Map集合
        Set keys = maps.keySet();
        for (String s:
        keys) {
            System.out.println(maps.get(s));
        }
    }
}

3.2 键值对

先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了

遍历Set集合,然后提取键以及提取值

方法名称 说明
Set> entrySet() 获取所有键值对对象的集合
K getKey() 获得键
V getValue() 获取值
package set_;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        // 创建一个Map集合对象
        Map maps = new HashMap<>();
        maps.put("笔记本", 23);
        maps.put("IPad", 234);
        maps.put("IPhone", 345);
        System.out.println(maps);
        // 遍历Map集合
        Set> entries = maps.entrySet();  // 获取键值对组成的集合
        for (Map.Entry entry:
        entries) {
            System.out.println("键为:" + entry.getKey() + "\n值为:" + entry.getValue());
        }
    }
}

3.3 Lambda表达式

得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的遍历集合的方式

package set_;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

public class SetDemo {
    public static void main(String[] args) {
        // 创建一个Map集合对象
        Map maps = new HashMap<>();
        maps.put("笔记本", 23);
        maps.put("IPad", 234);
        maps.put("IPhone", 345);
        System.out.println(maps);
        // 遍历Map集合
        maps.forEach((String s, Integer integer) -> {
                System.out.println(s + "====" + integer);
        });
    }
}

4、 HashMap

使用最多的Map集合是HashMap

特点:

  • HashMap是Map里面的一个实现类。特点都是由键决定的;无序、不重复、无索引
  • 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了
  • HashMap跟HashSet底层原理是一模一样的,都是哈希表结构,只是HashMap的每一个元素包含两个值而已

实际上:Set系列集合的底层就是Map实现的,只是Set集合中的元素只要键数据,不要值数据而已

HashMap的特点和底层原理:

  • 由键决定:无序、不重复、无索引。HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法
  • 基于哈希表。增删改查的性能都较好

5、 LinkedHashMap

特点:

  • 由键决定:有序、不重复、无索引
  • 这里的有序指的是保证存储和取出的元素顺序一致
  • 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序

6、 TreeMap

特点:

  • 由键决定特性:不重复、无索引、可排序
  • 可排序:按照键数据的大小默认升序(由小到大)拍戏,只能对键排序
  • 注意:TreeMap集合是一定要排序的,可以默认排序,也可以将键安装指定的规则进行排序
  • TreeMap跟TreeSet一样,底层原理是一样的

TreeMap集合自定义排序规则有2中:

  • 类实现Comparable接口,重写比较规则
  • 集合自定义Comparator比较器对象,重写比较规则

7、 不可变集合

特点:

  • 定义完成后不可以修改,或者添加、删除
Set names = Set.of("李华", "张三");  // 这个集合是不可以改变的
Map maps = Map.of("华为", 2, "Java", 4);  // 这个Map也是不可以改变的

七、 Stream流

1、 概述

什么是Stream流?

  • 在Java8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念
  • 目的:用于简化集合和数组操作的API

Stream流的思想:

  • 先得到集合或者数组的Stream流
  • 把元素放上去
  • 然后就用这个Stream六简化API来方便的操作元素

作用:

  • 简化集合、数组操作的API。结合了Lambda表达式

2、 获取

Stream流的三类方法:

  • 获取Stream流
    • 创建一条流水线,并把数据放到流水线上准备进行操作
  • 中间方法
    • 流水线上的操作,一次操作完毕之后,还可以继续进行其他操作
  • 终结方法
    • 一个Stream流只能有一个终结方法,是流水线上的最后一个操作

获取Stream流的方式:

  • 可以使用Collection接口中的默认方法stream()生成流
名称 说明
default Stream stream() 获取当前集合对象的Stream流

数组获取Stream流的方式:

名称 说明
public static Stream stream(T[] array) 获取当前数组的stream流
public static Stream of(T...values) 获取当前数组/可变数据的Stream流
String[] names = {"hello", "world"};
Stream nameStream = Array.stream(names);
Stream nameStream2 = Stream.of(names);

3、 常用API

即为中间操作方法

名称 说明
Stream filter(Predicate<? super T> predicate) 用于对流中数据进行过滤
Stream limit(long maxSize) 获取前几个元素
Stream skip(long n) 跳过前几个元素
Stream distinct() 去除流中重复的元素,依赖hashCode和equals
static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流

注意:

  • 中间方法也称为非终结方法,调用完成后返回新的Stream流可以继续使用,支持链式编程
  • 在Stream流中无法直接修改集合、数组中的数据

4、 收集数据

名称 说明
R collect(Collect collector) 开始收集Stream流,指定收集器

Collection工具类提供了具体的收集方式

名称 说明
public static Collector toList() 把数据收集到List集合中
public static Collector toSet() 把数据收集到Set集合中
public static Collector toMap(Function keyMapper, Function valueMapper) 把数据收集到Map集合
package set_;

import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class SetDemo {
    public static void main(String[] args) {
        List lis = new ArrayList<>();
        lis.add("李华");
        lis.add("赵华");
        lis.add("张华");
        lis.add("小华");
        System.out.println(lis);
        Stream s = lis.stream().filter(s_ -> s_.startsWith("张"));
        
        // 注意,流只能使用一次
        // Object[] arrs_t = s.toArray();  // 返回类数组,后面可以进行类型转换
        // String[] arrs = s.toArray(String[]::new);  // 直接返回字符串数组
        System.out.println(s.collect(Collectors.toList()));  // 转换为List集合。使用这个方法,s.toList(),返回不可变集合
    }
}

还有其他的接口,以及集合,大家可以通过官方文档查找