当前位置: 首页 > article >正文

11 Collection集合、Map集合:分类、功能、遍历、底层原理,Stream流:获取、中间方法、终结方法 (黑马Java视频笔记)

在这里插入图片描述

文章目录

    • 集合
      • 1. 认识集合
      • 2. Collection单列集合特点
      • 3. Collection单列集合的常用功能
      • 4. Collection的遍历方式
        • 1)迭代器遍历 Iterator
        • 2)增强for循环(for...each遍历)
        • 3)Lambda表达式:forEach()
      • 5. 三种遍历方式的区别
        • **5.1 并发修改异常问题**
        • **5.2 一般应对方案:**
        • 5.3 ⭐方案一:迭代器遍历并删除
          • 💡`modCount` 和 `expectedModCount` 的作用
        • 5.4 方案二:用增强for和Lambda都没有办法解决并发修改异常
      • 6. Collection-List集合:有序,可重复,有索引
        • 1) LIst集合因为支持索引,所以除了继承了Collection的功能外,还有很多与索引相关的方法
        • 2)遍历方式
        • 3)ArrayList和LinkedList的区别:
        • 4)LinkedList 应用场景:在首尾增删快
      • 7. Collection-Set系列集合:无序(添加顺序和获取顺序不一致)、不重复、无索引
        • 7.1 HashSet集合的底层原理
          • 1)哈希值
          • 2)对象哈希值的特点
          • 3)基于哈希表存储数据 >>> 无序、不重复、无索引
            • 存储方式变化过程:数组+链表 >>> 数组+链表+红黑树
            • 默认加载因子
          • 4)红黑树
          • 5)为什么性能好
        • 7.2 ⭐HashSet集合元素的去重操作 -> 自定义对象
          • 思考🤔🤔🤔
        • 7.3 LinkedHashSet集合的底层原理
          • **有序** → 每个元素都额外的多了一个**双链表**的机制记录它前后元素的位置
        • 7.4 TreeSet集合的原理
        • 7.5 什么时候用什么集合呢?
      • 8. Map集合(键值对集合)
        • 8.1 Map集合分类
        • 8.2 Map集合的常用方法
        • 8.3 Map集合的遍历方式
          • 1)键找值
          • 2)键值对
          • 3)Lambda表达式
        • 8.4 Map集合的实现类
          • 1)HashMap集合的底层原理
          • 2)LinkedHashMap的底层原理
          • 3)TreeMap的底层原理,同TreeSet集合
        • 8.5 Map集合的案例-统计投票信息
    • Stream流
      • 1. 获取Stream流
      • 2. 常用中间方法
      • 3. 常用终结方法
        • 3.1 常用终结方法
        • 3.2 收集Stream流:

集合

1. 认识集合

💡容器 >> 装数据

体系结构:

  • Collection 单列集合 :每个元素(数据)只包含一个值
  • Map 双列集合:每个元素包含两个值(键值对)

2. Collection单列集合特点

  • List系列集合
    • ArrayListLinekdList:添加的元素是有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不重复、无索引
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
    • TreeSet:按照大小默认升序排序、不重复、无索引
public static void main(String[] args) {
    // 目标:搞清楚Collection集合的整体特点
    // 1、List家族的集合:有序、可重复、有索引
    List<String> list = new ArrayList<>();
    list.add("张三");
    list.add("李四");
    list.add("王五");
    System.out.println(list);   // [张三, 李四, 王五]
    System.out.println(list.get(0));    // 张三

    // 2、Set家族的集合:无序、不可重复、无索引
    Set<String> set = new HashSet<>();
    set.add("张三");
    set.add("张三");
    set.add("李四");
    set.add("王五");
    System.out.println(set);    // [李四, 张三, 王五]
    System.out.println(set.contains("张三")); // true

}

3. Collection单列集合的常用功能

  • Collection作为单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的

在这里插入图片描述

  • toArray方法的使用
 public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        System.out.println(list);   // [张三, 李四, 王五]

        // 把集合转换成数组:toArray() 默认转Object数组
        Object[] arr = list.toArray();
        System.out.println(arr.getClass()); // class [Ljava.lang.Object;
        System.out.println(Arrays.toString(arr));   // [张三, 李四, 王五]

        // 把集合转换成字符串数组
        String[] arr2 = list.toArray(String[]::new);
     	// String[] arr2 = list.toArray(new String[0]);
/*      完整代码
        String[] arr3 = list.toArray(new IntFunction<String[]>() {
            @Override
            public String[] apply(int value) {
                return new String[0];
            }
        });
*/
        System.out.println(arr2.getClass());    // class [Ljava.lang.String;
    }

❓:具体询问ai理解

4. Collection的遍历方式

1)迭代器遍历 Iterator
  • 迭代器是用来遍历集合的专用方式(数组没有)

① 得到这个集合的迭代器对象

​ - 初始默认指在下标为0的位置

② 使用一个while循环来遍历

// 1. 迭代器遍历:Iterator
Collection<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");

// 获取迭代器对象
Iterator<String> it = list.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
2)增强for循环(for…each遍历)
  • 即可以遍历数组,也可以遍历集合

    for (元素的数据类型 变量名:数组或者集合){ 
    }
    
for (String s : list) {
    System.out.println(s);
}
3)Lambda表达式:forEach()

在这里插入图片描述

// forEach本质上就是for增强遍历
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

5. 三种遍历方式的区别

5.1 并发修改异常问题
  • 如果在遍历集合的同时又存在增删集合元素的行为,可能会出现业务异常,这种现象被称之为并发修改异常问题
// list = [宁夏枸杞, 枸杞, 西洋参, 枸杞子, 红枣]
// 需求1:删除全部枸杞
for(int i=0;i<list.size();i++){
    String name = list.get(i);
    System.out.println(name);    // 只有List集合才有索引
    if(name.contains("枸杞"))
    {
        list.remove(i);
    }
}
System.out.println(list);       // [枸杞, 西洋参, 红枣]

❓可以看到 list 集合中有一个遗漏的:这是因为在遍历到下标为 0 的 ” 宁夏枸杞 “ 时,i = 0 ,此时 ” 枸杞 “ 下标为 1,if 条件语句为 true → 删除下标为 0 的 ” 宁夏枸杞 “ ,” 枸杞 ” 的下标变为 0 ,“ 西洋参 ” 的下标变为 1 ,此时开始遍历 i = 1 的元素,不会遍历到 “ 枸杞 ”。

通过遍历时输出的 name ,也可以看到,并没有输出 “ 枸杞 ”

在这里插入图片描述

5.2 一般应对方案:
  • 在删除之后进行 i-- (编译器也会给出警告提示)

在这里插入图片描述

  • 倒着遍历并删除**(前提:支持索引)**
for(int i = list.size() - 1; i >= 0; i--){
    String name = list.get(i);
    System.out.println(name);
    if (name.contains("枸杞")) {
        list.remove(i);
    }
}
5.3 ⭐方案一:迭代器遍历并删除

使用迭代器来删除,而不是list集合删除(用list集合删除还是会存在并发修改异常)
迭代器内部会进行i--

// 迭代器遍历并删除
Iterator<String> it = list.iterator();

while(it.hasNext()){
    String name = it.next();
    System.out.println(name);
    if(name.contains("枸杞"))
    {
        it.remove();
    }
}
System.out.println(list);

⚠️如果使用迭代器遍历,使用集合方法删除 >> 报错!!!
在这里插入图片描述
🤔报错原因:modCount != expectedModCount,可以看到后台modCount = 6,expectedModCount = 5
在这里插入图片描述

💡modCountexpectedModCount 的作用

在这里插入图片描述

5.4 方案二:用增强for和Lambda都没有办法解决并发修改异常
  • 拿不到迭代器

  • 只适合做遍历,不适合做遍历并修改操作

6. Collection-List集合:有序,可重复,有索引

  • ArrayList:有序,可重复,有索引
  • LinkedList:有序,可重复,有索引
1) LIst集合因为支持索引,所以除了继承了Collection的功能外,还有很多与索引相关的方法

在这里插入图片描述

List<String> names = new ArrayList<>(); // 多态引用

在这里插入图片描述

List<String> names = new ArrayList<>();
// 1. 添加元素:add(E e)
names.add("张三");
names.add("李四");
names.add("王五");
System.out.println(names);

// 根据下标添加元素:add(int index, E e)
names.add(1, "赵六");
System.out.println(names);

// 2. 获取元素:get(int index)
String name = names.get(1);
System.out.println(name);

// 3. 修改元素:set(int index, E e)
names.set(1, "赵六");
System.out.println(names);

// 4. 删除元素:remove(int index)
names.remove(0);
System.out.println(names);

// 5. 获取长度:size()
int size = names.size();
System.out.println(size);
2)遍历方式
  • for循环
for (int i = 0; i < names.size(); i++) {
    String name2 = names.get(i);
    System.out.println(name2);
}
  • 迭代器
Iterator<String> it = names.iterator();
while (it.hasNext()) {
    String name2 = it.next();
    System.out.println(name2);
}
  • 增强for
for (String name2 : names) {
    System.out.println(name2);
}
  • lambda表达式
names.forEach(System.out::println);
 // System.out::println 是一种 实例方法引用,它引用了 PrintStream 类的 println 方法。
// names.forEach(name2 -> System.out.println(name2));
3)ArrayList和LinkedList的区别:
  • ArrayList基于数组存储数据:连续存储区域 -> 根据索引查询数据较快,查询任意数据耗时与链表查询相同;增删数据效率低(扩容、迁移等)
  • LinkedList基于双链表存储数据:数据由结点组成,一个结点包含当前的数据值和下一个结点的地址(双链表的结点包含上一个结点地址和下一个结点地址),查询慢(需要从头开始找),增删相对快(不用迁移其他数据),但对首位元素进行增删改查的速度是极快的

在这里插入图片描述
在这里插入图片描述

4)LinkedList 应用场景:在首尾增删快
  • 设计队列:先进先出,后进后出 >>> 尾进头出
    • 入队:addLast
    • 出队:removeFirst
// 目标:用LinkedList做一个队列对象
LinkedList<String> queue = new LinkedList<>();
// linkedlist独有功能,用list多态引用可能会使用不了
// 入队:addLast()从队尾入队
queue.addLast("张三");

// 出队:removeFirst()从队首出队
String name = queue.removeFirst();
  • 设计:后进先出,先进后出 >>> 头进头出
    • 进栈:push
    • 出栈:pop
// 做一个栈
LinkedList<String> stack = new LinkedList<>();
// 入栈:addFirst()从队首入栈  push()
stack.addFirst("张三");
stack.push("李四");

// 出栈:removeFirst()从队首出栈  pop()
String name2 = stack.removeFirst();
System.out.println(name2);
String name3 = stack.pop();
System.out.println(name3);

7. Collection-Set系列集合:无序(添加顺序和获取顺序不一致)、不重复、无索引

  • HashSet:无序、不重复、无索引
  • LinkedHashSet有序(哈希表)、不重复、无索引
  • TreeSet排序(红黑树)、不重复、无索引

其功能就是Collection的功能继承过来的,因为没有索引,所以也没有新增的功能。

// HashSet无序不重复、无索引
Set<String> set = new HashSet<>();
set.add("java");
set.add("java");
set.add("鸿蒙");
set.add("python");
set.add("新媒体");
System.out.println(set);    // [python, java, 鸿蒙, 新媒体]

// LinkedHashSet有序不重复、有索引
Set<String> set1 = new LinkedHashSet<>();
set1.add("java");
set1.add("java");
set1.add("鸿蒙");
set1.add("python");
set1.add("新媒体");
System.out.println(set1);   // [java, 鸿蒙, python, 新媒体]

// 2、创建一个TreeSet集合:排序(默认要大小升序排序)、不重复、无索引
Set<Double> set2 = new TreeSet<>();
set2.add(3.14);
set2.add(3.14);
set2.add(5.6);
set2.add(1.2);
System.out.println(set2);   // [1.2, 3.14, 5.6]
7.1 HashSet集合的底层原理

课程链接

1)哈希值
  • 一个int类型的随机值,java中每个对象都有一个哈希值
  • Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值
    • public int hashCode()
2)对象哈希值的特点
  • 同一个对象多次调用 hashCode()方法返回的哈希值是相同的
  • 不同的对象,他们的哈希值大概率不相等,但也有可能会相等(哈希碰撞)
    • int 范围 (-21亿多~21亿多) vs 创建了 50亿个对象 >>> 就会有重复的哈希值
3)基于哈希表存储数据 >>> 无序、不重复、无索引

哈希表

存储方式变化过程:数组+链表 >>> 数组+链表+红黑树

在这里插入图片描述
在这里插入图片描述

默认加载因子
  • 数据太多时,哈希表中的链表会很长,而链表查询效率很低(需要从头开始查找),所以在达到 加载因子*总长度 个元素时,对哈希表进行扩容
4)红黑树

关于集中排序树的具体含义

当数据已经排好序时,使用链表存储时的二叉查找树就相当于一个单链表

平衡二叉树(左右子树高度差不超过1)

红黑树(可以自平衡的二叉树)

  • 增删改查效率高
5)为什么性能好

查/存/改:底层基于数组,拿到 hash 值后就能通过映射关系算出元素存储的位置

删:找到位置后,直接抹除即可,不用进行数据迁移等操作

7.2 ⭐HashSet集合元素的去重操作 -> 自定义对象

需求:

  • 创建一个存储学生对象的集合,存储多个学生对象

要求:

  • 多个学生对象的成员变量值相同时,我们就认为是同一个对象,要求只保留一个

分析:

① 定义学生类、创建HashSet集合对象、创建学生对象

  • 学生类
public class Student {
    private String name;
    private int age;
    private int score;

    Student() {
    }

    Student(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}'+ '\n';
    }
}
  • 创建HashSet集合对象,并通过Collections工具类批量添加学生数据
Student s1 = new Student("小明", 14, 90);
// ....

Set<Student> students = new HashSet<>();

// 使用Collections集合工具类批量添加数据
Collections.addAll(students, s1, s2, s3, s4, s5);
System.out.println(students);

在这里插入图片描述

②在学生类中重写两个方法 : hashCode()和equals(),自动生成即可

在这里插入图片描述

③ 遍历集合(增强for)

for (Student student : students) {
    System.out.println(student);
}

在这里插入图片描述

思考🤔🤔🤔
  • 如果在类中使用了 @Data等构造器,会默认重写hashCode()equals()方法的

  • 为什么这些对象在Set集合中没有去重,需要重写方法 ?

    • 创建多个对象时,这几个对象的hash值是不同的,Set不会对其进行去重

    • 要使Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()equals()方法

7.3 LinkedHashSet集合的底层原理
  • 有序、不重复、无索引

基于哈希表(数组、链表、红黑树)实现,基于LinkedHashMap编写的

在这里插入图片描述

有序 → 每个元素都额外的多了一个双链表的机制记录它前后元素的位置

在这里插入图片描述在这里插入图片描述

7.4 TreeSet集合的原理

底层基于红黑树实现的排序

  • 对于数值类型:从小到大
Set<Double> ts = new TreeSet<>();
Collections.addAll(ts, 3.14, 5.6, 1.2, 3.14);
System.out.println(ts);     // [1.2, 3.14, 5.6]
  • 对于字符串类型:首字符的顺序
Set<String> ts2 = new TreeSet<>();
Collections.addAll(ts2, "java", "c++", "python", "java");
System.out.println(ts2);    // [c++, java, python]
  • 自定义类型:默认是无法排序的,不知道大小规则

    • 对象类实现Comparable比较接口重写CompareTo方法,指定大小比较规则
    public class Student implements Comparable<Student>{
        private String name;
        private int age;
        private int score;
    
        @Override // 重写compareTo方法 :按照年龄升序排序
        public int compareTo(Student o) {
            return this.age - o.age;
        }    
    }
    
    Set<Student> ts3 = new TreeSet<>();
    
    Collections.addAll(ts3, new Student("小明", 18, 100),
            new Student("小红", 22, 99),
            new Student("小刚", 20, 98),
            new Student("小明", 18, 100));
    System.out.println(ts3);
    
    • public TreeSet 集合自带比较器Comparator对象,指定比较规则
    Set<Student> ts3 = new TreeSet<>(new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.getAge() - o2.getAge();
        }
    });
    
    // 简化:Set<Student> ts3 = new TreeSet<>(Comparator.comparingInt(Student::getAge));
    

在这里插入图片描述

7.5 什么时候用什么集合呢?

在这里插入图片描述

8. Map集合(键值对集合)

{ key1 = value1 , key2 = value2....}

Map<K,V>

⚠️键不重复,值可重复:一个键对应一个值

场景:

  • 需要存储一一对应的数据时
8.1 Map集合分类

在这里插入图片描述

// HashMap无序不重复、无索引
Map<String,Integer> map =  new HashMap<>();
map.put("嫦娥",29);
map.put("紫霞",31);
map.put("太白金星",33);
map.put("太白金星",33);
System.out.println(map);    // {嫦娥=29, 太白金星=33, 紫霞=31}

// LinedHashMap有序不重复、无索引
Map<String,Integer> map2 =  new LinkedHashMap<>();
// Collections时Collection的工具,不是Map的
map2.put("嫦娥",29);
map2.put("紫霞",31);
map2.put("太白金星",33);
map2.put("太白金星",33);
System.out.println(map2);   // {嫦娥=29, 紫霞=31, 太白金星=33}

// TreeMap:按照键可排序,不重复,无索引
Map<String,Integer> map3 =  new TreeMap<>();
map3.put("嫦娥",29);
map3.put("紫霞",31);
map3.put("太白金星",33);
System.out.println(map3);   // {太白金星=33, 嫦娥=29, 紫霞=31}
8.2 Map集合的常用方法

Map是双列集合的祖宗,它的功能是全部双列集合(HashMap,LinkedHashMap,TreeMap)都可以继承的

Map<String, Integer> map = new HashMap<>();
map.put("嫦娥",29);
map.put("紫霞",31);
map.put("太白金星",33);
map.put("太白金星",33);
map.put(null,null);
System.out.println(map);    //{null=null, 嫦娥=29, 太白金星=33, 紫霞=31}

// get(key):根据键获取值
System.out.println(map.get("嫦娥"));  // 29
// getOrDefault(key, defaultValue):根据键获取值,如果找不到就返回默认值
System.out.println(map.getOrDefault("白骨精", 0)); // 0
// containsKey(key):判断集合中是否有这个键
System.out.println(map.containsKey("嫦娥"));  // true
// containsValue(value):判断集合中是否有这个值
System.out.println(map.containsValue(33));  // true
// remove(key):根据键删除键值对
map.remove("嫦娥");
System.out.println(map);    // {null=null, 太白金星=33, 紫霞=31}
// size():获取集合长度
System.out.println(map.size()); // 3
// keySet():获取所有的键
for (String key : map.keySet()) {
    System.out.println(key);    // null, 太白金星, 紫霞
}
// values():获取所有的值
Collection<Integer> values = map.values();
System.out.println(values); // [null, 33, 31]
// clear():清空集合
map.clear();
System.out.println(map);    // {}
// isEmpty():判断集合是否为空
System.out.println(map.isEmpty());  // true
8.3 Map集合的遍历方式
1)键找值

keySet() 方法获取全部键 >>> get() 方法通过遍历键找值

  • 提起Map集合的全部键到一个Set集合中
  • 遍历Set集合,得到每一个键
    • 根据键去找值
Set<String> keys = map.keySet();	//获取全部键
for (String key : keys) {
    System.out.println(key + ":" + map.get(key));
}
2)键值对

把 “ 键值对 ” 看成一个整体进行遍历

💡Map提供的entrySet() 方法 >>> 将键值对转为Entry对象,将Map转为Set集合

// 将Map集合转换为Set集合,Set集合中存储的元素是Map.Entry类型
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
3)Lambda表达式

JDK 1.8 后的新技术

map.forEach((key, value) -> {
    System.out.println(key + ":" + value);
});

在这里插入图片描述

源码解析:内部通过将其转为entry对象后遍历,再转为键值对传给重写的accept() 方法

8.4 Map集合的实现类
1)HashMap集合的底层原理

7.1的Set集合的底层是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据

2)LinkedHashMap的底层原理

LinkedHashSet底层原理基于LinkedHashMap在这里插入图片描述

3)TreeMap的底层原理,同TreeSet集合
  • 特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
  • 原理:TreeMapTreeSet集合的底层原理是一样的,都是基于红黑树实现的排序

排序方式

  • 让类实现Comparable接口,重写比较规则,同TreeSet
  • TreeMap集合有一个有参数构造器,支持构建Comparator比较器对象,以便用来指定比较规则
// 使用TreeMap的构造器传入比较器对象,并简化
// 按照分数升序
Map<Student, String> studentMap = new TreeMap<>((o1, o2) -> o1.getScore() - o2.getScore());
//  Map<Student, String> studentMap = new TreeMap<>(Comparator.comparingInt(Student::getScore));

studentMap.put(new Student("小明", 18, 100), "北京");
studentMap.put(new Student("小红", 19, 94), "上海");
studentMap.put(new Student("小亮", 22, 98), "广州");
studentMap.put(new Student("小丽", 21, 95), "深圳");
studentMap.put(new Student("小华", 22, 96), "武汉");
System.out.println(studentMap);

8.5 Map集合的案例-统计投票信息

需求

  • 某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多

分析:

  • 出现了一 一对应的数据 >>> 键值对存储
  • 将80个学生选择的数据拿到程序中去
  • 准备一个Map集合用于存储统计的结果
  • 遍历80个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该景点,不存在存入"景点 = 1",存在则其对应值 + 1
// 四个景点
String[] names = {"A", "B", "C", "D"};
// 80名学生的投票数据:用List集合存储
List<String> votes = new ArrayList<>();
Random r = new Random();
for (int i = 0; i < 80; i++) {
    int index = r.nextInt(names.length);    // 随机获取索引 0 1 2 3
    votes.add(names[index]);
}

// 整理投票结果,用Map集合存储统计结果
Map<String,Integer> map = new HashMap<>();
for (String vote : votes) {
    if(map.containsKey(vote)){  // 如果Map集合中包含该景点,则将投票人数加1
        map.put(vote, map.get(vote) + 1);
    }else{  // 如果Map集合中不包含该景点,则将投票人数设置为1
        map.put(vote, 1);
    }
}
  • 统计投票简化代码
for (String vote : votes) {
//  map.put(vote, map.getOrDefault(vote, 0) + 1);
    map.put(vote, map.containsKey(vote) ? map.get(vote) + 1 : 1);
}

在这里插入图片描述


Stream流

👍用于操作集合或者数组的数据

💡Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好

  • 处理数据的步骤

1)准备数据源

List<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周芷若","赵敏","张强","张三丰");

2)获取数据源的Stream流

3)调用流水线的各种方法,对数据进行处理

4)获取处理的结果

newList = list.stream()
        .filter(name -> name.startsWith("张")) // 以“张”开头的 -> 姓张的
        .filter(name -> name.length() == 3)		// 名字是三个字的
        .collect(Collectors.toList());		// 收集方法:将结果流转为list集合
  • 对比传统方式
List<String> newList = new ArrayList<>();
for (String name : list) {
    if (name.startsWith("张") && name.length() == 3) {
        newList.add(name);
    }
}
System.out.println(newList);

在这里插入图片描述

1. 获取Stream流

  • 获取集合的Stream流

    • Collection提供了获取当前集合对象的Stream流的方法
    Collection<String> list = new ArrayList<>();
    Stream<String> s1 = list.stream();
    
  • 获取数组的Stream流

    • Arrays类提供了获取当前数组的Stream流的方法
    String[] arr = {"林青霞", "张曼玉", "王祖蓝"};
    Stream<String> s5 = Arrays.stream(arr);
    System.out.println(s5.count());
    
    • Stream类提供了获取当前接收数据的Stream流的方法
    Stream<String> s6 = Stream.of(arr);
    Stream<String> s7 = Stream.of("林青霞", "张曼玉", "王祖蓝");
    
  • 获取Map集合的Stream流

    • 获取键流
    Stream<String> s2 = map.keySet().stream();
    
    • 获取值流
     Stream<Integer> s3 = map.values().stream();
    
    • 获取键值流:将Map集合转为Set集合,再获取Stream流
    Stream<Map.Entry<String, Integer>> s4 = map.entrySet().stream();
    

2. 常用中间方法

🤔作为中间方法,调完后会返回新流

在这里插入图片描述

ArrayList<Double> list = new ArrayList<>();
Collections.addAll(list, 1.2, 3.4, 5.6, 7.8, 9.0);
System.out.println(list);
// 过滤方法 filter()
list.stream()
        .filter(num -> num > 5)
        .forEach(System.out::println);
System.out.println("--------------------------------------------------");
// 排序方法 sorted() 默认升序  降序需重写Comparator接口的compare方法
// 从大到小 :
Stream<Double> sorted1 =list.stream()
        .sorted((num1, num2) -> num2.compareTo(num1));
sorted1.forEach(System.out::println);
// 从小到大
Stream<Double> sorted2 =list.stream()
        .sorted(Double::compareTo);  // 进一步简化了的(num1, num2) -> num1.compareTo(num2) 
sorted2.forEach(System.out::println);
System.out.println("--------------------------------------------------");
// 获取前几个元素 limit()
list.stream()
        .limit(3)
        .forEach(System.out::println);
// 跳过前几个元素 skip()
list.stream()
        .skip(3)
        .forEach(System.out::println);
System.out.println("--------------------------------------------------");
// 对元素进行加工,并返回对应的新流 map()
list.stream()
        .map(num -> num * 2)
        .forEach(System.out::println);
System.out.println("--------------------------------------------------");
// 合并两个流 concat()
Stream<Double> stream1 = list.stream();
Stream<Double> stream2 = Stream.of(1.2, 3.4, 5.6, 7.8, 9.0);
Stream<Double> stream3 = Stream.concat(stream1, stream2);
System.out.println(stream3.count());

3. 常用终结方法

💡终结:使用后不会返回新的流

在这里插入图片描述

3.1 常用终结方法

在获取最大值时,还需要通过get获取Optional对象中的元素

// 1、统计流中元素的个数:count()
ArrayList<Student> students = new ArrayList<>();
Collections.addAll(students, new Student("张三", 18, 100),
                             new Student("李四", 22, 79),
                             new Student("王五", 20, 80));
System.out.println(students.stream().count());  // 3
System.out.println("===========================================================");
// 2、获取此流运算后的最大值元素:max()
Optional<Student> max = students.stream().max((s1, s2) -> s1.getScore() - s2.getScore());
Student student = max.get();
System.out.println(student);    // Student{name='张三', age=18, score=100}
3.2 收集Stream流:

把Stream流操作后的结果转回到集合或者数组中

⚠️流只能收集一次

在这里插入图片描述

  • collect(Collector collector) : 把流处理后的结果收集到一个指定的集合中去
    • Collectors 又分为 toList()toSet()toMap()
ArrayList<String> names = new ArrayList<>();
Collections.addAll(names, "张三", "李四", "王五", "赵六", "张无忌");
Stream<String> s1 = names.stream().filter(name -> name.startsWith("张"));
// 收集Stream流到Set集合中
Set<String> set = s1.collect(Collectors.toSet());
System.out.println(set);

// 收集Stream流到数组中
Stream<String> s2 = names.stream().filter(name -> name.startsWith("张"));
Object[] arr = s2.toArray();	// 注意返回类型为Object
System.out.println("数组:" + Arrays.toString(arr));
  • toArray() 方法的返回类型是Object[] , 因为 Stream 默认不知道具体的泛型类型,因此只能返回 Object[]
  • Arrays.toString将数组的内容转换为字符串形式,便于打印和调试
// 收集到Map集合中
ArrayList<Student> students = new ArrayList<>();
Collections.addAll(students,
        new Student("张三", 18, 100),
        new Student("李四", 22, 99),
        new Student("王五", 20, 98),
        new Student("赵六", 19, 97),
        new Student("张无忌", 22, 96)
);

// 简化:Map<String,Integer> map = students.stream().collect(Collectors.toMap(Student::getName, Student::getAge));
Map<String, Integer> map = students.stream().collect(Collectors.toMap(new Function<Student, String>() {
          @Override
          public String apply(Student student) {
              return student.getName();
          }
      }, new Function<Student, Integer>() {
          @Override
          public Integer apply(Student student) {
              return student.getAge();
          }
      }
));

System.out.println(map);

在这里插入图片描述


http://www.kler.cn/a/589140.html

相关文章:

  • 探索 Rust 高效 Web 开发:Hyperlane 框架深度解析
  • Spring Boot 参数校验异常与错误编码映射方案
  • 线性回归中的最小二乘法:直接法与梯度下降的比较
  • Android 高版本兼容的原生定位工具类封装(支持 Android 5.0 到 Android 14)
  • kotlin中的模块化结构组件
  • 机器人交社保属于“无稽之谈”?
  • 《Operating System Concepts》阅读笔记:p309-p330
  • uni-app 生命周期
  • 数学建模 第二节
  • 深度学习模型压缩:非结构化剪枝与结构化剪枝的定义与对比
  • 基于C#的以太网通讯实现:TcpClient异步通讯详解
  • 政策助力,3C 数码行业数字化起航
  • 考研408-数据结构完整代码 线性表的顺序存储结构 - 顺序表
  • 运维工具推荐 -- 宝塔面板:一键部署服务器
  • nexus的使用
  • Linux搭建conda虚拟环境流程
  • never_give_up
  • 聊一下CSS层叠
  • Centos离线安装perl
  • redis zset基本介绍以及底层实现