一.Stream流的总结
Stream流的思想:
获取方法 --> 中间方法 --> 终结方法
获取方法:
创建一条流水线,并把数据放到流水线上准备进行操作
中间方法:
流水线上的操作
一次操作完毕之后,还可以进行其他操作
终结方法:
一个Stream流只能有一个终结方法
是流水线上的最后一个操作
二.Stream流的获取方法
1.单列集合
可以使用Collection接口中的默认方法stream()生成流
default Stream<E> stream
代码示例:
private static void method1() {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// Stream<String> stream = list.stream();
// stream.forEach(s-> System.out.println(s));
list.stream().forEach(s-> System.out.println(s));
}
2.双列集合
间接 的生成流
可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
集合对象.keySet().stream();
集合对象.entrySet().stream();
代码示例:
private static void method2() {
HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
stringIntegerHashMap.put("zhangsan", 23);
stringIntegerHashMap.put("lisi", 24);
stringIntegerHashMap.put("wangwu", 25);
stringIntegerHashMap.put("zhaoliu", 26);
stringIntegerHashMap.put("qianqi", 27);
// 双列集合不能直接获取Stream流
// keySet 先获取到所有的键,再把这个Set集合中所有的键放到Stream流中
stringIntegerHashMap.keySet().stream().forEach(s -> System.out.println(s));
// entrySet
// 先获取到所有的键值对对象,再把这个Set集合中所有的键值对对象放到Stream流中
stringIntegerHashMap.entrySet().stream().forEach(s -> System.out.println(s));
}
3.数组
Arrays中的静态方法Stream生成流
代码示例:
private static void method3() {
int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr).forEach(s -> System.out.println(s));
}
4.同种数据类型的多个数据
1,2,3,4,5....
"aaa","bbb","ccc"...
使用Stream.of(T...value) T...value,这个形参是一个可变参数
代码示例:
private static void method4() {
Stream.of(1, 2, 3, 4, 5, 6).forEach(s -> System.out.println(s));
}
三.中间方法
1.filter()
Stream<T> filter(Predicate predicate):过滤
Predicate接口: Predicate接口是个断言式接口.,其参数是<T,boolean>,也就是给一个参数T,返回boolean类型的结果.
Predicate接口中的方法: boolean rest(T t):对给定的参数进行判断,返回一个boolean值
filter方法获取流中的每一个数据,而test方法中对s进行判断就可以了.如果判断结果为true,则当前数据留下,如果判断结果为false,则当前数据就不要了.
代码示例:
ArrayList<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张无忌");
list.add("张翠山");
list.add("王二麻子");
list.add("张良");
list.add("谢广坤");
/*list.stream().filter(
new Predicate<String>() {
@Override
public boolean test(String s) {
boolean result = s.startsWith("张");
return result;
}
}
).forEach(s -> System.out.println(s));*/
/*list.stream().filter((String s)->{
boolean result = s.startsWith("张");
return result;
}).forEach(s-> System.out.println(s));*/
list.stream().filter(s->s.startsWith("张")).forEach(s-> System.out.println(s));
2.其他方法
(1)Stream<T> limit(long maxSize):截取指定参数个数的数据 这个方法保留的是前面的数据
(2)Stream<T> skip(long n):跳过指定参数个数的数据 去除前面的数据,保留后面的数据
(3)static <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流
(4)Stream<T> distinct():去除流中重复的元素.依赖(hashCode和equals方法)
四.终结方法
1.foreach()
2.count()
五.Stream流的收集方法
使用Stream流的方式操作完毕之后,我想把流中的数据收集起来,该怎么办
Stream流的手机方法
R collect(Collector collector)
工具类Collectors提供了具体的手机方法
public static <T> Collector toList():把元素收集到List集合中
public static <T> Collector toSet():把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
六.Java中的::是什么意思?
.forEach(element -> {System.out.println(element)})
但是System.out.println的参数和传递的参数element 的类型完全匹配,所以这样的时候就可以简化为:
.forEach(System.out::println)
forEach方法的源码:
default void foreach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
其实它本质上是调用了for循环,它的参数的是一个Consumer对象,在for循环中调用了对象的accept方法。
注意该方法是接口的default方法,是JDK8新增的语法。至于为什么可以写成System.out::println,这种语法叫做方法引用。
该功能特性也是JDK8以后引入的,你可以把它看做拉姆达表达式的语法糖。
如果我们不这样写的话,我们可以用拉姆达表达式改写成以下代码:
list.forEach((t) -> System.out.println(t));
如果还不明白的话,也可以这样:
list.forEach((String t) -> System.out.println(t));
这样的效果跟System.out::println是一样的。
Lambda表达式允许4中方式的双冒号:
种类 | 使用方法 | 对应的lambda表达式 |
(1)引用特定对象的实例方法 | Object::instanceMethod | (a,b,...) -> 特定对象,实例方法(a,b,...) |
(2)引用类方法 | Class::staticMethod | (a,b,...) -> 类名.类方法(a,b,...) |
(3)引用某类对象的实例方法 | Class::instanceMethod | (a,b,...) -> a.实例方法(b,...) |
(4)引用构造器 | Class::new | (a,b,...) -> new 类名(a,b,...) |
其中的Class指的是类名,产生于class Class{}
object是实例对象,产生于Class object = new Class;
object::instanceMethod 的一个典型用法就是 System.out::println out继承于FilterOutputStream,可以看成System的一个成员对象,通过它可以调用printstream中的输出方法。
System.out是printStream的实例: 因为System类当中的out 数据成员是由printStream流创建出来的对象,在system类中为public static final printStream out 而且out又是static的,所以只能够通过system来调用,即为System.out了。
System.out说是printStream的实例化对象,意思就是说System类当中的out数据成员即为printStream 类的对象了。System.out 整个就表示屏幕输出了,这时候还只是字节流而已,当调用pringStream的方法println()是就表示用什么用的方式打印输出的关系了。
在此请回看 Consumer 类的代码,就明白了为什么 ```Consumer one = System.out::println``` 是 ```Consumer one = t -> System.out.println(t)``` 的简写,
在这里插一个多线程的lambda表达式使用技巧:
public class SynchronizedTest {
public synchronized void method1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) {
// 一种最简洁的启动方法
// new Thread( ()-> new SynchronizedTest().method1() ).start();
//新建实例对象的启动方法
final SynchronizedTest test = new SynchronizedTest();
new Thread(test::method1).start();
new Thread( ()-> test.method1() ).start();
//显式的赋值Runnable后再启动
Runnable tt = test::method1;
new Thread(tt).start();
//
new Thread(test::method2).start();
//运行完后对结果有没有疑问? 为什么2后面还有1?
}
}