【黑马java基础】Lamda, 方法引用,集合{Collection(List, Set), Map},Stream流

news2024/9/20 18:35:47

文章目录

  • JDK8新特性:Lambda表达式
    • 认识Lambda表达式
    • Lambda表达式的省略规则
  • JDK8新特性:方法引用
    • 静态方法的引用
    • 实例方法的引用
    • 特定类型方法的引用
    • 构造器的应用
  • 集合
  • ➡️Collection单列集合体系
  • Collection的常用方法
  • Collection的遍历方法
    • 迭代器
    • 增强for循环
    • lambda表达式
    • 案例
  • List集合
    • List特点、特有方法
    • List的遍历方式
    • ArrayList集合的底层原理
      • ArrayList集合的底层原理
      • ArrayList集合适合的应用场景
    • LinkedList集合的底层原理
      • LinkedList集合的底层原理
      • **LinkedList**新增特有方法
      • **LinkedList**集合适合的应用场景
        • 场景一:可以用来设计队列
        • 场景二:可以用来设计栈
  • Set集合
    • Set集合的特点
    • HashSet集合的底层逻辑
        • 前导知识
          • 哈希值
          • 对象哈希值的特点
          • 哈希表
          • 数据结构(树)
        • HashSet集合底层原理
        • HashSet集合去重复的机制
    • LinkedHashSet集合的底层逻辑
    • TreeSet集合
  • ✅总结(场景选择)
  • 注意事项:集合的并发修改异常问题
  • Collection的其他相关知识
    • 可变参数
    • Collections工具类
    • 综合案例
  • ➡️Map集合
    • 概述
    • 常用方法
    • 遍历方法
      • 方法1:键找值
      • 方法2:键值对
      • 方法3:Lambda
    • 案例 Map集合-统计投票人数
    • HashMap
      • HashMap底层原理
    • LinkedHashMap
      • LinkedHashMap底层原理
    • TreeMap
    • 补充知识:集合的嵌套
  • Stream流
    • 认识Stream
      • Stream流的使用步骤
    • Stream的常用方法
      • 1、获取Stream流
      • 2、Stream流常见的中间方法
      • 3、Stream流常见的终结方法

JDK8新特性:Lambda表达式

认识Lambda表达式

  • Lambda表达式是DK8开始新增的一种语法形式,作用:用于简化匿名内部类的代码写法。

  • 格式:
    在这里插入图片描述

  • 注意:Lambda表达式并不能简化全部匿名内部类的写法,只能简化函数式接口(有且只有一个抽象方法的接口)的匿名内部类

package lambda;

public class LambdaTest1 {
    public static void main(String[] args) {
        Swimming s = new Swimming() {
            @Override
            public void swim() {
                System.out.println("学生游泳");
            }
        };
        s.swim();
    }
}

interface Swimming{
    void swim();
}

简化后:

package lambda;

public class LambdaTest1 {
    public static void main(String[] args) {
//        Swimming s = new Swimming() {
//            @Override
//            public void swim() {
//                System.out.println("学生游泳");
//            }
//        };
//        s.swim();

        //简化后:
        Swimming s = () -> {
            System.out.println("学生游泳");
        };
        s.swim();
    }
}

interface Swimming{
    void swim();
}
  • 将来我们见到的大部分函数式接口,上面都可能会有一个@FunctionalInterface的注解,有该注解的接口就必定是函数式接口。

在这里插入图片描述

Lambda表达式的省略规则

Lambda表达式的省略写法(进一步简化Lambda表达式的写法)

  • 参数类型可以省略不写。
  • 如果只有一个参数,参数类型可以省略,同时()也可以省略。
  • 如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写。
Arrays.setAll(prices, (int value) ->{
    return prices[value] * 0.8;
});

//可以简化为:
Arrays.setAll(prices, (value) ->{
    return prices[value] * 0.8;
});

//接着简化:
Arrays.setAll(prices, value ->{
    return prices[value] * 0.8;
});

//接着简化:
Arrays.setAll(prices, value -> prices[value] * 0.8);

再或者:

Arrays.sort(students, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2){
        return Double.compare(o1.getHeight)
    }
});

//可以简化为:
Arrays.sort(students, (Student o1, Student o2) -> {
    return Double.compare(o1.getHeight(), o2.getHeight()); //升序
});

//接着简化:
Arrays.sort(students, (o1, o2) -> {
    return Double.compare(o1.getHeight(), o2.getHeight()); //升序
});

//接着简化:
Arrays.sort(students, (o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()) );

用来简化函数式接口的匿名内部类

JDK8新特性:方法引用

用于进一步简化Lambda表达式的

方法引用的标志性符号 “ :: ”

静态方法的引用

  • 类名 :: 静态方法
  • 使用场景:如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用

在这里插入图片描述

实例方法的引用

  • 对象名 :: 实例方法

  • 使用场景:如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用

在这里插入图片描述

特定类型方法的引用

  • 类型 :: 方法

  • 使用场景:如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法入参的,则此时就可以使用特定类型的方法引用。

在这里插入图片描述

构造器的应用

  • 类名 :: new
  • 使用场景:如果某个Lambda表达式里只是在创建对象,并且前后参数的情况一致,就可以使用构造器引用

在这里插入图片描述

集合

集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用

  • 集合的体系结构

在这里插入图片描述

在这里插入图片描述

集合分为:Collection单列集合,Map双列集合

➡️Collection单列集合体系

在这里插入图片描述

Collection集合特点:

  • List系列集合:添加的元素是有序(取时的顺序和拿时的顺序是一致的)、可重复(可以往集合中加一模一样的数据)、有索引。
    • ArrayList:有序、可重复、有索引
    • LinkedList:有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不重复、无索引。
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
    • TreeSet:按照大小默认升序排序、不重复、无索引
package collection;

import java.util.ArrayList;
import java.util.HashSet;

public class CollectionTest1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();  //List:有序 可重复 有索引
        list.add("java1");
        list.add("java2");
        list.add("java3");
        list.add("java4");
        list.add("java5");
        System.out.println(list);

        HashSet<String> set = new HashSet<>();   //Set: 无序 无重复 无索引
        set.add("java1");
        set.add("java2");
        set.add("java3");
        set.add("java2");
        set.add("java1");
        System.out.println(set);
    }
}

//Out:
[java1, java2, java3, java4, java5]
[java3, java2, java1]

集合的存储对象存的并不是元素本身,而是元素的地址,通过元素地址到栈里面获取元素

Collection的常用方法

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

  • Collection的常见方法:(单列集合都能用)

在这里插入图片描述

package collection;

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

public class CollectionTest2API {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();

        //1.public boolean add(E e):添加元素,添加成功返回true。
        c.add("java1");
        c.add("java1");
        c.add("java2");
        c.add("java2");
        c.add("java3");
        System.out.println(c);

        //2.public void clear():清空集合的元素。
//        c.clear();
//        System.out.println(c);

        //3.public boolean isEmpty():判断集合是否为空是空返回true,反之。
        System.out.println(c.isEmpty());

        //4.public int size():获取集合的大小。
        System.out.println(c.size());

        //5.public boolean contains(object obj):判断集合中是否包含某个元素。
        System.out.println(c.contains("java3"));
        System.out.println(c.contains("Java3"));   //精确匹配,所以是false

        //6.public boolean remove(E e):删除某个元素:如果有多个重复元素默认删除前面的第一个!
        System.out.println(c.remove("java1"));    //true
        System.out.println(c);

        //7.public Object[] toArray():把集合转换成数组
        Object[] arr = c.toArray();
        System.out.println(Arrays.toString(arr));

        String[] arr2 = c.toArray(new String[c.size()]);   //指定一个String类型的数组
        System.out.println(Arrays.toString(arr2));


        System.out.println("==============================");
        //把一个集合里的全部数据倒入到另一个集合中去
        Collection<String> c1 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");
        Collection<String> c2 = new ArrayList<>();
        c2.add("java3");
        c2.add("java4");
        c1.addAll(c2);  //就是把c2集合的全部数据倒入到c1集合中去
        System.out.println(c1);
        System.out.println(c2);  //不空,相当于拷贝


    }
}
//Out:
[java1, java1, java2, java2, java3]
false
5
true
false
true
[java1, java2, java2, java3]
[java1, java2, java2, java3]
[java1, java2, java2, java3]
==============================
[java1, java2, java3, java4]
[java3, java4]

Collection的遍历方法

迭代器

迭代器是用来遍历集合的专用方式(数组没有选代器),在java中选代器的代表是Iterator。

  • Collection集合获取迭代器的方法

在这里插入图片描述

  • Iterator迭代器中的常用方法

在这里插入图片描述

package collection;

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

public class CollectionDemo1 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("aaa");
        c.add("sss");
        c.add("ddd");
        c.add("fff");
        System.out.println(c);

        // c = [aaa,sss,ddd,fff]
        //      it                  it的位置,it会把数据取出来,然后往后移动一位


        //使用迭代器遍历集合
        //1、从集合对象中获取迭代器对象
        Iterator<String> it = c.iterator();
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());

//        System.out.println(it.next());  //出现异常:NoSuchElementException

        //2、我们应该使用循环结合迭代器遍历集合
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
    }
}
//Out:
[aaa, sss, ddd, fff]
aaa
sss
ddd
fff

增强for循环

在这里插入图片描述

  • 增强for可以用来遍历集合或者数组。
  • 增强for遍历集合,本质就是迭代器遍历集合的简化写法
package collection;

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

public class CollectionDemo2 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("aaa");
        c.add("sss");
        c.add("ddd");
        c.add("fff");
        System.out.println(c);

        // c = [aaa,sss,ddd,fff]
        //      ele                    ele类似游标

        //使用增强for遍历集合或者数组
        //快捷键:c.for 然后回车,改变量名就行
        
        for (String ele : c){
            System.out.println(ele);
        }

        String[] names = {"11","22","33"};
        for (String name : names){
            System.out.println(name);
        }
    }
}

//Out:
[aaa, sss, ddd, fff]
aaa
sss
ddd
fff
11
22
33

lambda表达式

  • Lambda表达式遍历集合:

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

  • 需要使用Collection的如下方法来完成

在这里插入图片描述

在这里插入图片描述

package collection;

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

public class CollectionDemo3 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("aaa");
        c.add("sss");
        c.add("ddd");
        c.add("fff");
        System.out.println(c);

        // c = [aaa,sss,ddd,fff]
        //       s
        
        c.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }
}
//Out:
[aaa, sss, ddd, fff]
aaa
sss
ddd
fff

可以用Lambda进行简化:

c.forEach((String s) -> {
                System.out.println(s);
        });
//再简化
c.forEach(s -> {
                System.out.println(s);
        });
//再简化
c.forEach(s -> System.out.println(s));
//再简化
c.forEach(System.out::println);

案例

在这里插入图片描述

集合的存储对象存的并不是元素本身,而是元素的地址,通过元素地址到栈里面获取元素

List集合

有啥特点?是否有特有功能?适合什么业务场景?

List系列集合:有序、可重复、有索引。(两个底层实现不同,适合的场景不同)

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

List特点、特有方法

List集合特有的方法:

List集合因为支持索引,所以多了很多与索引相关的方法。同时,Collection的功能List也都继承了。
在这里插入图片描述

List list = new ArrayList<>();

//一行经典代码(设计到多态,优雅,用的多)

package list;

import java.util.ArrayList;
import java.util.List;

public class ListTest1 {
    public static void main(String[] args) {
        //1、创建一个ArrayList集合对象(有序、可重复、有索引)
        List<String> list = new ArrayList<>();   //一行经典代码(设计到多态,优雅,用的多)
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        System.out.println(list);

        //2、public void add(int index, E element): 在某个索引位置插入元素。
        list.add(2,"chacha");
        System.out.println(list);

        // 3.public E remove(int index): 根据索引删除元素,返回被删除元素
        System.out.println(list.remove(2));
        System.out.println(list);

        // 4.public E get(int index): 返回集合中指定位置的元素。
        System.out.println(list.get(2));

        // 5.public E set(int index, E element): 修改索引位置处的元素,修改成功后,会返回原来的数据
        System.out.println("========");
        System.out.println(list.set(2, "gaigai"));
        System.out.println(list);

    }
}

//
[aaa, bbb, ccc, ddd]
[aaa, bbb, chacha, ccc, ddd]
chacha
[aaa, bbb, ccc, ddd]
ccc
========
ccc
[aaa, bbb, gaigai, ddd]

List的遍历方式

List集合相比于前面的Collection多了一种可以通过索引遍历的方式,所以List集合遍历方式一共有四种:

  • 普通for循环(只因为List有索引)
  • 迭代器
  • 增强for
  • Lambda表达式
package list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListTest2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("糖宝宝");
        list.add("蜘蛛精");
        list.add("至尊宝");

        //(1)for循环
        for (int i = 0; i < list.size(); i++) {
            //i=0,1,2...
            System.out.println(list.get(i));
        }

        //(2)迭代器。
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        //(3)增强for循环(foreach遍历)
        for (String s : list) {
            //s=0,1,2...
            System.out.println(s);
        }

        //(4)JDK 1.8开始之后的Lambda表达式
        list.forEach(s -> {
            System.out.println(s);
        });
        
    }
}

ArrayList集合的底层原理

ArrayList和LinkedList底层采用的数据结构不同,应用场景也不同

所谓数据结构,就是存储、组织数据的方式

ArrayList集合的底层原理

  • 基于数组实现(数组的特点:查询快、增删慢)

  • 特点:

    • 查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
    • 删除效率低:可能需要把后面很多的数据进行前移。
    • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
  • 原理:

    1. 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组;
    2. 添加第一个元素时,底层会创建一个新的长度为10的数组;
    3. 存满时,会扩容1.5倍,然后把原数组的数据迁移到新数组里;
    4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
  • 注意:数组扩容,并不是在原数组上扩容(原数组是不可以扩容的),底层是创建一个新数组,然后把原数组中的元素全部复制到新数组中去。

ArrayList集合适合的应用场景

1、ArrayList适合:根据索引查询数据,比如根据随机索引取数据(高效)!或者数据量不是很大时!

2、ArrayList不适合:数据量大的同时 又要频繁的讲行增删操作!

LinkedList集合的底层原理

LinkedList集合的底层原理

  • 基于双链表实现(链表的特点:查询慢<无论查询哪个数据都要从头开始找>、增删快)
  • 特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。

在这里插入图片描述

LinkedList新增特有方法

LinkedList新增了:很多首尾操作的特有方法。

方法名称说明
public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素

LinkedList集合适合的应用场景

场景一:可以用来设计队列

先进先出,后进后出。(排号)

只是在首尾增删元素,用LinkedList来实现很合适!

// 1、创建一个队列。
        LinkedList<String> queue = new LinkedList<>();

// 入队
        queue.addLast("1");
        queue.addLast("2");
        queue.addLast("3");
        queue.addLast("4");
        System.out.println(queue);
        
// 出队
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);

//
[1, 2, 3, 4]
1
2
3
[4]
场景二:可以用来设计栈

先进后出,后进先出。

数据进入栈模型的过程称为:压/进栈(push)

数据离开栈模型的过程称为:弹/出栈(pop)

只是在首部增删云素,用LinkedList来实现很合适!

// 2、创建一个栈对象。
        LinkedList<String> stack = new LinkedList<>();

// 压栈(push)
        stack.addFirst("a");
        stack.addFirst("b");
        stack.addFirst("c");
        stack.addFirst("d");
        System.out.println(stack);

// 出栈(pop)
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack.removeFirst());
        System.out.println(stack);

//
[d, c, b, a]
d
c
b
a
[]

但是java对于栈有专门的方法:

// 2、创建一个栈对象。
        LinkedList<String> stack = new LinkedList<>();
// 压栈(push) 等价于 addFirst()
        stack.push("第1颗子弹");
        stack.push("第2颗子弹");
        stack.push("第3颗子弹");
        stack.push("第4颗子弹");
        System.out.println(stack);
// 出栈(pop) 等价于 removeFirst()
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack);

Set集合

Set集合的特点

无序(添加数据的顺序和获取出的数据顺序不一致); 不重复; 无索引;

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

Set set = new HashSet<>();

//创建了一个HashSet的集合对象。 一行经典代码

package set;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetTest1 {
    public static void main(String[] args) {
        // 1、创建一个Set集合的对象
//        Set<Integer> set = new HashSet<>();  //创建了一个HashSet的集合对象。  一行经典代码    HashSet: 无序 不重复 无索引
//        Set<Integer> set = new LinkedHashSet<>();  //LinkedHashSet: 有序 不重复 无索引
        Set<Integer> set = new TreeSet<>();  //TreeSet: 可排序(默认升序) 不重复 无索引
       
        set.add(444);
        set.add(111);
        set.add(333);
        set.add(222);
        set.add(555);
        set.add(333);
        System.out.println(set);
    }
}
//
[555, 444, 333, 222, 111]      HashSet
[444, 111, 333, 222, 555]      LinkedHashSet
[111, 222, 333, 444, 555]      TreeSet

注意:

Set要用到的常用方法,基本上就是Collection提供的!!自己几乎没有额外新增一些常用功能!

HashSet集合的底层逻辑

HashSet : 无序、不重复、无索引

前导知识
哈希值
  • 就是一个int类型的数值,Java中每个对象都有一个哈希值。

  • Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。

    • public int hashCode() : 返回对象的哈希码值
对象哈希值的特点
  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
哈希表
  • JDK8以前:哈希表 = 数组+链表
  • JDK8以后:哈希表 = 数组+链表+红黑树

JDK8之前:

在这里插入图片描述

默认加载因子为0.75的意思是,一旦数组中的数据占满了数组长度的0.75倍了,就开始扩容,扩容到原数组的2倍,然后把原数组的数据放到新数组里

JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树:

在这里插入图片描述

数据结构(树)

二叉树-》二叉查找树-》平衡二叉树-》红黑树

  • 二叉树分为普通二叉树(没啥用),二叉查找树(左小右大,一样不放)

在这里插入图片描述

在这里插入图片描述

二叉查找树存在的问题:当数据已经是排好序的,导致查询的性能与单链表一样,查询速度变慢!

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

在这里插入图片描述

红黑树,就是可以自平衡的二叉树。红黑树是一种增删改查数据性能相对都较好的结构。

在这里插入图片描述

HashSet集合底层原理
  • 是基于哈希表实现的
  • 哈希表是一种增删改查数据性能都较好的数据结构
HashSet集合去重复的机制

HashSet集合默认不能对内容一样的两个不同对象去重复!

比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的!因为比较的是哈希地址,内容一样,哈希地址不一定一样。

如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?

结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法(右键generate->equals() and hashCode()->一路next,finish)

LinkedHashSet集合的底层逻辑

LinkedHashSet:有序、不重复、无索引

  • 依然是基于哈希表**(数组、链表、红黑树)**实现的。
  • 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。

在这里插入图片描述

缺点就是更占内存,空间换时间

TreeSet集合

TreeSet:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)

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

排序规则:

  • 对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。
  • 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。

如果往TreeSet集合中存储自定义类型的元素,比如说Student类型,则需要我们自己指定排序规则,否则会出现异常。

比如:此时运行代码,会直接报错。原因是TreeSet不知道按照什么条件对Student对象来排序。

在这里插入图片描述

TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。

自定义排序规则

方式一
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则

方式二
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象),用于指定比较规则。

public TreeSet(Comparator<? super E> comparator)

方法一:重写compareTo方法

在这里插入图片描述

方法二:Comparator对象

在这里插入图片描述

Lambda简化后:

在这里插入图片描述

注意:如果有自定义了两个比较规则,Treeset会采取就近选择自带的比较器对象进行比较

两种方式中,关于返回值的规则:

  • 如果认为第一个元素 > 第二个元素 返回正整数即可。
  • 如果认为第一个元素 < 第二个元素返回负整数即可。
  • 如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。

✅总结(场景选择)

在这里插入图片描述

在这里插入图片描述

注意事项:集合的并发修改异常问题

集合的并发修改异常:就是使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。

List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李爱花");
list.add("张全蛋");
list.add("晓李");
list.add("李玉刚");
System.out.println(list); // [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]

//需求:找出集合中带"李"字的姓名,并从集合中删除
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String name = it.next();
    if(name.contains("李")){
        list.remove(name);
    }
}
System.out.println(list);

运行上面的代码,会出现下面的异常。这就是并发修改异常:

在这里插入图片描述

为什么会出现这个异常呢?那是因为迭代器遍历机制,规定迭代器遍历集合的同时,不允许集合自己去增删元素,否则就会出现这个异常。

怎么解决这个问题呢?不使用集合的删除方法,而是使用迭代器的删除方法,代码如下:

List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李爱花");
list.add("张全蛋");
list.add("晓李");
list.add("李玉刚");
System.out.println(list); // [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]

//需求:找出集合中带"李"字的姓名,并从集合中删除
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String name = it.next();
    if(name.contains("李")){
        //list.remove(name);
        it.remove(); //当前迭代器指向谁,就删除谁✅
    }
}
System.out.println(list);

由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。

怎么保证遍历集合同时删除数据时不出bug?

  • 使用迭代器遍历集合,用迭代器自己的删除方法删除数据即可。
  • 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做**i --**操作。

Collection的其他相关知识

可变参数

什么是可变参数?

可变参数就是一种特殊形参,定义在方法、构造器的形参列表里,它可以让方法接收多个同类型的实际参数。

什么格式

格式是:数据类型…参数名称;

public class ParamTest{
    public static void main(String[] args){
        //不传递参数,下面的nums长度则为0, 打印元素是[]
        test();	
        
        //传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30]
        test(10,20,30); 
        
        //传递一个数组,下面数组长度为4,打印元素是[10,20,30,40] 
        int[] arr = new int[]{10,20,30,40}
        test(arr); 
    }
    
    public static void test(int...nums){
        //可变参数在方法内部,本质上是一个数组
        System.out.println(nums.length);
        System.out.println(Arrays.toString(nums));
        System.out.println("----------------");
    }
}

注意事项:

最后还有一些错误写法,需要让大家写代码时注意一下,不要这么写哦!!!

  • 一个形参列表中,只能有一个可变参数;否则会报错
  • 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错
  • 可变参数对外接收数据,但是在方法内部,本质上是一个数组

在这里插入图片描述

可变参数的特点和好处

特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。

好处:常常用来灵活的接收数据。

Collections工具类

是一个用来操作集合的工具类

注意Collections并不是集合,它比Collection多了一个s,一般后缀为s的类很多都是工具类。这里的Collections是用来操作Collection的工具类。

Collections提供的常用静态方法

方法名称说明
public static boolean addAll(Collection<? super T> c, T… elements)给集合批量添加元素。Collection下的种类都能用
public static void shuffle(List<?> list)打乱List集合中的元素顺序。仅用于List集合
public static void sort(List list)List集合中的元素进行升序排序
public static void sort(List list,Comparator<? super T> c)List集合中元素,按照比较器对象指定的规则进行排序
public class CollectionsTest{
    public static void main(String[] args){
        //1.public static <T> boolean addAll(Collection<? super T> c, T...e):为集合批量添加数据
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三","王五","李四", "张麻子");
        System.out.println(names);
        
        //2.public static void shuffle(List<?> list):对集合打乱顺序
        Collections.shuffle(names);
        System.out.println(names);
        
        //3.public static <T> void short(List<T list): 对List集合排序
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(5);
        list.add(2);
        Collections.sort(list);
        System.out.println(list);
        
        //4、使用调用sort方法,传递比较器
        Collections.sort(students, new Comparator<Student>(){
    @Override
    public int compare(Student o1, Student o2){
        return o1.getAge()-o2.getAge();
    }
});	
System.out.println(students);
        
        
    }
}

在这里插入图片描述

综合案例

斗地主

➡️Map集合

概述

什么是Map集合

所谓双列集合,就是说集合中的元素是一对一对的。Map集合中的每一个元素是以key=value的形式存在的,一个key=value就称之为一个键值对,而且在Java中有一个类叫Entry类,Entry的对象用来表示键值对对象。

  • Map集合称为双列集合,格式:{key1=value1 , key2=value2 , key3=value3 , …}, 一次需要存一对数据做为一个元素.
  • Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合
  • Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值

Map集合在什么业务场景下使用

需要存储一一对应的数据时,就可以考虑使用Map集合来做。

Map集合体系

在这里插入图片描述

Map集合体系的特点

注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的

  • HashMap(由键决定特点): 无序、不重复、无索引; (用的最多)
  • LinkedHashMap (由键决定特点):有序、不重复、无索引。
  • TreeMap (由键决定特点):按照大小默认升序排序**、**不重复、无索引。

Map<String, Integer> map = new HashMap<>(); // 一行经典代码

public class MapTest1 {
    public static void main(String[] args) {
        // Map<String, Integer> map = new HashMap<>(); // 一行经典代码。 按照键 无序,不重复,无索引。
        Map<String, Integer> map = new LinkedHashMap<>(); // 有序,不重复,无索引。
        map.put("手表", 100);
        map.put("手表", 220); // 后面重复的数据会覆盖前面的数据(键)
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);

        Map<Integer, String> map1 = new TreeMap<>(); // 可排序,不重复,无索引
        map1.put(23, "Java");
        map1.put(23, "MySQL");
        map1.put(19, "李四");
        map1.put(20, "王五");
        System.out.println(map1);
    }
}

常用方法

由于Map是所有双列集合的父接口,所以我们只需要学习Map接口中每一个方法是什么含义,那么所有的Map集合方法就都会用了。

Map的常用方法如下:

方法名称说明
public V put(K key,V value)添加元素
public int size()获取集合的大小
public void clear()清空集合
public boolean isEmpty()判断集合是否为空,为空返回true , 反之
public V get(Object key)根据键获取对应值
public V remove(Object key)根据键删除整个元素
public boolean containsKey(Object key)判断是否包含某个键
public boolean containsValue(Object value)判断是否包含某个值
public Set keySet()获取全部键的集合
public Collection values()获取Map集合的全部值
public class MapTest2 {
    public static void main(String[] args) {
        // 1.添加元素: 无序,不重复,无索引。
        Map<String, Integer> map = new HashMap<>();
        map.put("手表", 100);
        map.put("手表", 220);
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);
        // map = {null=null, 手表=220, Java=2, 手机=2}

        // 2.public int size():获取集合的大小
        System.out.println(map.size());

        // 3、public void clear():清空集合
        //map.clear();
        //System.out.println(map);

        // 4.public boolean isEmpty(): 判断集合是否为空,为空返回true ,反之!
        System.out.println(map.isEmpty());

        // ✅5.public V get(Object key):根据键获取对应值
        int v1 = map.get("手表");
        System.out.println(v1);
        System.out.println(map.get("手机")); // 2
        System.out.println(map.get("张三")); // null

        // 6. public V remove(Object key):根据键删除整个元素(删除键会返回键的值)
        System.out.println(map.remove("手表"));
        System.out.println(map);

        // 7.public  boolean containsKey(Object key): 判断是否包含某个键 ,包含返回true ,反之。(精确匹配!)
        System.out.println(map.containsKey("手表")); // false
        System.out.println(map.containsKey("手机")); // true
        System.out.println(map.containsKey("java")); // false
        System.out.println(map.containsKey("Java")); // true

        // 8.public boolean containsValue(Object value): 判断是否包含某个值。
        System.out.println(map.containsValue(2)); // true
        System.out.println(map.containsValue("2")); // false

        // 9.public Set<K> keySet(): 获取Map集合的全部键。
        Set<String> keys = map.keySet();
        System.out.println(keys);

        // 10.public Collection<V> values(); 获取Map集合的全部值。
        Collection<Integer> values = map.values();
        System.out.println(values);

        // 11.把其他Map集合的数据倒入到自己集合中来。(拓展)
        Map<String, Integer> map1 = new HashMap<>();
        map1.put("java1",  10);
        map1.put("java2",  20);
        Map<String, Integer> map2 = new HashMap<>();
        map2.put("java3",  10);
        map2.put("java2",  222);
        map1.putAll(map2); // putAll:把map2集合中的元素全部倒入一份到map1集合中去。
        System.out.println(map1); //{java3=10, java2=222, java1=10}
        System.out.println(map2); //{java3=10, java2=222}
    }
}

遍历方法

在这里插入图片描述

方法1:键找值

需要用到Map的如下方法:

方法名称说明
public Set keySet()获取所有键的集合
public V get(Object key)根据键获取其对应的值
/**
 *  目标:掌握Map集合的遍历方式1:键找值
 */
public class MapTest1 {
    public static void main(String[] args) {
        // 准备一个Map集合。
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 162.5);
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}

        // 1、获取Map集合的全部键
        Set<String> keys = map.keySet();
        // System.out.println(keys);
        // [蜘蛛精, 牛魔王, 至尊宝, 紫霞]
        //         key
        // 2、遍历全部的键,根据键获取其对应的值
        for (String key : keys) {
            // 根据键获取对应的值
            double value = map.get(key);
            System.out.println(key + "=====>" + value);
        }
    }
}

方法2:键值对

Map集合是用来存储键值对的,而每一个键值对实际上是一个Entry对象。

这里Map集合的第二种方式,是直接获取每一个Entry对象,把Entry存储扫Set集合中去,再通过Entry对象获取键和值。

用到的方法:

Map提供的方法说明
Set<Map.Entry<K, V>> entrySet()获取所有“键值对”的集合
Map.Entry提供的方法说明
K getKey()获取键
V getValue()获取值

在这里插入图片描述

/**
 * 目标:掌握Map集合的第二种遍历方式:键值对。
 */
public class MapTest2 {
    public static void main(String[] args) {
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}
        // entries = [(蜘蛛精=169.8), (牛魔王=183.6), (至尊宝=169.5), (紫霞=165.8)]
        // entry = (蜘蛛精=169.8)
        // entry = (牛魔王=183.6)
        // ...
		
        // 1、调用Map集合提供entrySet方法,把Map集合转换成键值对类型的Set集合
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        for (Map.Entry<String, Double> entry : entries) {
            String key = entry.getKey();
            double value = entry.getValue();
            System.out.println(key + "---->" + value);
        }
    }
}

方法3:Lambda

需要用到Map的如下方法

方法名称说明
default void forEach(BiConsumer<? super K, ? super V> action)结合lambda遍历Map集合
/**
 * 目标:掌握Map集合的第三种遍历方式:Lambda。
 */
public class MapTest3 {
    public static void main(String[] args) {
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        // map = {蜘蛛精=169.8, 牛魔王=183.6, 至尊宝=169.5, 紫霞=165.8}

		//✅遍历map集合,传递Lambda表达式
        map.forEach(( k,  v) -> {
            System.out.println(k + "---->" + v);
        });
        
        
        //上面的原始的遍历架子
        //遍历map集合,传递匿名内部类
        map.forEach(new BiConsumer<String, Double>() {
            @Override
            public void accept(String k, Double v) {
                System.out.println(k + "---->" + v);
            }
        });
    }
}

案例 Map集合-统计投票人数

在这里插入图片描述

public class MapDemo4 {
    public static void main(String[] args) {
        // 1、把80个学生选择的景点数据拿到程序中来。
        List<String> data = new ArrayList<>();
        String[] selects = {"A", "B", "C", "D"};
        Random r = new Random();
        for (int i = 1; i <= 80; i++) {
            // 每次模拟一个学生选择一个景点,存入到集合中去。
            int index = r.nextInt(4); // 0 1 2 3
            data.add(selects[index]);
        }
        System.out.println(data);

        // 2、开始统计每个景点的投票人数
        // 准备一个Map集合用于统计最终的结果
        Map<String, Integer> result = new HashMap<>();

        // 3、开始遍历80个景点数据
        for (String s : data) {
            // 问问Map集合中是否存在该景点
            if(result.containsKey(s)){
                // 说明这个景点之前统计过。其值+1. 存入到Map集合中去
                result.put(s, result.get(s) + 1);
            }else {
                // 说明这个景点是第一次统计,存入"景点=1"
                result.put(s, 1);
            }
        }
        System.out.println(result);
    }
}

需要存储一一对应的数据时,就可以考虑使用Map集合来做

HashMap

HashMap(由键决定特点): 无序、不重复、无索引; (用的最多)

  • HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。

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

在这里插入图片描述

哈希表
JDK8之前,哈希表 = 数组+链表
JDK8开始,哈希表 = 数组+链表+红黑树
哈希表是一种增删改查数据,性能都较好的数据结构。

HashMap底层原理

  1. 利用键计算哈希值,跟值无关。

  2. 如何处理哈希碰撞

    • JDK8之前,如果新元素和老元素位置一样,新元素占据老元素位置,老元素挂到新元素下面

    • JDK8之后,新元素直接挂到老元素下面

    • 挂的长度超过8&数组长度>=64,自动转成红黑树

  3. 基于哈希表实现的,增删改查都较好

  4. HashMap的键依赖*hashCode*方法和equals方法保证键的唯一

    用hashCode算位置,用equals算内容,如果都一样,则两个元素一样

  5. 如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。

LinkedHashMap

LinkedHashMap (由键决定特点):有序、不重复、无索引。

public static void main(String[] args) {
        // Map<String, Integer> map = new HashMap<>();    //  按照键 无序,不重复,无索引。
        LinkedHashMap<String, Integer> map = new LinkedHashMap<>(); //  按照键 有序,不重复,无索引。
        map.put("手表", 100);
        map.put("手表", 220);
        map.put("手机", 2);
        map.put("Java", 2);
        map.put(null, null);
        System.out.println(map);
    }

//Out
{null=null,手表=220Java=2,手机=2}  //HashMap
{手表=220,手机=2Java=2null=null} //LinkedHashMap

LinkedHashMap底层原理

底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。

和LinkedHashSet很像

实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。

在这里插入图片描述

增删改查性能都还可以,有序,不重复,选LinkedHashMap

TreeMap

特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)

原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。

TreeMap集合同样也支持两种方式来指定排序规则:

  • 让类实现Comparable接口,重写比较规则。
  • TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。

排序方式1:写一个Student类,让Student类实现Comparable接口

//第一步:先让Student类,实现Comparable接口
public class Student implements Comparable<Student>{
    private String name;
    private int age;
    private double height;
	//无参数构造方法
    public Student(){}
    //全参数构造方法
    public Student(String name, int age, double height){
        this.name=name;
        this.age=age;
        this.height=height;
    }
    //...get、set、toString()方法自己补上..
    
    //按照年龄进行比较,只需要在方法中让this.age和o.age相减就可以。
    /*
    原理:
    在往TreeSet集合中添加元素时,add方法底层会调用compareTo方法,根据该方法的
    结果是正数、负数、还是零,决定元素放在后面、前面还是不存。
    */
    @Override
    public int compareTo(Student o) {
        //this:表示将要添加进去的Student对象
        //o: 表示集合中已有的Student对象
        return this.age-o.age;
    }
}

排序方式2:在创建TreeMap集合时,直接传递Comparator比较器对象。

/**
 * 目标:掌握TreeMap集合的使用。
 */
public class Test3TreeMap {
    public static void main(String[] args) {
        Map<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return Double.compare(o1.getHeight(), o2.getHeight());
            }
        });
//        Map<Student, String> map = new TreeMap<>(( o1,  o2) ->   Double.compare(o2.getHeight(), o1.getHeight()));
        map.put(new Student("蜘蛛精", 25, 168.5), "盘丝洞");
        map.put(new Student("蜘蛛精", 25, 168.5), "水帘洞");
        map.put(new Student("至尊宝", 23, 163.5), "水帘洞");
        map.put(new Student("牛魔王", 28, 183.5), "牛头山");
        System.out.println(map);
    }
}

这种方式都可以对TreeMap集合中的键排序。注意:只有TreeMap的键才能排序,HashMap键不能排序。

补充知识:集合的嵌套

集合的嵌套就是集合中的元素又是一个集合

在这里插入图片描述

  • 案例分析
1.从需求中我们可以看到,有三个省份,每一个省份有多个城市
  我们可以用一个Map集合的键表示省份名称,而值表示省份有哪些城市
2.而又因为一个省份有多个城市,同一个省份的多个城市可以再用一个List集合来存储。
  所以Map集合的键是String类型,而指是List集合类型
  HashMap<String, List<String>> map = new HashMap<>();
  • 代码如下
/**
 * 目标:理解集合的嵌套。
 * 江苏省 = "南京市","扬州市","苏州市“,"无锡市","常州市"
 * 湖北省 = "武汉市","孝感市","十堰市","宜昌市","鄂州市"
 * 河北省 = "石家庄市","唐山市", "邢台市", "保定市", "张家口市"
 */
public class Test {
    public static void main(String[] args) {
        // 1、定义一个Map集合存储全部的省份信息,和其对应的城市信息。
        Map<String, List<String>> map = new HashMap<>();

        List<String> cities1 = new ArrayList<>();
        Collections.addAll(cities1, "南京市","扬州市","苏州市" ,"无锡市","常州市");
        map.put("江苏省", cities1);

        List<String> cities2 = new ArrayList<>();
        Collections.addAll(cities2, "武汉市","孝感市","十堰市","宜昌市","鄂州市");
        map.put("湖北省", cities2);

        List<String> cities3 = new ArrayList<>();
        Collections.addAll(cities3, "石家庄市","唐山市", "邢台市", "保定市", "张家口市");
        map.put("河北省", cities3);
        System.out.println(map);

        List<String> cities = map.get("湖北省");
        for (String city : cities) {
            System.out.println(city);
        }

        map.forEach((p, c) -> {
            System.out.println(p + "----->" + c);
        });
    }
}

Stream流

JDK8开始最大的改变:Lambda, Stream

认识Stream

也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据

优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好。

在这里插入图片描述

public class StreamTest1 {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        System.out.println(names);
        // names = [张三丰, 张无忌, 周芷若, 赵敏, 张强]
        //          name

        // 找出姓张,且是3个字的名字,存入到一个新集合中去。
        List<String> list = new ArrayList<>();
        for (String name : names) {
            if(name.startsWith("张") && name.length() == 3){
                list.add(name);
            }
        }
        System.out.println(list);

        // 开始使用Stream流来解决这个需求。
        List<String> list2 = names.stream().filter(s -> s.startsWith("张"))
                .filter(a -> a.length()==3).collect(Collectors.toList());
        System.out.println(list2);
    }
}

Stream流的使用步骤

在这里插入图片描述

Stream的常用方法

在这里插入图片描述

1、获取Stream流

  • 获取 集合 的Stream流
Collection提供的如下方法说明
default Stream stream()获取当前集合对象的Stream流
  • 获取 数组 的Stream流
Arrays类提供的如下 方法说明
public static Stream stream(T[] array)获取当前数组的Stream流
Stream类提供的如下 方法说明
public static Stream of(T… values)获取当前接收数据的Stream流
/**
 * 目标:掌握Stream流的创建。
 */
public class StreamTest2 {
    public static void main(String[] args) {
        // 1、如何获取List集合的Stream流?
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "张三丰","张无忌","周芷若","赵敏","张强");
        Stream<String> stream = names.stream();

        // 2、如何获取Set集合的Stream流?
        Set<String> set = new HashSet<>();
        Collections.addAll(set, "刘德华","张曼玉","蜘蛛精","马德","德玛西亚");
        Stream<String> stream1 = set.stream();
        stream1.filter(s -> s.contains("德")).forEach(s -> System.out.println(s));

        // 3、如何获取Map集合的Stream流?
        Map<String, Double> map = new HashMap<>();
        map.put("古力娜扎", 172.3);
        map.put("迪丽热巴", 168.3);
        map.put("马尔扎哈", 166.3);
        map.put("卡尔扎巴", 168.3);

        Set<String> keys = map.keySet();
        Stream<String> ks = keys.stream();

        Collection<Double> values = map.values();
        Stream<Double> vs = values.stream();

        Set<Map.Entry<String, Double>> entries = map.entrySet();
        Stream<Map.Entry<String, Double>> kvs = entries.stream();
        kvs.filter(e -> e.getKey().contains("巴"))
                .forEach(e -> System.out.println(e.getKey()+ "-->" + e.getValue()));

        // 4、如何获取数组的Stream流?
        String[] names2 = {"张翠山", "东方不败", "唐大山", "独孤求败"};
        Stream<String> s1 = Arrays.stream(names2);
        Stream<String> s2 = Stream.of(names2);
    }
}

2、Stream流常见的中间方法

中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。

在这里插入图片描述

 /*
  * 目标:掌握Stream流提供的常见中间方法。
 */
public class StreamTest3 {
    public static void main(String[] args) {
        List<Double> scores = new ArrayList<>();
        Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
        // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
        scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s));

        List<Student> students = new ArrayList<>();
        Student s1 = new Student("蜘蛛精", 26, 172.5);
        Student s2 = new Student("蜘蛛精", 26, 172.5);
        Student s3 = new Student("紫霞", 23, 167.6);
        Student s4 = new Student("白晶晶", 25, 169.0);
        Student s5 = new Student("牛魔王", 35, 183.3);
        Student s6 = new Student("牛夫人", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
        students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
                .sorted((o1, o2) -> o2.getAge() - o1.getAge())
                .forEach(s -> System.out.println(s));

        // 需求3:取出身高最高的前3名学生,并输出。
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .limit(3).forEach(System.out::println);
        System.out.println("-----------------------------------------------");

        // 需求4:取出身高倒数的2名学生,并输出。   s1 s2 s3 s4 s5 s6
        students.stream().sorted((o1, o2) -> Double.compare(o2.getHeight(), o1.getHeight()))
                .skip(students.size() - 2).forEach(System.out::println);

        // 需求5:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
        students.stream().filter(s -> s.getHeight() > 168).map(Student::getName)
               .distinct().forEach(System.out::println);
        //简化前:.map(s -> s.getName())

        // distinct去重复,自定义类型的对象(希望内容一样就认为重复,重写hashCode,equals)
        students.stream().filter(s -> s.getHeight() > 168)
                .distinct().forEach(System.out::println);

        Stream<String> st1 = Stream.of("张三", "李四");
        Stream<String> st2 = Stream.of("张三2", "李四2", "王五");
        Stream<String> allSt = Stream.concat(st1, st2);
        allSt.forEach(System.out::println);
    }
}

3、Stream流常见的终结方法

终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

在这里插入图片描述

有的需求需要我们把结果放到集合或者数组中去,所以需要收集Stream流

收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。

Stream流:方便操作集合/数组的手段; 集合/数组:才是开发中的目的。

Stream提供的常用终结方法说明
R collect(Collector collector)把流处理后的结果收集到一个指定的集合中去
Object[] toArray()把流处理后的结果收集到一个数组中去
Collectors工具类提供了具体的收集方式说明
public static Collector toList()把元素收集到List集合中
public static Collector toSet()把元素收集到Set集合中
public static Collector toMap(Function keyMapper , Function valueMapper)把元素收集到Map集合中
/**
 * 目标:Stream流的终结方法
 */
public class StreamTest4 {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        Student s1 = new Student("蜘蛛精", 26, 172.5);
        Student s2 = new Student("蜘蛛精", 26, 172.5);
        Student s3 = new Student("紫霞", 23, 167.6);
        Student s4 = new Student("白晶晶", 25, 169.0);
        Student s5 = new Student("牛魔王", 35, 183.3);
        Student s6 = new Student("牛夫人", 34, 168.5);
        Collections.addAll(students, s1, s2, s3, s4, s5, s6);
        // 需求1:请计算出身高超过168的学生有几人。
        long size = students.stream().filter(s -> s.getHeight() > 168).count();
        System.out.println(size);

        // 需求2:请找出身高最高的学生对象,并输出。
        Student s = students.stream().max((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get();
        System.out.println(s);

        // 需求3:请找出身高最矮的学生对象,并输出。
        Student ss = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get();
        System.out.println(ss);

        // 需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。
        // 流只能收集一次。
        List<Student> students1 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toList());
        System.out.println(students1);

        Set<Student> students2 = students.stream().filter(a -> a.getHeight() > 170).collect(Collectors.toSet());
        System.out.println(students2);

        // 需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存入到一个Map集合返回。
        Map<String, Double> map =
                students.stream().filter(a -> a.getHeight() > 170)
                        .distinct().collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight()));
        System.out.println(map);

        // Object[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray();
        Student[] arr = students.stream().filter(a -> a.getHeight() > 170).toArray(len -> new Student[len]);
        System.out.println(Arrays.toString(arr));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1936426.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring框架、02SpringAOP

SpringAOP 日志功能 基本方法 分析代码问题 目前代码存在两个问题 代码耦合性高&#xff1a;业务代码和日志代码耦合在了一起 代码复用性低&#xff1a;日志代码在每个方法都要书写一遍 问题解决方案 使用动态代理&#xff0c;将公共代码抽取出来 JDK动态代理 使用JDK动…

Ubuntu系统SSH免密连接Github配置方法

Ubuntu系统SSH免密连接Github配置方法 一、相关介绍1.1 Ubuntu简介1.2 Git简介1.3 Github简介 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、Git本地环境配置工作4.1 安装Git工具4.2 创建项目目录4.3 …

scp免密复制文件

实现在服务器A和服务器B之间使用scp命令免密互相传输文件 1. 在服务器A中免密复制到服务器B 1.1 生成服务器A的公钥私钥 #在服务器A中执行 ssh-keygen -t rsa -P ""命令执行完毕会在服务器A的 ~/.ssh 目录下生成两个文件&#xff1a;id_rsa 和 id_rsa.pub 1.2 拷…

网络爬虫入门(学习笔记)

爬取网页源代码 抓取百度首页的HTML源代码&#xff0c;并将其保存到一个名为baidu.html的文件中。打开这个文件&#xff0c;可以看到一个和百度首页一模一样的页面。 from urllib.request import urlopen# 发送请求并获取响应 response urlopen("http://www.baidu.com&q…

windows中使用Jenkins打包,部署vue项目完整操作流程

文章目录 1. 下载和安装2. 使用1. 准备一个 新创建 或者 已有的 Vue项目2. git仓库3. 添加Jenkinsfile文件4. 成功示例 1. 下载和安装 网上有许多安装教程,简单罗列几个 Windows系统下Jenkins安装、配置和使用windows安装jenkins 2. 使用 在Jenkins已经安装的基础上,可以开始下…

【游戏/社交】BFS算法评价用户核心程度or人群扩量(基于SparkGraphX)

【游戏/社交】BFS算法评价用户核心程度or人群扩量&#xff08;基于SparkGraphX&#xff09; 在游戏和社交网络领域&#xff0c;评估用户的核心程度或进行人群扩量是提升用户粘性和拓展社交圈的关键。广度优先搜索&#xff08;BFS&#xff09;算法以其在图结构中评估节点重要性…

WebRTC通话原理(SDP、STUN、 TURN、 信令服务器)

文章目录 1.媒体协商SDP简介 2.网络协商STUN的工作原理TURN工作原理 3.信令服务器信令服务器的主要功能信令服务器的实现方式 1.媒体协商 比如下面这个例子 A端与B端要想通信 A端视频采用VP8做解码&#xff0c;然后发送给B端&#xff0c;B端怎么解码&#xff1f; B端视频采用…

使用vscode搜索打开的文件夹下的文件

右键空白处打开命令面板 摁一次删除键&#xff0c;删除掉图中的大于号 这样就能够找到例化的模块&#xff0c;文件具体在哪个位置&#xff0c;然后打开了

pdf怎么压缩的小一点?PDF压缩变小的6种方法(2024全新)

pdf怎么压缩的小一点&#xff1f;首先&#xff0c;PDF文件可以进行压缩。职场文档传阅还是比较建议PDF压缩&#xff0c;PDF文件可以无障碍访问&#xff0c;保持原始文本、图像和表格&#xff0c;无需担心展示效果差异等等优势&#xff0c;成为我们日常工作中不可或缺的一部分。…

Grid Search:解锁模型优化新境界

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

8、添加第三方包

目录 1、安装Django Debug Toolbar Django的一个优势就是有丰富的第三方包生态系统。这些由社区开发的包&#xff0c;可以用来快速扩展应用程序的功能集 1、安装Django Debug Toolbar Django Debug Toolbar位于名列前三的第三方包之一 这是一个用于调试Debug Web应用程序的有…

win_vscode_wsl_ubuntu教程

文章目录 win_vscode_wsl_ubuntu教程 win_vscode_wsl_ubuntu教程 在启用或关闭Windows功能处开启适用于Linux的Windows子系统和虚拟机平台&#xff0c;可能会需要重启电脑 设置wsl # 将wsl2设置为默认版本 C:\Users\Administrator>wsl --set-default-version 2 有关与 WS…

PhantomJs将html生成img|pdf

PhantomJS PhantomJS是一个可编程的无头浏览器&#xff0c;‌它基于WebKit内核&#xff0c;‌通过JavaScript API进行脚本化操作&#xff0c;它对各种web标准有快速和原生化的支持&#xff0c;包括DOM处理、CSS选择器、JSON、Canvas和SVG。‌无头浏览器指的是一个完整的浏览器内…

QT反射内存读写操作

反射内存技术适用于通过以太网、光纤通道或其他串行网络连接计算机或可编程逻辑控制器的应用&#xff0c;尤其在实时交互和高通信要求的系统中表现突出。虽然价格较高&#xff0c;但其易用性和性能优势带来了显著回报。反射内存能够在微秒级内将计算机的内存副本分发到整个网络…

Element UI DatePicker选择日期范围区间默认显示前一个月和本月

要求&#xff1a;点击el-date-picker选择时间范围时&#xff0c;默认展开当月和上个月。 但是Element UI的组件默认展开的是本月和下一个月&#xff0c;如下图所示&#xff1a; 改为 <span click"changeInitCalendarRange"><el-date-picker v-model"r…

QT获取电脑网卡IP等信息

文章目录 一、背景信息二、代码实现 一、背景信息 电脑有一个或者多个网卡&#xff0c;如下图所示&#xff1a; 一个网卡又可以配有多个IP地址&#xff0c;包括 IPv4 和 IPv6 地址&#xff1a; 二、代码实现 以下代码实现了查找电脑所有网卡&#xff0c;并获取某个网卡的 IP …

苹果电脑crossover怎么下载 苹果电脑下载crossover对电脑有影响吗 MacBook下载crossover软件

CodeWeavers 发布了 CrossOver 24 版本更新&#xff0c;不仅兼容更多应用和游戏&#xff0c;得益于 Wine 9.0 带来的 7000 多项改进&#xff0c;CrossOver 还可以在 64 位系统上运行Windows应用的软件&#xff0c;使得用户可以在Mac系统中轻松安装使用仅支持Windows系统运营环境…

ubuntu源码安装Odoo

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 Odoo具有非常多的安装方式&#xff0c;除了我最爱用的 apt-get install&#xff0c;我们还可以使用git拉取Odoo源码进行安装。 本次示例于ubuntu20.04 Desktop上进行操作&#xff0c;理论上在ubuntu14.04之后都可以用此操作。 …

uniapp 小程序 嵌套 webview 返回需要点击两次

uniapp 小程序 嵌套 webview 返回需要点击两次 先 上图 小程序也监听不到 返回事件在网上找了一圈 都没有理想的答案&#xff0c;猜测 是因为嵌入的页面中有问题果然 小程序中嵌入的代码 <view><web-view :src"urlSrc" ></web-view></view>…

【思科】链路聚合实验配置和背景

【思科】链路聚合实验配置和背景 背景链路聚合基本概念链路聚合聚合接口 思科链路聚合协议01.PAgP协议02.LACP协议 思科链路聚合模式LACP协议模式PAgP协议模式ON模式 实验准备配置二层链路聚合LACP协议模式SW1SW2PC1PC2查看LACP聚合组建立情况查看LACP聚合端口情况查看逻辑聚合…