✨你好啊,我是“ 罗师傅”,是一名程序猿哦。 🌍主页链接:楚门的世界 - 一个热爱学习和运动的程序猿 ☀️博文主更方向为:分享自己的快乐 briup-jp3-ing ❤️一个“不想让我曾没有做好的也成为你的遗憾”的博主。 💪很高兴与你相遇,一起加油!
前言
目标:JVM、JDK新特性、JDK源码、高并发、MySql优化
本章目标:函数式接口、Lambda表达式、方法引用、Optional、Stream
接口方法
整型常量数据可以用下划线分隔表示,在整数的值比较大的时候, 使数值更加直观
1 2 3 4 5 6 7 8 9 10 11 12 public class Test01_NumberUnderlineTest { public static void main (String[] args) { long a = 1000_000_000L ; int b = 0b00000000_00000000_00000000_00000111 ; int c = 1234_5678_9 ; int d = 123_456_789 ; System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); System.out.println("d = " + d); } }
默认方法 JDK1.8中,接口里面可以定义默认方法。
1 2 3 4 5 interface InterfaceName { default returnType methodName (arg-list) { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface FirstInterface { void before () ; default void test () { System.out.println("Default method in FirstInterface" ); } } class FirstClass implements FirstInterface { @Override public void before () { System.out.println("我是FirstInterface中的抽象方法,所有实现类必须实现!" ); } } public class DefaultMethod { public static void main (String[] args) { FirstClass fc = new FirstClass (); fc.test(); fc.before(); } }
默认方法存在的两大优势:
可以让接口更优雅的升级,减少使用人员操作的负担
可以让实现类中省略很多不必要方法的空实现
(不必随着接口方法增加,从而修改实现代码,因为默认方法在字了中可以不用实现)
接口继承及默认方法冲突
方法调用的判断规则:
类中声明的方法优先级最高
如果无法依据第一条进行判断,那么子接口的优先级更高
最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写 )方法来确定方法的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class A { public void test () { System.out.println("Default Method test in A" ); } } interface B { default void test () { System.out.println("default method test in B" ); } } class C extends A implements B { public static void main (String[] args) { C c = new C (); c.test(); } }
因为A是类,B是接口,A里边的test的优先级更高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface A { default void test () { System.out.println("Default Method test in A" ); } } interface B extends A { default void test () { System.out.println("default method test in B" ); } } class C implements A ,B{ public static void main (String[] args) { C c = new C (); c.test(); } }
因为B重写了A中的默认方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 interface A { default void test () { System.out.println("Default Method test in A" ); } } interface B { default void test () { System.out.println("default method test in B" ) } } class C implements A ,B{ public void test () { 名.super .方法名(); A.super .test(); B.super .test(); } }
静态方法 JDK1.8中,接口里面可以定义静态方法。
1 2 3 4 5 6 7 interface InterfaceName { static returnType methodName (arg-list) { } } InterfaceName.methodName();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface StaticMethod { public static void test () { System.out.println("我是StaticMethod接口中的静态方法!" ); } } class A implements StaticMethod {}class Test { public static void main (String args[]) { StaticMethod.test(); } }
注意,接口中的静态方法,只能使用当前接口的名字来调用
Lambda Lambda表达式是JDK1.8新增的一种语法,以确保在java代码中可以支持函 数式编程,让代码的表示含义更简单。
理解lambda表达式之前,需要先理解行为参数化和函数式编程的概念。
行为参数化 在java代码中,我们如果需要运算,那么大部分我们的过程是确定的,但是实际参与运算的数却不确定。
1 2 3 4 5 6 7 8 public static int calculate (int a,int b, 此处传递一个计算行为) { int result = a; for (int i = a+1 ;i<=b;i++){ 调用计算行为,操作当前数据 } return result; }
我们可以将这个 计算行为 ,用接口的形式进行定义:
1 2 3 interface Action { int action (int result, int i) ; }
这时候, calculate 方法就可以这样进行定义:
1 2 3 4 5 6 7 public static int calculate (int a, int b, Action cal) { int result = a; for (int i = a+1 ; i<=b; i++) { result = cal.action(result, i); } return result; }
具体的使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Add implements Action { public int action (int result,int i) { return result+i; } } class Multiply implements Action { public int action (int result,int i) { return result*i; } } class Test { public static void main (String[] args) { int result = 0 ; result = calculate(3 ,5 ,new Add ()); System.out.println(result); result = calculate(3 ,5 ,new Multiply ()); System.out.println(result) } }
上述代码中,将我们要执行的核心计算操作,定义成一个参数,传给了calculate方法
我们可以通过这个参数,给方法传递不同的行为,来实现不同操作,这就是行为参数化
java中,不允许孤立的代码存在,我们要想将行为(核心操作代码)传递给 calculate 方法,就必须要将这些核心操作代码,包装在一个实现了 Action的类中。
为了减少声明和定义类,Java提供了匿名内部类 的实现,来简化我们刚才的调用过程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Test { public static void main (String[] args) { int result = 0 ; result = calculate(3 ,5 ,new Action (){ public int action (int result,int i) { return result+next; } }); System.out.println(result); result = calculate(3 ,5 ,new Action (){ public int action (int result,int i) { return result*next; } }); System.out.println(result) } }
但是仍然存在相同的代码:
new Action()
public int action(int result,int next){}
我们真正关心的是:
方法的参数列表
方法中的核心操作代码
方法的返回类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test02_LambdaClass { public static void main (String[] args) { Action action1 = new Action () { @Override public int action (int result, int i) { return result + i; } }; Action action2 = (result, i) -> result + i; System.out.println(action1.getClass()); System.out.println(action2.getClass()); } public static int calculate (int a, int b, Action cal) { int result = a; for (int i = a + 1 ; i <= b; i++) { result = cal.action(result, i); } return result; } }
思考,对Action接口,分别使用匿名类和Lambda表达式进行实现,然后调 用俩个对象getClass方法,观察结果有何不同?
$$Lambda表示是Lambda表达式
Lambda$1表示是Lambda表达式中的第一个匿名内部类
/1324119927:唯一标识这个lambda表达式的内部类
函数式编程
函数式编程,和面向对象编程、以及面向过程编程一样,它们都是编程的一种方式
函数式编程时面向数学的抽象,将计算过程描述为一种表达式求值。f(x) = y
严格意义上的表达式,就是由数据和操作我按照一定的规则,组合再一切形成的序列,并且所有的表达式都是有返回结果的 ,这里所说的表达式和代码语句的最大区别。
但是在java中,是允许函数式编程中没有任何返回值的,因为java中有关键字void(也可以看做是一个结果),但是在其他一些专门的函数式编程语言中,是没有void的。
所以,jdk1.8中,可以把Action接口的实现,简写为:
1 2 3 4 5 6 7 Action add = (result,next) -> result + next;
Lambda概述
Lambda表达式,可以用来表示一个函数,它只关注函数的参数列表, 函数主体、返回类型 ,并且可以将此函数作为一个参数,进行传递。
在java中,Lambda表达式还有另一个存在的意义,那就是作为一个接口的实现类对象 。
1 2 3 4 5 6 7 8 9 public class Test { public static void main (String[] args) { Action a = (str) -> str.length(); System.out.println(a.test("hello" )); } } interface Action { public int test (String str) ; }
可以看出,Lambda表达式,虽然可以通过(参数列表,函数主体,返回类型)三部分来标识一个具体的函数操作,但是它必须是依托一个接口才行,所以Lambda表达式就是对接口 中抽象方法的实现
Lambda使用
函数式接口
有且只有一个抽象方法的接口,就是函数式接口。该接口中,也允许有其他的默认方法和静态方法
1 2 3 4 5 6 7 8 9 10 11 public interface Action { void action () ; } public interface DefaultAction { void action () ; default void test () { System.out.println("我是默认实现" ); } }
1 2 3 4 5 6 7 8 9 10 11 public interface Calculate { int add (int a,int b) ; } 己一个,继承一个 public interface SubCalculate extends Calculate { double add (double a,double b) ; } public interface Noting {}
JDK1.8中,针对函数式接口,新增了一个注解 @FunctionalInterface ,用来检查被标注的接口,是不是一个函数式接口,如果不是,那么编译器会报错。
但是,该注解不是必须要用的,它只是会让编辑器帮我们检查一下而已,以免出现接口中抽象个数不是1 的情况
1 2 3 4 @FunctionalInterface public interface Action { void run () ; }
Lambda语法
Lambda表达式的格式为:() -> {}
()表示参数列表
-> 后面跟的式函数主体
{} 函数主题,表达式的返回值,由这个函数主体中代码来决定
函数式接口中,抽象方法常见的有以下几种情况:
1 2 3 4 5 6 7 8 9 10 Action action1 = () -> {};Action action2 = () -> System.out.println("hello" );Action action3= () -> { int a = 1 ; int b = 2 ; System.out.println(a+b); }; interface Action { public void run () ; }
1 2 3 4 5 Action action1 = (a,b) -> {};interface Action { public void run (int a,int b) ; }
1 2 3 4 Action action = () -> 1 ;interface Action { public int run () ; }
1 2 3 4 5 6 7 8 Action action1 = (a,b) -> a + b;Action action2 = (a,b) -> { int num = a+b; return (int )(Math.random()*num); }; interface Action { public int run (int a,int b) ; }
注意,Lambda表达式中的参数列表,里面的参数可以不写类型,因为JVM在运行时会自动推断,当然,如果直接手写上去,也完全没有问题
常用接口 JDK1.8中,已经定了一些会常用到的函数式接口,这些函数式接口都定义 在 java.lang.function 包中,例如 Predicate 、 Consumer 、 Function 、 Supplier 、 UnaryOperator 和 BinaryOperator 等。
注意,如果需要,也可以自己定义类似的函数式接口,并不是必须要使用 这些定义好的接口。
Predicate 1 2 3 4 5 @FunctionalInterface public interface Predicate <T> { boolean test (T t) ; }
定义一个方法,用来过滤数组中所有符合要求的数据,选出大于50 的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test3_PredicateTest { public static void main (String[] args) { Integer[] arr = {1 , 54 , 3 , 4 , 6 , 8 , 455 , 8 , 35 , 835 , 98 }; arr = Test3_PredicateTest.filter(arr, e -> e > 50 ); System.out.println(Arrays.toString(arr)); } public static Integer[] filter(Integer[] arr, Predicate<Integer> p) { List<Integer> list = new ArrayList <>(); for (Integer integer : arr) { if (p.test(integer)) { list.add(integer); } } return list.toArray(new Integer [0 ]); } }
1 2 3 4 5 6 7 Predicate<Integer> p1 = e -> e>10 ; Predicate<Integer> p2 = e -> e<50 ; Predicate<Integer> p = p1.and(p2); arr = t.filter(arr,p);
1 2 3 4 5 6 7 Predicate<Integer> p1 = e -> e<10 ; Predicate<Integer> p2 = e -> e>50 ; Predicate<Integer> p = p1.or(p2); arr = t.filter(arr,p);
1 2 3 4 5 Predicate<Integer> p1 = e -> e>10 ; Predicate<Integer> p = p1.negate(); arr = t.filter(arr,p);
JDK1.8中,给 Collection 集合增加了默认方法: removeIf
Consumer java.util.function.Consumer 接口:
1 2 3 4 5 6 7 8 9 @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); }; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class Test04_ConsumerTest { public static void main (String[] args) { Test04_ConsumerTest t = new Test04_ConsumerTest (); Student stu = new Student ("peter" ); Consumer<Student> consumer1 = (student) -> student.name = "TangClan_" + student.name; Consumer<Student> consumer2 = student -> student.name = student.name + "_" + System.currentTimeMillis(); Consumer<Student> consumer3 = consumer1.andThen(consumer2); t.operStu(stu, consumer3); System.out.println(stu.name); System.out.println("------------------------------------------------" ); Collection<String> col = new ArrayList <>(); col.add("lwsj" ); col.add("peter" ); col.add("parker" ); col.forEach(res -> System.out.println(res)); } public void operStu (Student stu, Consumer<Student> consumer) { consumer.accept(stu); } } class Student { String name; public Student (String name) { this .name = name; } }
Function java.util.function.Function 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @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; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test05_FunctionTest { public static void main (String[] args) { String str = "a-b-c-a-b-c" ; Function<String, String[]> f1 = s -> s.split("-" ); Function<String[], Set<String>> f2 = arr -> { Set<String> set = new HashSet <>(); set.addAll(Arrays.asList(arr)); return set; }; Function<String, Set<String>> f3 = f1.andThen(f2); Set<String> set = f3.apply(str); System.out.println(set); } }
理解了andThen方法,那么compose方法也就理解:f1.andThen(f2), f1.compose(f2)
俩个方法的区别是,把f2操作放在f1操作之前还是之后的问题
静态方法 identity ,API中给出的注释为:
1 2 3 4 5 6 7 8 9 static <T> Function<T, T> identity () { return t -> t; }
可以看出,该方法可以直接返回一个Function对象,传入一个参数,直接把该参数返回,不做任何操作
需要注意的式,这是一个泛型方法,因为泛型参数T是在这个方法上定义的
1 2 3 4 5 6 7 8 public class Test { public static void main (String[] args) { Function<String,String> f = Function.identity(); String result = f.apply("hello" ); System.out.println(result); } }
具体的使用场景后面遇到再说哈~~
Supplier java.util.function.Supplier 接口
1 2 3 4 5 6 7 8 9 @FunctionalInterface public interface Supplier <T> { T get () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test08_SupplierTest { public static void main (String[] args) { Supplier<Integer> supplier = () -> { int num; do { num = (int ) (Math.random() * 100 + 1 ); } while ((num & 1 ) == 0 ); return num; }; for (int i = 0 ; i < 10 ; i++) { System.out.println(supplier.get()); } } }
Predicate 、 Consumer , Function , Supplier ,这些接口都是带泛型的接口,泛型的类型只能是引用类型,那么如果需要操作基本类型的数据,这时候就会做自动装箱和拆箱。而大量 的装箱和拆箱是比较消耗性能的,所以JDK1.8中,还专门定义了一 些针对基本类型的函数式接口,例如:
类型推断 使用Lambda表达式,相当于给函数式接口生成一个实例,但是Lambda表达 式本身,并不包含这个接口的任何信息,例如:
1 2 3 4 5 6 7 8 9 10 public class Test { public static void main (String[] args) { Test t = new Test (); t.test(()->{}); } public void test (Runnable run) { } }
之所以Lambda表达式中没有接口的任何信息,JVM还能将其和接口匹配上,那是因为:
我们在使用Lambda表达式的时候,JVM是会通过上下文自动推断 它所属接口类型的
并且接口中只有一个抽象方法,自然也能匹配成功该表达式所对应实现的抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Test t = new Test (); t.test1(()->{}); t.test2(()->{}); } public void test1 (Runnable run) { } public void test2 (Action action) { } } interface Action { void run () ; }
类似的,JVM还能自动推断出Lambda表达式中参数的类型,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) { Test t = new Test (); t.test((int a,int b)->a+b); t.test((a,b)->a+b); } public void test (Action action) { } } interface Action { int run (int a,int b) ; }
重载解析 如果类中的方法进行了重载,那么在使用Lambda表达式的时候,很可能给它的类型推断带来问题。
1 2 3 4 5 6 7 8 9 10 public class Test { public static void main (String[] args) { test(1 ,num -> num>0 ); } public static void test (int a,Predicate<Integer> p) { } public static void test (int a,Function<Integer,Boolean> f) { } }
1 2 3 4 5 6 7 8 9 10 public class Test { public static void main (String[] args) { test(1 ,(Predicate<Integer>)(num->num>0 )); } public static void test (int a,Predicate<Integer> p) { } public static void test (int a,Function<Integer,Boolean> f) { } }
局部变量 如果在Lambda表达式中,使用了局部变量,那么这个局部变量就一定要使 用 final 修饰符进行修饰,这方面的语法要求,和之前学习的匿名内部类 保持一致。
1 2 3 4 5 6 7 8 9 10 11 public class Test { public static void main (String[] args) { int a = 1 ; Runnable run = ()->{ System.out.println(a); }; } }
方法引用 静态方法引用
注意:方法名后面一定没有小括号 ,因为这里不是在调用方法,而是在引用这个方法的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Action a1 = str -> 1 ; Action a2 = Test::len; System.out.println(a1.run("hello" )); System.out.println(a2.run("hello" )); } public static int len (String str) { return str.length(); } } interface Action { int run (String str) ; }
实例方法引用
注意,这里也是使用类名来进行引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Test { public static void main (String[] args) { Action a = MyHandler::test; } } interface Action { int run (MyHandler handler, int i, String str, List<Integer> list) ; } class MyHandler { public int test (int i, String str, List<Integer> list) { return 0 ; } }
规律:方法参数列表中第一个参数作为对象 ,从第二个参数开始,后面的都作为该对象的参数列表,返回值类型要一致
使用对象引用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test { public static void main (String[] args) { MyHandler handler = new MyHandler (); Action a1 = str -> str; Action a2 = handler::test; System.out.println(a2.run("tom" )); } } interface Action { String run (String str) ; } class MyHandler { public String test (String name) { return "hello! " +name; } }
构造方法引用
无参构造器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { public static void main (String[] args) { Action a1 = ()->new Student (); Action a2 = Student::new ; System.out.println(a2.run()); } } interface Action { Student run () ; } class Student {}
有参构造器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test { public static void main (String[] args) { Action a1 = name->new Student (name); Action a2 = Student::new ; System.out.println(a2.run("tom" )); } } interface Action { Student run (String name) ; } class Student { private String name; public Student (String name) { this .name = name; } }
数组构造
1 2 3 4 5 6 7 8 9 10 11 12 public class Test { public static void main (String[] args) { Action a1 = len -> new int [len]; Action a2 = int []::new ; System.out.println(a2.run(5 )); } } interface Action { int [] run(int len); }
可以看出,无论是构造器,还是普通方法,在Lambda表达式中,都有一个可以引用过来,当做一个函数,这个函数有参数列表、函数主体、返回类型,并且把这个函数当做一个函数式接口中抽象方法的实现!
Optional
java.util.Optional 类,是用来防止NullPointerException 异常的辅助类型, Optional 对 象中封装的值,可以是 null ,也可以不是 null
在Java8之前,一个函数可能因为代码逻辑问题,最终返回一个null,这时候程序中很可能出现空指针异 常。而在Java8中,不推荐返回 null ,而是返回 Optional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public class Test { public static void main (String[] args) { Optional<String> op1 = Optional.of("hello" ); Optional<String> op2 = Optional.ofNullable(null ); if (op1.isPresent()){ System.out.println(op1.get()); } if (op2.isPresent()){ System.out.println(op2.get()); } op1.ifPresent(str->System.out.println(str)); op2.ifPresent(str->System.out.println(str)); System.out.println(op1.orElse("如果op1中的值为null则返回这句话" )); System.out.println(op2.orElse("如果op2中的值为null则返回这句话" )); System.out.println(op1.orElseGet(()->"自己定义的返回值" )); System.out.println(op2.orElseGet(()->"自己定义的返回值" )); Optional<Integer> map1 = op1.map(str->1 ); System.out.println(map1.orElse(0 )); Optional<Double> map2 = op2.map(str->1.2 ); System.out.println(map2.orElse(0D )); Optional<String> flatMap = op1.flatMap(str->Optional.of(str+"_briup" )); System.out.println(flatMap.get()); op1 = op1.filter(str->str.length()<10 ); System.out.println(op1.orElse("值为null" )); op1 = op1.filter(str->str.length()>10 ); System.out.println(op1.orElse("值为null" )); } }
Stream 概述
Stream操作分为中间操作或者最终操作两种:
中间操作:返回Stream本身,这样就可以将多个操作依次串起来
1 map、flatMap、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered
1 forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) { List<Integer> list = new ArrayList <>(); Collections.addAll(list,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ); list = list.stream() .filter(e->e%2 ==0 ) .sorted((e1,e2)->e2-e1) .collect(Collectors.toList()); System.out.println(list); } }
优雅、简洁
其他转Stream 可以将现有的数据,转换为Stream对象,然后再使用Stream的API对数据进行一些系列操作
值 1 2 3 4 5 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("a" , "b" , "c" ); } }
数组 1 2 3 4 5 6 7 public class Test { public static void main (String[] args) { String[] arr = {"a" , "b" , "c" }; Stream<String> stream1 = Arrays.stream(arr); Stream<String> stream2 = Stream.of(arr); } }
集合 1 2 3 4 5 6 7 public class Test { public static void main (String[] args) { List<String> list = new ArrayList <>(); Collections.addAll(list,"a" , "b" , "c" ); Stream<String> stream = list.stream(); } }
注意,只要是Collection类型的集合,都可以调用stream()方法,将集合转换为Stream对象
基本类型
1 2 3 4 5 6 7 8 9 public class Test { public static void main (String[] args) { IntStream stream1 = IntStream.of(new int []{1 , 2 , 3 }); IntStream stream2 = IntStream.range(1 , 3 ); IntStream stream3 = IntStream.rangeClosed(1 , 3 ); } }
Stream转其他
使用Stream的API对数据操作后,还可以把结果转换为其他类型
数组 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); String[] strArray = stream.toArray(String[]::new ); } }
集合 1 2 3 4 5 6 7 8 9 10 11 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); List<String> list1 = stream.collect(Collectors.toList()); Set<String> set3 = stream.collect(Collectors.toSet()); } }
注意,一个Stream在代码中,只能使用一次,再次使用就会报错
字符串 1 2 3 4 5 6 7 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" , "world" , "truman" ); String result = stream.collect(Collectors.joining("-" )); System.out.println(result); } }
Stream操作
最终操作
1 2 3 4 5 6 7 8 9 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); Iterator<String> it = stream.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } }
forEach ,将调Stream中的每个元素,交给一个Consumer函数处理
1 2 3 4 5 6 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); stream.forEach(System.out::println); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); Optional<String> max = stream.max(String::compareTo); System.out.println(max.get()); } }
min ,返回流中基于comparator所指定的比较规则,比较出的最小值
toArray : 使用调用流中的元素,生成数组返回。
collect ,将元素收集到一个可以修改的容器中,并返回该容器
1 2 3 4 5 6 7 8 9 10 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); List<String> list1 = stream.collect(Collectors.toList()); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("test" , "hello" , "world" , "java" , "tom" , "C" , "javascript" ); Map<Integer, List<String>> map = stream.collect(Collectors.groupingBy(String::length)); map.forEach((k, v) -> System.out.println(k + " : " + v)); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("test" , "hello" , "world" , "java" , "tom" , "C" , "javascript" ); Map<Boolean, List<String>> map = stream.collect(Collectors.partitioningBy(s -> s.indexOf("java" ) != -1 )); map.forEach((k, v) -> System.out.println(k + " : " + v)); } }
Match ,匹配操作,Stream中提供了多种匹配模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("test" ,"hello" ,"world" ,"java" ,"tom" ,"C" ,"javascript" ); boolean allMatch = stream.allMatch((s)->s.startsWith("j" )); System.out.println(allMatch); boolean anyMatch = stream.anyMatch((s)->s.startsWith("j" )); System.out.println(anyMatch); boolean noneMatch = stream.noneMatch((s)->s.startsWith("j" )); System.out.println(noneMatch); } }
注意,这些操作不能同时执行,因为一个Stream只能使用一次
findFirst ,返回 Stream的第一个元素
1 2 3 4 5 6 7 public class Test { public static void main (String[] args) { Stream<String> stream = Stream.of("test" ,"hello" ,"world" ,"java" ,"tom" ,"C" ,"javascript" ); Optional<String> first = stream.findFirst(); System.out.println(first.get()); } }
中间操作
filter , 过滤方法,返回满足predicate指定的条件的所有元素的一个新流
map , 对调用流中的元素,应用Function所指定的操作,然后返回一个新流
1 2 3 4 5 6 7 Stream<String> nameStream = Stream.of("lwsj" , "truman" , "peter" , "parker" ); List<Integer> collect = nameStream.map(s -> s.length()).collect(Collectors.toList()); for (Integer integer : collect) { System.out.println(integer); }
map 方法可以和 reduce 方法配合使用, reduce 方法是将一组数据俩俩合并,最后得出一个结果:
1 2 3 4 5 6 7 IntStream stream = IntStream.rangeClosed(1 , 10 );int result = stream.reduce(0 , (a, b) -> a + b);System.out.println(result);
1 2 3 4 5 6 7 Stream<String> stream = Stream.of("tom" , "mary" , "lucy" ); Optional<String> result = stream.map(str -> "Truman_" + str) .reduce((s1, s2) -> s1 + "|" + s2); System.out.println(result.get());
1 2 3 Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); stream.sorted().forEach(System.out::println);
1 2 3 Stream<String> stream = Stream.of("hello" ,"world" ,"briup" ); stream.sorted((o1, o2) -> - o1.compareTo(o2)).forEach(System.out::println);
limit ,返回 Stream 的前面 n 个元素
1 2 Stream<String> stream = Stream.of("test" ,"javap" ,"hello" ,"world" ,"java" ,"tom" ,"C" ,"javascript" ); stream.limit(5 ).forEach(System.out::println);
1 2 Stream<String> stream = Stream.of("test" ,"javap" ,"hello" ,"world" ,"java" ,"tom" ,"C" ,"javascript" ); stream.skip(5 ).forEach(System.out::println);
1 2 Stream<String> stream = Stream.of("test" ,"test" ,"hello" ,"world" ,"java" ,"java" ,"C" ,"C" ); stream.distinct().forEach(System.out::println)
静态方法
1 2 3 4 Stream<String> stream1 = Stream.of("hello" ,"world" ); Stream<String> stream2 = Stream.of("tom" ,"mary" ); Stream<String> result = Stream.concat(stream1, stream2); result.forEach(System.out::println);
Stream.generate ,通过Supplier接口,可以自己来控制数据的生成
1 2 3 public static <T> Stream<T> generate (Supplier<T> s) { }
1 2 3 4 5 6 7 8 9 final Random random = new Random ();Stream.generate(()->random.nextInt(100 )) .limit(100 ) .forEach(System.out::println); List<Integer> list = Stream.generate(()->random.nextInt(100 )) .limit(100 ) .collect(Collectors.toList());
1 2 3 4 5 6 7 8 9 public class Test { public static void main (String[] args) { Stream.iterate(0 , n -> n+3 ) .limit(10 ) .forEach(System.out::println); } }
IO流和Stream 在IO流中,也有方法,可以将读取到的数据转换为Stream对象
1 2 3 4 5 6 public class BufferedReader extends Reader { public Stream<String> lines () { } }
1 2 3 4 5 6 7 8 9 10 11 12 public class Main { public static void main (String[] args) throws FileNotFoundException { BufferedReader br = null ; br = new BufferedReader (new FileReader ("src/dir/a.txt" )); int maxLen = br.lines() .mapToInt(String::length) .max() .getAsInt(); System.out.println(maxLen); } }
并行流 Stream有串行和并行两种。串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多 个线程上同时执行。
创建并行Stream的两种方式:
调用串行Stream的 parallel()方法,可以将其转换并行Stream
1 2 Stream<String> stream = Stream.of("truman" , "peter" , "parker" ); Stream<String> parallel = stream.parallel();
调用集合对象的parallelStream方法,之后获取并行Stream
1 2 3 ArrayList<String> list = new ArrayList <>(); Collections.addAll(list, "truman" ,"perter" ,"parker" ); Stream<String> stream = list.parallelStream();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Main { public static void main (String[] args) throws FileNotFoundException { int maxx = 2000000 ; List<UUID> collect = Stream.generate(UUID::randomUUID) .limit(maxx) .collect(Collectors.toList()); long startTime = System.currentTimeMillis(); long count = collect.parallelStream().sorted().count(); long endTime = System.currentTimeMillis(); System.out.println("-----costTime: " + (endTime - startTime) + "ms" ); } }
可以看出,在数据量较大的特定场景下,并行Stream比串行Stream的效率要高一些
❤️❤️❤️忙碌的敲代码也不要忘了浪漫鸭!
立志欲坚不欲锐,成功在久不在速——宋.张孝祥 《论治体札子》💪