函数式编程-Stream流

函数式编程

1. 概述

  1. 提高代码可读性
  2. 大数据量效率高

1.1函数式编程思想

1.1.1概念

函数式编程关注对数据进行什么操作

面向对象关注用什么对象完成什么事

1.1.2优点
  1. 简洁
  2. 易理解
  3. 易于“并发编程”

2. Lambda表达式

2.1概述

jdk8认可的一种语法,对匿名内部类的写法进行简化。是函数式编程的一个重要体现。

2.2 核心原则

可推导时可省略

方法名可推导方法名可省略

参数名可推导参数名可省略

2. 3 基本格式

(参数列表)->{代码}

我们在创建线程并启动时可以使用匿名内部类的写法:

new Thread(new Runnable() {
    @Override
    public void run() { 
    }
}).start();

可以使用Lambda的格式对其进行修改。修改后如下:

new Thread(()->{
}).start();

2.4 省略规则

  • 参数类型可以省略
  • 方法体只有一句代码时大括号return和唯一一句代码的分号可以省略
  • 方法只有一个参数时小括号可以省略
  • 以上这些规则都记不住也可以省略不记

3.Stream流

3.1概述

java8的Stream使用的是函数式编程模式,可以对集合或数组进行链状流式操作。方便我们对集合和数组操作。

3.2入门

3.2.1创建流

collection:集合对象.stream();

List list = new ArrayList();
Stream stream = list.stream;

数组:Arrays.stream(“数组”)或者Stream.of

   //Stream.of()
    Integer[] array = {1, 2, 3, 4, 5};
    Stream<Integer> array1 = Stream.of(array);
    //Arrays.stream()
    Stream<Integer> stream = Arrays.stream(array);

map:先转成Collection再.stream

    Map<String,Integer> map = new HashMap<>();
    map.put("蜡笔小新",19);
    map.put("黑子",17);
    map.put("日向翔阳",16);
    Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
3.2.2中间操作

filter:对流中元素进行过滤,符合条件的留在流中.

.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
     return author.getAge()>18;
     }
 })
 .filter(author -> author.getAge()>18)    
  

map:可以把对流中的元素进行计算或转换。

 List<Author> authors1 = getAuthors();
        authors1.stream()
                .distinct()
                .map(new Function<Author, String>() {
                    public String apply(Author author) {
                        return author.toString();
                    }
                })
                .forEach(author->{
                    System.out.println(author.getClass());
                });
//lambda表达式写法
 authors1.stream()
                .distinct()
                .map(author -> author.toString()
                )
                .forEach(author->{
                    System.out.println(author.getClass());
                });
/*结果全转成String类型
class java.lang.String
class java.lang.String
class java.lang.String
*/

distinct:去重(根据equals方法),根据需求选择是否重写equals

     authors.stream()
                .distinct()
                .forEach(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(author);
                    }
                });

sorted:对流中内容进行排序(如果流中对象实现了Comparable接口中的compareto()方法则只需要调用sorted()的无参方法,否则调用有参方法)

    authors.stream()
                .distinct()
                .sorted(new Comparator<Author>() {
                    @Override
                    public int compare(Author o1, Author o2) {
                        return o2.getAge()-o1.getAge();
                    }
                })
                .forEach(author->{
                    System.out.println(author);
                });
	//lambda表达式写法
  authors1.stream()
                .distinct()
                .sorted((author1,author2)->author1.getAge()-author2.getAge())
                .forEach(author->{
                    System.out.println(author);
                });

limit:设置流的长度限制(超过的会被舍弃)

 authors1.stream()
                .distinct()
                .sorted(new Comparator<Author>() {
                    @Override
                    public int compare(Author o1, Author o2) {
                        return o2.getAge()-o1.getAge();
                    }
                })
                .limit(2)
                .forEach(author->{
                    System.out.println(author);
                });

skip:跳过流中前n个元素,返回剩下的元素

       authors1.stream()
                .distinct()
                .sorted((author1,author2)->author1.getAge()-author2.getAge())
                .skip(1)
                .forEach(author->{
                    System.out.println(author);
                });

flatMap: flatMap可以把一个对象转换成多个对象作为流中的元素。(map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。)

 authors1.stream()
                .distinct()
                .sorted((author1,author2)->author1.getAge()-author2.getAge())
                .flatMap(author->author.getBooks().stream())
     			//将author中的books(list)也转为流对象并且进行拼接
                .distinct()
                .sorted((b1,b2)-> (int) (b1.getId()-b2.getId()))
                .forEach(author->{
                    System.out.println(author);
                });
/*
如果用map出来的就不是一条一条的book信息而是一个类型值
*/
 authors1.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream()
                )
                .flatMap(book-> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(author->{
                    System.out.println(author);
                });

3.2.3终结操作

forEach:对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作

 authors1.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream()
                )
                .flatMap(book-> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(author->{
                    System.out.println(author);
                });

count:获取流中元素个数

  long count = authors1.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream()
                )
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .count();
        System.out.println(count);

max and min:最大值和最小值

//最大  
Optional<Integer> max = authors1.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .map(book -> book.getScore())
                .max((b1, b2) -> b1 - b2);
        System.out.println(max.get());
//最小
  Optional<Integer> max = authors1.stream()
                .distinct()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .map(book -> book.getScore())
                .min((b1, b2) -> b1 - b2);

collect:流转换成collect集合

  1. 转成List:Collectors.toList()

            List<String> collect = authors1.stream()
                    .distinct()
                    .map(author -> author.getName())
                    .collect(Collectors.toList());
    
  2. 转成Set : Collectors.toSet()

        Set<String> collect = authors1.stream()
                    .distinct()
                    .map(author -> author.getName())
                    .collect(Collectors.toSet());
    
  3. 转成Map

      Map<String,Integer> collect = authors1.stream()
          //一定要去重,因为map键不能重复
                    .distinct()
                    .collect(Collectors.toMap(author -> author.getName(), author -> author.getAge()));
            System.out.println(collect);
    
查找与匹配
  1. anyMatch:可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型。(只要有一个满足就行)

       boolean b = authors1.stream()
                    .distinct()
                    .anyMatch(new Predicate<Author>() {
                        @Override
                        public boolean test(Author author) {
                            return author.getAge() > 10;
                        }
                    });
    //简化
          boolean b = authors1.stream()
                    .distinct()
                    .anyMatch( author-> author.getAge() > 10);
    
    1. allMatch:所有都满足返回true

          boolean c = authors1.stream()
                      .distinct()
                      .allMatch( author-> author.getAge() > 18);
      
    2. noneMatch:所有都不符合返回true

      	 boolean b = authors1.stream()
                    	  .noneMatch(author -> author.getAge() > 100);
      
    3. findAny:获取随机一个元素(不能保证)

              Optional<Author> any = authors1.stream()
                      .distinct()
                      .findAny();
      //不建议使用get,有可能是空
              System.out.println(any.get());
      //建议使用这个
         any.ifPresent(new Consumer<Author>() {
                 @Override
                 public void accept(Author author) {
                     System.out.println(author);
                 }
             });
      
    4. findFirst:获取第一个元素

          Optional<Author> any = authors1.stream()
                      .distinct()
                      .findFirst();
      //建议使用这个
         any.ifPresent(new Consumer<Author>() {
                 @Override
                 public void accept(Author author) {
                     System.out.println(author);
                 }
             });
      
    5. reduce归并 :对流进行操作,按照你定的规则返回一个结果

      //底层
        T result = identity;
      for (T element : this stream)
      	result = accumulator.apply(result, element)
      return result;  
      
      //将所有作者的名称拼接起来
        String reduce = authors1.stream()
                      .distinct()
                      .map(author -> author.getName())
                      .reduce("", new BinaryOperator<String>() {
                  
                          @Override
                          public String apply(String s, String s2) {
                              return s + s2;
                          }
                      });
              System.out.println(reduce);
      //简易版
       String reduce = authors1.stream()
                      .distinct()
                      .map(author -> author.getName())
                      .reduce("",(s1,s2)->s1+s2);
              System.out.println(reduce);
      //reduce中传一个参数的
      Optional<String> reduce = authors1.stream()
                      .distinct()
                      .map(author -> author.getName())
                      .reduce((s1, s2) -> s1 + s2);
             reduce.ifPresent(name-> System.out.println(name));
      /*
      源码为
      */
      //设定一个tag为foundAny判断是否是首次输入,如果是首次输入将首个输入赋值给result剩下操作与传初始值的操作一样
          *     boolean foundAny = false;
           *     T result = null;
           *     for (T element : this stream) {
           *         if (!foundAny) {
           *             foundAny = true;
           *             result = element;
           *         }
           *         else
           *             result = accumulator.apply(result, element);
           *     }
           *     return foundAny ? Optional.of(result) : Optional.empty();
      

3.3注意事项

  1. 惰性求值: 流如果没有终结操作,则中间操作不会执行
  2. 流是一次性的:当一个流对象执行完一次终结操作后,再次使用中间操作或者终结操作会报错
  3. 流不会干扰原数据的:流对数据进行操作时不会影响原数据,除非你在流中给引用类型赋值.

4.Optional

4.1概述

代码中经常会出现空指针问题,需要 进行非空判断

4.2使用

4.2.1创建

Option可以将对象封装进去,然后对封装进去的数据进行操作.Option会提供方法进行非空判断.

  1. 静态方法ofNullable

    Author author = getAuthor();
            Optional<Author> authorOptional = Optional.ofNullable(author);
    

    也可以直接将方法的返回值设为Option,这样就可以直接接收到Option对象

    底层代码:非空时调用of方法,空时调用Optional的empty()方法返回空的Optional对象

     public static <T> Optional<T> ofNullable(T value) {
            return value == null ? empty() : of(value);
        }
    
  2. 静态方法of:不会进行非空判断,如果传入为空则报错(不建议使用)

    Author author = getAuthor();
            Optional<Author> authorOptional = Optional.of(author);
    //传入null会报空指针异常
    authorOptional = Optional.of(null);
    
4.2.2 安全消费(使用)

获取到Option对象后肯定需要 使用数据ifPresent:会判断Option对象是否为空,不为空则使用 .

    author.ifPresent(new Consumer<Author>() {
            @Override
            public void accept(Author author) {
                System.out.println(author);
            }
        });
//简化版
     author.ifPresent(au-> System.out.println(au));
4.2.3 获取值

如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候使用get()会出现异常。

源码:

    public T get() {
        //一旦为空则会抛出异常
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
 Optional<Author>  author= Optional.of(null);
//Optional中的value为null时会报错
        Author author1 = author.get();
4.2.4 安全获取值
  1. orElseGet:当Option的value为空,返回你传入的内容.

    源码:

       public T orElseGet(Supplier<? extends T> other) {
           //Option.value是否为空,如果为空返回other.get();other就是你传进来的接口,otherget()就是你传进来的内容
            return value != null ? value : other.get();
        }
    
    Optional<Author>  author= Optional.ofNullable(null);
     // Optional<Author>  author= Optional.ofNullable(new Author(18L,"亚索",18,"托儿所",null));
            Author author1 = author.orElseGet(new Supplier<Author>() {
                @Override
                public Author get() {
                    return new Author(19L,"张三",19,"法外狂徒",null);
                }
            });
            System.out.println(author1);
    
  2. orElseThrow:当Option的value为空时,抛出定义的异常

    源码:

        public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
            //如果value不为空返回value
            if (value != null) {
                return value;
            } else {
                //为空抛出异常
                throw exceptionSupplier.get();
            }
        }
    
       Optional<Author>  author= Optional.ofNullable(null);
            Author value = author.orElseThrow(new Supplier<Throwable>() {
                @Override
                public Throwable get() {
                    return new Exception("value为空");
                }
            });
            System.out.println(value);
    
4.2.5 过滤filter

Optional也有一个filter方法,可以对数据进行过滤,如果原本有数据但是不符合规定,也会返回空value的Optional

源码:

    public Optional<T> filter(Predicate<? super T> predicate) {
        //做非空判断的,判断你传进来的过滤接口是否为空,如果为空抛出异常
     /*
    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }
    */
        Objects.requireNonNull(predicate);
        if (!isPresent())
            /*
        	public boolean isPresent() {
        	//判断value是否为空,为空直接返回空value
        			return value != null;
    		}*/
            return this;
        else
            //如果接口不为空,value不为空,调用实现接口的test方法看最后返回的内容,如果不满足返回空value的Optional
            return predicate.test(value) ? this : empty();
    }
  Optional<Author> author2 = author.filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge() > 20;
            }
        });
4.2.6 判断isPresen

isPresen:判断value是否为null,为null返回false,不为null返回true

源码:

//很简单,判断value是否为null,为null返回false,不为null返回true
public boolean isPresent() {
    return value != null;
}
boolean present = author.isPresent();
4.2.7转换map

map:数据转换也可以做计算,并且Optional在底层做了ofNullable保证不报错

源码:

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        //判断传入的实现的匿名接口不为null,为null会报错
        Objects.requireNonNull(mapper);
        if (!isPresent())
            //isPresent()判断value是否为空,不为空则为进else如果为空调用empty()方法返回空的Optional
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    Optional<String> s = author.map(new Function<Author, String>() {
            @Override
            public String apply(Author author) {
                return author.getName();
            }
    });

5.函数式接口

5.1 概念

接口中只有一个抽象方法的接口,称为函数式接口

JDK的函数式接口都有**@FuncationalInterface**注解进行标识.

5.2常见函数式接口

  1. Consumer 消费接口

    无返回值,因此一般都是使用.

    @FunctionalInterface
    public interface Consumer<T> {
       
        void accept(T t);
       
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }
       
    
  2. Function 计算转换接口

    有R类型的返回值,可以执行运算和转换操作

    @FunctionalInterface
    public interface Function<T, R> {
       
        R apply(T t);
    
    
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
    
    
        default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    
    
        static <T> Function<T, T> identity() {
            return t -> t;
        }
    }
    
  3. Predicate 判断接口

    返回值为boolean类型,判断型接口

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
  4. **Supplier **

    T类型返回值,可以返回你想要的类型的对象

    @FunctionalInterface
    public interface Supplier<T> {
    
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }
    

5.3常用默认方法

用于将Predicate判断接口的拼接
and 相当于&&

源码:

  default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
      //返回自己的test的结果与上传进来的and里面的test
        return (t) -> test(t) && other.test(t);
    }

or相当于||

源码:

 default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

negate相当于!

源码:

  default Predicate<T> negate() {
        return (t) -> !test(t);
    }
  authors1.stream()
                .distinct()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()>10;
                    }
                }.and(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()<23;
                    }
                }))
                .forEach(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(author);
                    }
                });
    }

6方法引用

当方法体重只有一个方法调用时可以对代码进行进一步简化

6.1推荐用法

​ 我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。

​ 当我们方法引用使用的多了慢慢的也可以直接写出方法引用。

6.2 基本格式

​ 类名或者对象名::方法名

6.3详解

6.3.1引用静态方法
使用前提

使用前提 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。

格式

类名::方法名
List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        
        authorStream.map(author -> author.getAge())
                .map(age->String.valueOf(age));

//优化后
 List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();

        authorStream.map(author -> author.getAge())
                .map(String::valueOf);
6.3.2 引用对象的实例方法
格式
对象名::方法名
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法

例如:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(name->sb.append(name));

优化后:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(sb::append);
6.3.4 引用类的实例方法
格式
类名::方法名
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。

例如:

    interface UseString{
        String use(String str,int start,int length);
    }

    public static String subAuthorName(String str, UseString useString){
        int start = 0;
        int length = 1;
        return useString.use(str,start,length);
    }
    public static void main(String[] args) {

        subAuthorName(new UseString() {
            @Override
            public String use(String str, int start, int length) {
                return str.substring(start,length);
            }
        });

	}

优化后如下:

    public static void main(String[] args) {

        subAuthorName(String::substring);

    }
6.3.5 构造器引用

如果方法体中的一行代码是构造器的话就可以使用构造器引用。

格式
类名::new
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。

例如:

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(name->new StringBuilder(name))
                .map(sb->sb.append("张三").toString())
                .forEach(str-> System.out.println(str));

优化后:

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(StringBuilder::new)
                .map(sb->sb.append("张三").toString())
                .forEach(str-> System.out.println(str));

7.高级用法

7.1 基本类型优化

主要还是流对象在操作时会使用包装类,会经常拆箱装箱,导致效率低下.因此可以对这一现象进行优化.

可以使用map操作进行避免

mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble

 authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println)

7.2并行流

大量数据进行操作时,可以使用并行流提高效率,就是多项成.stream对多线程做了优化,可以使用其方法来实现,从而提高效率

parallel:可以把串行流转换成并行流。

//使用parallel需要先得到流对象
authors1.stream().parallel()
                .peek(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(Thread.currentThread()+author.toString());
                    }
                })
                .distinct()
                .filter(author -> author.getAge()>15)
                .forEach(System.out::println);
    }

parallelStream

//使用parallelStream可以直接得到流对象
 authors1.parallelStream()
                .peek(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(Thread.currentThread()+author.toString());
                    }
                })
                .distinct()
                .filter(author -> author.getAge()>15)
                .forEach(System.out::println);
    }

建议看看这个up的视频**:https://www.bilibili.com/video/BV1Gh41187uR

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值