不可变集合、Lambda表达式、Stream流

不可变集合、Lambda表达式、Stream流

创建不可变集合

不能被修改的集合

应用场景

如果某个数据不能被修改,把它防御性的拷贝到不可变集合中是个很好的实践。

当集合对象被不可信的库调用时,不可变形式是安全的。

创建不可变集合

在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合。

方法名称 说明
static<E> List<E> of(E...elements) 创建一个具有指定元素的List集合对象
static<E> Set<E> of(E...elements) 创建一个具有指定元素的Set集合对象
static<K, V> Map<K, V> of(E...elements) 创建一个具有指定元素的Map集合对象

注意:这个集合不能添加、不能删除、不能修改。

示例

List
java 复制代码
package com.louis;

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

/**
 * @author XRY
 * @date 2023年08月30日16:41
 */
public class ImmutableDemo1 {
    public static void main(String[] args) {
        /**
         * 创建不可变的List集合
         * 一旦创建完毕之后,是无法进行修改的,只能查询操作
         */
        List<String> list = List.of("louis", "khan", "Alex");
        System.out.println("list = " + list.toString());

        System.out.println("--------使用迭代器的方法遍历--------");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String con = iterator.next();
            System.out.println(con);
        }
        /*
        * louis
          khan
          Alex
        * */

        System.out.println("---------测试是否能删除-----------");
        list.remove("louis");
        /*Exception in thread "main" java.lang.UnsupportedOperationException
                ....
                at com.louis.ImmutableDemo1.main(ImmutableDemo1.java:32)*/
    }
}
Set
java 复制代码
package com.louis;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * @author XRY
 * @date 2023年08月30日16:41
 */
public class ImmutableDemo2 {
    public static void main(String[] args) {
        /**
         * 创建不可变的Set集合
         * 一旦创建完毕之后,是无法进行修改的,只能查询操作
         */
        Set<String> set = Set.of("louis", "khan");
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            String con = iterator.next();
            System.out.println(con);
        }
        /*
        * louis
          khan
        * */

        set.remove("louis");
        /*
        * Exception in thread "main" java.lang.UnsupportedOperationException
        *       ....
        *       at com.louis.ImmutableDemo2.main(ImmutableDemo2.java:28)      
        * */
    }
}

注意:

当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性。

Map
java 复制代码
package com.louis;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author XRY
 * @date 2023年08月30日16:41
 */
public class ImmutableDemo3 {
    public static void main(String[] args) {
        /**
         * 创建不可变的Map集合
         * 一旦创建完毕之后,是无法进行修改的,只能查询操作
         */

        Map<String, String> map = Map.of("1", "louis", "2", "louis", "3", "alex");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(map.get(key));
        }
        /*
        * alex
          louis
          louis
        * */

        System.out.println("------------------");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value =  entry.getValue();
            System.out.println(key + "=" + value);
        }
        /*
        * 1=louis
          3=alex
          2=louis
        * */
    }
}

注意:

1、键是不能重复的

2、Map里面的of方法,参数是有上限的,最多只能传递20个参数,即10个键值对

原因:可变参数只能存在一个且只能在最后。

java 复制代码
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                           K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
    return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
                                           k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}
ofEntries

如果想要创建Map的不可变集合,且键值对的数量超过了10个。

java 复制代码
package com.louis;

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

/**
 * @author XRY
 * @date 2023年08月30日16:41
 */
public class ImmutableDemo4 {
    public static void main(String[] args) {
        /**
         * 创建Map的不可变集合, 键值对的数量超过10个
         */
        //1、创建一个普通的Map集合
        HashMap<String, String> map = new HashMap<>();
        map.put("1", "a");
        map.put("2", "b");
        map.put("3", "c");
        map.put("4", "d");
        map.put("5", "e");
        map.put("6", "f");
        map.put("7", "g");
        map.put("8", "h");
        map.put("9", "i");
        map.put("10", "j");
        map.put("11", "k");

        //2、利用上面的数据来获取一个不可变的集合
        //获取到所有的键值对对象(Entry对象)
        Set<Map.Entry<String, String>> entries = map.entrySet();

        //把entries变成一个数组(指定类型entry)
        Map.Entry[] array = entries.toArray(new Map.Entry[0]);

        /*toArray方法会比较集合的长度和数组长度两者之间的大小
        如果集合的长度大于数组的长度,表示数据在数组中放不下,此时会根据实际数据的个数重新创建数组。
        如果集合的长度小于等于数组的长度,数据在数组中可以存放,此时不会创建新的数组而是直接使用, 剩余的会默认初始化值null*/
        Map m = Map.ofEntries(array);

    }
}

在JDK10之后,不可变集合可以使用copyOf()方法

java 复制代码
    /**
     * Returns an <a href="#unmodifiable">unmodifiable Map</a> containing the entries
     * of the given Map. The given Map must not be null, and it must not contain any
     * null keys or values. If the given Map is subsequently modified, the returned
     * Map will not reflect such modifications.
     *
     * @implNote
     * If the given Map is an <a href="#unmodifiable">unmodifiable Map</a>,
     * calling copyOf will generally not create a copy.
     *
     * @param <K> the {@code Map}'s key type
     * @param <V> the {@code Map}'s value type
     * @param map a {@code Map} from which entries are drawn, must be non-null
     * @return a {@code Map} containing the entries of the given {@code Map}
     * @throws NullPointerException if map is null, or if it contains any null keys or values
     * @since 10
     */
    @SuppressWarnings({"rawtypes","unchecked"})
    static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
        if (map instanceof ImmutableCollections.AbstractImmutableMap) {
            return (Map<K,V>)map;
        } else {
            return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0]));
        }
    }

Lambda表达式

简介

什么是Lambda

Lambda是JAVA 8 添加的新特性,是一个匿名函数,使用Lambda表达式可以对一个接口进行非常简洁的实现。它是实现接口的一种方式。

示例

java 复制代码
package com.louis;

/**
 * @author XRY
 * @date 2023年08月31日16:16
 */
public class Program {
    //1、实现接口
    MyComparator myComparator = new MyComparator();
    //2、使用匿名内部类
    Comparator comparator = new Comparator() {
        @Override
        public int compare(int a, int b) {
            return a-b;
        }
    };
    //3、使用Lambda表达式实现接口
    Comparator comparator1 = (a, b)->a-b;

    class MyComparator implements Comparator{

        @Override
        public int compare(int a, int b) {
            return a - b;
        }
    }


    interface Comparator{
        int compare(int a, int b);
    }
}

Lambda对接口的要求

虽然可以使用Lambda表达式对某些接口进行简单的实现,但并不是所有的接口都可以使用Lambda表达式实现。要求接口中定义的必须要实现的抽象方法只能是一个。

在JAVA8对接口加了一个新特性:default(可以实现也可以不实现)。

@FunctionalInterface:用来修饰函数式接口,即接口中的抽象方法只有一个。

java 复制代码
@FunctionalInterface
interface Comparator{
    int compare(int a, int b);
}

Lambda基础语法

Lambda是一个匿名函数,最主要包括参数列表和方法体。

():用来描述参数列表

{}:用来描述方法体

->:Lambda运算符,读作goes to

不同类型接口

java 复制代码
package com.louis.interfaces;

/**
 * @author XRY
 * @date 2023年08月31日16:34
 */
public class Interfaces {
    @FunctionalInterface
    public interface LambdaHaveReturnMultipleParameter {
        int test(int a, int b);
    }

    @FunctionalInterface
    public interface LambdaHaveReturnNoneParameter {
        int test();
    }

    @FunctionalInterface
    public interface LambdaHaveReturnSingleParameter {
        int test(int a);
    }

    @FunctionalInterface
    public interface LambdaNoneReturnMultipleParameter {
        void  test(int a, int b);
    }

    @FunctionalInterface
    public interface LambdaNoneReturnNoneParameter {
        void  test();
    }

    @FunctionalInterface
    public interface LambdaNoneReturnSingleParameter {
        void  test(int n);
    }
}

基础语法实现

java 复制代码
package com.louis.syntax;

import com.louis.interfaces.Interfaces;

/**
 * @author XRY
 * @date 2023年08月31日16:36
 */
public class Syntax1 {
    public static void main(String[] args) {
        //1、Lambda表达式的基础语法
        /*Lambda是一个匿名函数,最主要包括参数列表和方法体。
        ():用来描述参数列表
        {}:用来描述方法体
        ->:Lambda运算符,读作goes to*/


        //无参无返回
        Interfaces.LambdaNoneReturnNoneParameter lambda1 = ()->{
            System.out.println("hello Lambda");
        };
        lambda1.test();
        /*hello Lambda*/

        //无返回值单个参数
        Interfaces.LambdaNoneReturnSingleParameter lambda2 = (int a) -> {
            System.out.println(a);
        };
        lambda2.test(2);
        /*2*/

        //无返回值、多个参数
        Interfaces.LambdaNoneReturnMultipleParameter lambda3 = (int a, int b)->{
            System.out.println(a-b);
        };
        lambda3.test(10, 20);
        /*-10*/

        //有返回值、无参数
        Interfaces.LambdaHaveReturnNoneParameter lambda4 = ()->{
            System.out.println("lambda4");
            return 100;
        };
        int test = lambda4.test();
        System.out.println(test);
        /*lambda4
          100*/

        //有返回值、有一个参数
        Interfaces.LambdaHaveReturnSingleParameter lambda5 = (int a)->{
            return a + 2;
        };
        int test1 = lambda5.test(10);
        System.out.println(test1);
        /*12*/

        //有返回值、多个参数
        Interfaces.LambdaHaveReturnMultipleParameter lambda6 = (int a, int b)->{
            return a + b;
        };
        int test2 = lambda6.test(10, 20);
        System.out.println(test2);
        /*30*/
    }
}

Lambda语法精简

1、参数:

参数类型:由于在接口中已经定义了参数的类型和数量,所以在Lambda表达式中参数的类型可以省略。
注意:如果需要省略类型,则每一个参数的类型都要省略。千万不要出现省略一个参数,不省略一个参数类型的请况。

参数小括号:如果参数列表中,参数的数量只有一个。此时小括号可以省略。

2、方法体

方法大括号:如果方法体中只有一条语句,此时大括号可以省略。

方法体有返回:如果方法体中唯一的一条语句是一个返回语句,则在省略掉大括号的同时也必须省略掉return。

java 复制代码
package com.louis.syntax;

import com.louis.interfaces.Interfaces;

/**
 * @author XRY
 * @date 2023年08月31日16:58
 */
public class Syntax2 {
    public static void main(String[] args) {
        //语法精简
        //1、参数类型
        /*参数类型:由于在接口中已经定义了参数的类型和数量,所以在Lambda表达式中参数的类型可以省略。
`       注意:`如果需要省略类型,则每一个参数的类型都要省略。千万不要出现省略一个参数,不省略一个参数类型的请况。*/
        Interfaces.LambdaNoneReturnMultipleParameter lambda1 = (a, b)->{
            System.out.println("Hello Lambda" + (a + b));
        };
        lambda1.test(10, 11);
        /*Hello Lambda21*/

        //2、参数小阔号
        /*如果参数列表中参数数量只有一个,可以省略小括号*/
        Interfaces.LambdaNoneReturnSingleParameter lambda2 = a->{
            System.out.println(a);
        };
        lambda2.test(10);
        /*10*/

        //3、方法大括号:如果方法体中只有一条语句,此时大括号可以省略
        Interfaces.LambdaNoneReturnSingleParameter lambda3 = a-> System.out.println(a);
        lambda3.test(12);
        /*12*/

        //4、方法体有返回:如果方法体中唯一的一条语句是一个返回语句,则在省略掉大括号的同时也必须省略掉return。
        Interfaces.LambdaHaveReturnNoneParameter lambda4 = ()->13;
        int test = lambda4.test();
        System.out.println(test);
        /*13*/
    }
}

Lambda语法进阶

方法引用

方法引用:可以将一个Lambda表达式的实现指向一个已经实现的方法。

语法:方法的隶属者::方法名,如果是静态方法,隶属者就是所在类,如果是非静态,隶属者就是所在对象。

注意:

  • 1、参数数量和类型必须和接口中定义的方法一致
  • 2、返回值的类型也一定需要和接口中定义的方法一致
java 复制代码
package com.louis.syntax;

import com.louis.interfaces.Interfaces;

/**
 * @author XRY
 * @date 2023年08月31日17:19
 */
public class Syntax3 {
    public static void main(String[] args) {
        //方法引用:可以将一个Lambda表达式的实现指向一个已经实现的方法。
        //语法:方法的隶属者::方法名,如果是静态方法,隶属者就是所在类,如果是非静态,隶属者就是所在对象。
        /*注意:
        * 1、参数数量和类型必须和接口中定义的方法一致
        * 2、返回值的类型也一定需要和接口中定义的方法一致
        * */
        

        /*Interfaces.LambdaHaveReturnSingleParameter lambda = a->a*2;*/
        Interfaces.LambdaHaveReturnSingleParameter lambda1 = a->change(a);
        //方法引用:引用了change的方法实现
        Interfaces.LambdaHaveReturnSingleParameter lambda2 = Syntax3::change;

    }

    private static int change(int a){
        return a*2;
    }
}

构造方法引用

Person实体类

java 复制代码
package com.louis.data;

/**
 * @author XRY
 * @date 2023年08月31日17:33
 */
public class Person {
    public String name;
    public Integer age;

    public Person(){
        System.out.println("Person的无参构造方法");
    }

    public Person(String name, Integer age){
        this.age = age;
        this.name = name;
        System.out.println("Person类的有参构造执行了");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

构造方法引用

java 复制代码
package com.louis.syntax;

import com.louis.data.Person;
import com.louis.interfaces.Interfaces;

/**
 * @author XRY
 * @date 2023年08月31日16:36
 */
public class Syntax4 {
    public static void main(String[] args) {
        PersonCreator creator = ()->new Person();
        creator.getPerson();
        /*Person的无参构造方法*/

        //无参构造方法的引用
        PersonCreator creator1 = Person::new;
        creator1.getPerson();
        /*Person的无参构造方法*/

        //有参构造方法的引用
        PersonCreator1 creator2 = Person::new;
        creator2.getPerson("louis", 24);
        /*Person类的有参构造执行了*/

    }
    //场景:
    interface PersonCreator{
        Person getPerson();
    }

    interface PersonCreator1{
        Person getPerson(String name, int age);
    }
}

实例

实例1

在一个ArrayList中有若干个Person对象,将这些Person对象按照年龄降序排序

java 复制代码
package com.louis.exercise;

import com.louis.data.Person;

import java.util.ArrayList;

/**
 * @author XRY
 * @date 2023年08月31日17:47
 */
public class Exercise1 {
    //集合排序ArrayList
    public static void main(String[] args) {
        //场景:已知在一个ArrayList中有若干个Person对象,将这些Person对象按照年龄降序排序
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("a", 10));
        list.add(new Person("b", 8));
        list.add(new Person("c", 3));
        list.add(new Person("d", 9));
        list.add(new Person("e", 7));
        list.add(new Person("f", 5));
        list.add(new Person("g", 1));

        list.sort(((o1, o2) -> o2.age-o1.age));

        System.out.println(list);
        /*[Person{name='a', age=10}, Person{name='d', age=9}, Person{name='b', age=8}, Person{name='e', age=7}, Person{name='f', age=5}, Person{name='c', age=3}, Person{name='g', age=1}]*/
    }
}

实例2

TreeSet:它是一个set集合,但它能够对集合中的内容进行自动排序,如果两个年龄相同只会保留一个,可以添加逻辑进行保留

java 复制代码
package com.louis.exercise;

import com.louis.data.Person;

import java.util.ArrayList;
import java.util.TreeSet;

/**
 * @author XRY
 * @date 2023年08月31日17:47
 */
public class Exercise2 {
    //使用Lambda表达式实现Comparator接口
    // TreeSet:它是一个set集合,但它能够对集合中的内容进行自动排序,如果两个年龄相同只会保留一个,可以添加逻辑进行保留
    public static void main(String[] args) {
        TreeSet<Person> set = new TreeSet<>((o1, o2)->o2.age-o1.age);
        set.add(new Person("a", 10));
        set.add(new Person("b", 8));
        set.add(new Person("c", 3));
        set.add(new Person("d", 9));
        set.add(new Person("e", 7));
        set.add(new Person("f", 5));
        set.add(new Person("g", 1));

        System.out.println(set);
        /*[Person{name='a', age=10}, Person{name='d', age=9}, Person{name='b', age=8}, Person{name='e', age=7}, Person{name='f', age=5}, Person{name='c', age=3}, Person{name='g', age=1}]*/
    }
}

实例3

集合遍历

java 复制代码
package com.louis.exercise;

import java.util.ArrayList;
import java.util.Collections;

/**
 * @author XRY
 * @date 2023年08月31日18:08
 */
public class Exercise3 {
    //遍历结合
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8);

        //将集合中的每一个元素都带入到方法accept中
        list.forEach(System.out::print);
        /*12345678*/

        //输出集合中所有的偶数
        list.forEach(ele->{if(ele%2==0) System.out.print(ele + " ");});
        /*2 4 6 8*/
    }
}

实例4

removeIf:移除满足条件的元素

java 复制代码
package com.louis.exercise;

import com.louis.data.Person;

import java.util.ArrayList;

/**
 * @author XRY
 * @date 2023年08月31日18:58
 */
public class Exercise4 {
    public static void main(String[] args) {
        //需求:删除集合中满足条件的元素
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("a", 10));
        list.add(new Person("b", 8));
        list.add(new Person("c", 3));
        list.add(new Person("d", 9));
        list.add(new Person("e", 7));
        list.add(new Person("f", 5));
        list.add(new Person("g", 1));
        
        //删除集合中年龄大于5的元素
        //将集合中的每一个元素都带入到test方法中,如果返回值是true则删除这个元素
        list.removeIf(ele->ele.age>5);
        System.out.println(list);
        
        /*[Person{name='c', age=3}, Person{name='f', age=5}, Person{name='g', age=1}]*/
    }
}

实例5

java 复制代码
package com.louis.exercise;

import com.louis.data.Person;

import java.util.ArrayList;

/**
 * @author XRY
 * @date 2023年08月31日17:47
 */
public class Exercise5 {
    public static void main(String[] args) {
        //线程实例化
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.print(i + " ");
            }
        });

        thread.start();
        /*0 1 2 3 4 5 6 7 8 9 */
    }
}

函数式接口

系统内置的函数式接口

1、Predicate<T> :参数T ,返回Boolean

​ IntPredicate<T> :参数int ,返回Boolean

​ LongPredicate<T> :参数long ,返回Boolean

​ DoublePredicate<T> :参数double ,返回Boolean

2、Consumer<T>: 参数T,返回值void

​ IntConsumer 参数int,返回值void

​ LongConsumer 参数long,返回值void

​ DoubleConsumer 参数double,返回值void

3、Function<T, R>:参数T,返回值R-->指定类型的参数、指定类型的返回值

​ IntFunction< R>:参数int,返回值R

​ LongFunction< R>:参数long,返回值R

​ DoubleFunction< R>:参数double,返回值R

​ IntToLongFunction:参数int,返回值long

​ IntToDoubleFunction:参数int,返回值double

​ LongToIntFunction:参数long,返回值int

​ LongToDoubleFunction:参数long,返回值double

​ DoubleToIntFunction:参数double,返回值int

​ DoubleToLongFunction:参数double,返回值long

4、Supplier<T>:无参,返回值T

5、UnaryOperator<T>:参数T,返回值T

6、BinaryOperator<T> :参数T,T,返回值T

7、BiFunction<T, U, R> :参数T,U,返回值R

8、BiPredicate<T, U>:参数T、U 返回值boolean

9、BiConsumer<T, U>:参数T,U返回值void

Stream流

示例

场景:创建一个集合,存储多个字符串元素,完成集合的创建和遍历。

java 复制代码
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("王大锤");
list.add("张三三");
list.add("张二三");
list.add("李斯");

要求:

  • 把所有以"张"开头的元素存储到新集合中
  • 把"张"开头,长度为三的元素再存储到新集合中
  • 遍历打印最终结果
java 复制代码
package com.louis.stream;

import java.util.ArrayList;

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo1 {
    public static void main(String[] args) {
        /**
         * 创建集合添加元素,完成以下需求
         * - 把所有以"张"开头的元素存储到新集合中
         * - 把"张"开头,长度为三的元素再存储到新集合中
         * - 遍历打印最终结果
         */

        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("王大锤");
        list.add("张三三");
        list.add("张三四");
        list.add("李斯");

/*        //1、把所有以"张"开头的元素存储到新集合中
        ArrayList<String> list1 = new ArrayList<>();
        for (String name : list) {
            if(name.startsWith("张")){
                list1.add(name);
            }
        }
        System.out.println(list1);

        //2、把"张"开头,长度为三的元素再存储到新集合中
        ArrayList<String> list2 = new ArrayList<>();
        for (String name : list1) {
            if(name.length() == 3){
                list2.add(name);
            }
        }

        //3、遍历打印最终结果
        for (String name : list2) {
            System.out.println(name);
        }*/

        list.stream()
                .filter(name->name.startsWith("张"))
                .filter(name->name.length() == 3)
                .forEach(name->System.out.println(name));
    }
}

stream流

流的思想

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

使用步骤

  1. 先得到一条stream流(流水线),并把数据放上去

    获取方式 方法名 说明
    单列集合 default Stream<E> stream() Collecotion中的默认方法
    双列集合 无法直接使用stream流
    数组 public static<T> Stream<T> stream(T[] array) Arrays工具类中的静态方法
    一堆零散数据 public static<T> Stream<T> of(T...values) Stream接口中的静态方法
  2. 使用中间方法对流水线上的数据进行操作

  3. 使用终结方法对流水线上的数据进行操作

    中间方法-->方法调用完毕后,还可以调用其他方法(如:过滤、转换)

    终结方法-->最后一步,调用完毕之后,不能调用其它方法(如:统计、打印)

示例

单列集合
java 复制代码
package com.louis.stream;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo2 {
    public static void main(String[] args) {
        //1、单列集合获取Stream流
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c", "d", "e");
        //因为Array是Collection中的实现类,所以可以直接使用

        //获取到一条流水线,并把集合中的数据放到流水线上
     /*  Stream<String> stream = list.stream();

        //使用终结方法打印流水线上的所有数据
       stream.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                //s:依次表示流水线上的每一个数据
                System.out.println(s);
            }
        });*/

        list.stream().forEach(s -> {
            System.out.println(s);
        });
        
        /*
        * a
          b
          c
          d
          e
        
        Process finished with exit code 0

        * */
    }
}
双列集合
java 复制代码
package com.louis.stream;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo3 {
    public static void main(String[] args) {
       //双列集合   无             不能直接使用stream流

        //1、创建双列集合
        HashMap<Integer, String> map = new HashMap<>();
        //2、添加数据
        map.put(1, "a");
        map.put(2, "b");
        map.put(3, "c");
        map.put(4, "d");
        //3、获取stream流
        //方式一:获取键的单列集合
        map.keySet().stream().forEach(s-> System.out.println(s));
        /*
        1
        2
        3
        4
        * */
        
        //方式二:
        map.entrySet().stream().forEach(s-> System.out.println(s));
        /*
        1=a
        2=b
        3=c
        4=d
        * */
    }
}
数组
java 复制代码
package com.louis.stream;

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

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo4 {
    public static void main(String[] args) {
       //数组     public static<T> Stream<T> stream(T[] array)     Arrays工具类中的静态方法

        //1、创建数组
        int[] arr = {1, 2, 3, 4, 5, 6};

        //2、获取stream流
        Arrays.stream(arr).forEach(s-> System.out.print(s + " "));
        /*1 2 3 4 5 6 */
    }
}
零散数据

需要注意的是,这些数据的类型必须相同。

java 复制代码
package com.louis.stream;

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

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo5 {
    public static void main(String[] args) {
       //零散的数据
        Stream.of(1, 2, 3, 4, 5).forEach(re-> System.out.print(re + " "));
        /*1 2 3 4 5 */
    }
}

注意:

Stream接口中静态方法of的使用细节:

方法的形参是一个可变参数,可以传递一堆零散的数据,也可以传递数组。但是数组必须是引用类型,如果传递基本数据类型,会把整个数组当作一个元素,放到Stream当中。

stream流的中间方法

名称 说明
Stream<T> filter(Predicate<? super T> predict) 过滤
Stream<T> limit(long maxSize) 获取前几个元素
Stream<T> skip(long n) 跳过前几个元素
Stream<T> distinct() 元素去重,依赖(hashCode和equals方法)
static<T> Stream<T> concast(Stream a, Stream b) 合并a和b两个流为一个流
Stream<R> map(Function<T, R> mapper) 转换流中的数据类型

注意:

  • 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
  • 修改Stream流中的数据,不会影响原来集合中或者数组中的数据

filter

java 复制代码
package com.louis.stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo6 {
    public static void main(String[] args) {
        //filter 过滤
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三", "张三三", "李斯", "王五");

        //把姓张的留下
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                //如果返回值为true,表示当前数据留下
                //如果返回值为false, 表示当前数据舍弃
                return s.startsWith("张");
            }
        }).forEach(re-> System.out.print(re + " "));
        /*张三 张三三 */
        
        list.stream().filter(s->s.startsWith("张")).forEach(re->{
            System.out.print(re + " ");
        });
        /*张三 张三三 */
    }
}

limit

java 复制代码
list.stream().limit(3).forEach(re-> System.out.print(re + " "));
/*张三 张三三 李斯 */

skip

java 复制代码
list.stream().skip(2).forEach(re-> System.out.print(re + " "));
/*李斯 王五 */

distinct

java 复制代码
list.add("张三");
System.out.println(list);
list.stream().distinct().forEach(re-> System.out.print(re + " "));
/*[张三, 张三三, 李斯, 王五, 张三]
  张三 张三三 李斯 王五 */

concat

java 复制代码
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "louis", "khan");
Stream.concat(list.stream(), list1.stream()).forEach(re-> System.out.print(re + " "));
/*张三 张三三 李斯 王五 louis khan */

map

java 复制代码
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"louis-21", "khan-23", "alex-24");
//获取集合中的数字

//第一个类型:表示流中原来的数据类型
//第二个类型:表示要转成的之后的类型

//apply的形参s:表示流中的每一个数据
//返回值:表示转换之后的数据
//当map方法执行完毕之后,流上的数据就变成了整数,所以在下面forEach当中,s依次表示流里面的每一个数据,但这个数据现在就是整数
    list2.stream().map(new Function<String, Integer>() {
    @Override
    public Integer apply(String s) {
        String tar = s.split("-")[1];
        return Integer.parseInt(tar);
    };
}).forEach(re -> System.out.print(re + " "));
*//*21 23 24 *//*

list2.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(re-> System.out.print(re + " "));
/*21 23 24 */

stream流的终结方法

名称 说明
void forEach(Consumer action) 遍历
long count() 统计
toArray() 收集流中的数据,放到数组中
collect(Collector collector) 收集流中的数据,放到集合中

forEach

java 复制代码
package com.louis.stream;

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

/**
 * @author XRY
 * @date 2023年08月31日8:53
 */
public class StreamDemo7 {
    public static void main(String[] args) {

        /*
        *
        *   | void forEach(Consumer action) | 遍历                       |
            | long count()                  | 统计                       |
            | toArray()                     | 收集流中的数据,放到数组中 |
            | collect(Collector collector)  | 收集流中的数据,放到集合中 |*/
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"张三", "张三三", "李斯", "王五");

        //void forEach(Consumer action)
        //accept方法形参s:依次表示流里面的每一个数据
        //方法体:对每一个数据的处理操作(打印)
        list.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.print(s + " ");
            }
        });
        *//*张三 张三三 李斯 王五 *//*
        
        
        list.stream().forEach(s -> System.out.print(s + " "));
       /*张三 张三三 李斯 王五 */    
    }
}

count

java 复制代码
//   long count()  统计

long count = list.stream().count();
System.out.println(count);
/*4*/

Array

java 复制代码
//  toArray()                      收集流中的数据,放到数组中
Object[] arr1 = list.stream().toArray();
System.out.println(Arrays.toString(arr1));
/*[张三, 张三三, 李斯, 王五]*/

//指定类型
//IntFunction的泛型:具体类型的数组
//apply的形参:流中数据的个数,要跟数组长度保持一致
//apply的返回值:具体类型的数组
//方法体:就是创建数组
String[] arr2 = list.stream().toArray(new IntFunction<String[]>() {
    @Override
    public String[] apply(int value) {
        return new String[value];
    }
});

System.out.println(Arrays.toString(arr2));
/*[张三, 张三三, 李斯, 王五]*/

String[] arr3 = list.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(arr3));
/*[张三, 张三三, 李斯, 王五]*/

collect

java 复制代码
package com.louis.stream;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author XRY
 * @date 2023年08月30日19:07
 */
public class StreamDemo8 {
    public static void main(String[] args) {
     ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "louis-男-23", "khan-男-24", "alex-女-21", "jonny-女-22");

        //场景1:收集List集合中所有的男性
        List<String> newList = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toList());
        System.out.println(newList);
        /*[louis-男-23, khan-男-24]*/

        //场景2:收集Set集合中所有的男性
        Set<String> newList1 = list.stream()
                .filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toSet());
        System.out.println(newList1);

        //场景3:收集Map集合中所有的男性,键:姓名、值:年龄
        Map<String, Integer> map = list.stream().filter(s -> "男".equals(s.split("-")[1]))
                /*
                 * toMap:包含两个参数
                 * 参数一:表示键的生成规则
                 * 参数二:表示值的生成规则
                 *
                 * 参数一:
                 *   Function泛型一:表示流中每一个数据的类型
                 *           泛型二:表示Map集合中键的数据类型
                 *       方法apply形参:依次表示流里面的每一个数据
                 *           方法体:生成键的代码
                 *           返回值:已经生成的键
                 *
                 * 参数二:
                 *   Function泛型一:表示流中每一个数据的类型
                 *           泛型二:表示Map集合中键的数据类型
                 *       方法apply形参:依次表示流里面的每一个数据
                 *           方法体:生成值的代码
                 *           返回值:已经生成的值
                 *
                 * */
                .collect(Collectors.toMap(new Function<String, String>() {
                    @Override
                    public String apply(String s) {
                        return s.split("-")[0];
                    }
                }, new Function<String, Integer>() {
                    @Override
                    public Integer apply(String s) {
                        return Integer.parseInt(s.split("-")[2]);
                    }
                }));
        System.out.println(map);
        /*{khan=24, louis=23}*/
        //注意:收集到Map集合当中时,键不能重复,否则会报错

        Map<String, Integer> map1 = list.stream().filter(s -> "男".equals(s.split("-")[1]))
                .collect(Collectors.toMap(s -> s.split("-")[0], re -> Integer.parseInt(re.split("-")[2])));
        System.out.println(map1);
        /*{khan=24, louis=23}*/
    }
}

实例

java 复制代码
package com.louis.practise;

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

/**
 * @author XRY
 * @date 2023年08月31日9:50
 */
public class Demo01 {
    public static void main(String[] args) {
        /*
         * 定义一个集合,并添加一些整数1、2、3、4、5、6、7、8、9、10
         * 过滤基数,只留下偶数
         * 并将结果保存起来
         * */
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> newList = list.stream().filter(s -> s % 2 == 0).collect(Collectors.toList());
        System.out.println(newList);
        /*[2, 4, 6, 8, 10]*/

        /*
        * 创建一个ArrayList集合,并添加以下字段,字符串中前面时姓名,后面是年龄
        * "张三, 23"
        * "李四, 24"
        * "王五, 25"
        * 保留年龄大于等于24,并将集合收集到Map集合中,姓名为键,年龄为值
        * */
        ArrayList<String> list1 = new ArrayList<>();
        Collections.addAll(list1, "张三, 23", "李四, 24", "王五, 25");
        Map<String, String> newList1 = list1.stream().filter(s -> Integer.parseInt(s.split(", ")[1]) >= 24)
                .collect(Collectors.toMap(s -> s.split(", ")[0], re -> re.split(", ")[1]));
        System.out.println(newList1);
        /*{李四=24, 王五=25}*/

        /*
        * 现在有两个ArrayList集合
        * 第一个集合中:存储6名男演员的名字和年龄, 第二个集合中:存储6名女演员的名字和年龄
        * 姓名和年龄之间使用逗号隔开。如:张三,23
        * 要求:
        * 1、男演员只要名字为三个字的前两个人
        * 2、女演员只要姓杨的,并且不要第一个
        * 3、把过滤后的男演员姓名和女演员姓名合并到一起
        * 4、将上面的演员信息封装成一个Actor对象
        * 5、将所有的演员对象都保存在List集合中
        * Actor类:只有:name、age
        * */
        ArrayList<String> list2 = new ArrayList();
        Collections.addAll(list2,"张三,23", "李四,24", "周杰伦,30", "刘德华,50", "王宝强,50", "胡歌,45");
        ArrayList<String> list3 = new ArrayList();
        Collections.addAll(list3, "杨幂,40", "杨紫,35", "朱丹,45", "柳岩,10", "夏紫,12", "杨演员,35");
        List<String> newList2 = list2.stream().filter(s -> s.split(",")[0].length() == 3).limit(2)
                .collect(Collectors.toList());
        List<String> newList3 = list3.stream().filter(s -> s.startsWith("杨")).skip(1)
                .collect(Collectors.toList());
        List<Actor> listAll = Stream.concat(newList2.stream(), newList3.stream())
                .map((String s) -> new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1])))
                .collect(Collectors.toList());
        System.out.println(listAll);
        /*[Actor(name=周杰伦, age=30), Actor(name=刘德华, age=50), Actor(name=杨紫, age=35), Actor(name=杨演员, age=35)]*/
    }
}

Actor

java 复制代码
package com.louis.practise;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author XRY
 * @date 2023年08月31日10:30
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Actor {
    private String name;
    private Integer age;
}
相关推荐
Full Stack Developme1 天前
java.nio 包详解
java·python·nio
零千叶1 天前
【面试】Java JVM 调优面试手册
java·开发语言·jvm
代码充电宝1 天前
LeetCode 算法题【简单】290. 单词规律
java·算法·leetcode·职场和发展·哈希表
li3714908901 天前
nginx报400bad request 请求头过大异常处理
java·运维·nginx
摇滚侠1 天前
Spring Boot 项目, idea 控制台日志设置彩色
java·spring boot·intellij-idea
Aevget1 天前
「Java EE开发指南」用MyEclipse开发的EJB开发工具(二)
java·ide·java-ee·eclipse·myeclipse
黄昏晓x1 天前
C++----多态
java·jvm·c++
Brookty1 天前
【算法】前缀和
java·学习·算法·前缀和·动态规划
少许极端1 天前
算法奇妙屋(七)-字符串操作
java·开发语言·数据结构·算法·字符串操作
懒羊羊不懒@1 天前
Java基础语法—字面量、变量详解、存储数据原理
java·开发语言