Stream
lambda表达式是Stream流的基础 先了解一下lambda表达式
lambda 表达式
说起lambda 必须提一下函数式接口
函数式接口
函数式接口:只有一个方法的接口 java.util.function
例如:多线程中的Runnable接口就是 函数式接口
jdk提供的常用函数式接口常见的有:
Consumer:提供任意一种类型的参数,返回空值。 void accept(T t);
Supplier:参数为空,得到任意一种类型的返回值。T get();
Function:提供任意一种类型的参数,返回另外一个任意类型返回值。 R apply(T t);
Predicate:提供任意一种类型的参数,返回boolean类型返回值。boolean test(T t);
lambda 表达式的推导
方法引用
什么是方法引用?
答:方法引用是对Lambda表达式符合某种情况下的一种缩写,使得我们的Lambda表达式更加的精简,
也可以理解为Lambda表达式的另一种表现形式(缩写)
什么时候使用方法引用呢?
答:当要传递给Lambda体内的操作,已经有实现的方法了,就可以使用方法引用了
方法引用使用的前提条件是什么呢?
答:
1.方法引用所引用的方法的参数列表必须要和函数式接口中抽象方法的参数列表相同(完全一致)
2.方法引用所引用的方法的的返回值必须要和函数式接口中抽象方法的返回值相同(完全一致)
注意
1.方法名的后面没有括号“()”
2.方法的引用是可以有多个参数入口的,虽然再::表达式中没有体现(由于没有小括号),但是接口中对其已有所规定了;
使用方式
方法引用主要有如下三种使用情况
(1). 类::实例方法
(2). 类::静态方法
(3). 对象::实例方法
其中后两种情况等同于提供方法参数的lambda表达式,
如 : System.out::println 等同于(x)->System.out.println(x),
Math::pow 等同于(x,y)->Math.pow(x,y).
第一种中,第一个参数会成为执行方法的对象:
String::compareToIgnoreCase 等同于(x,y)->x.compareToIgnoreCase(y)
此外,方法引用还可以使用this::methodName及super::methodName表示该对象或者其父类对象中的方法
补充:
1. java 方法的分类
静态方法:有static 修饰的方法
实例方法:没有static 修饰 就是普通的方法
构造方法:没有返回值,方法名与类名一样
public static void myMethod();//类方法
public void myMethod2();//实例方法
public Test(){}//构造方法,没有返回值,方法名与类名一样
- 类方法与普通方法的区别
1、类方法是属于整个类,而不属于某个对象。
2、类方法只能访问类成员变量,不能访问实例变量,而实例方法可以访问类成员变量和实例变量。
3、在类方法中不能使用super、this关键字。
4、类方法的调用可以通过类名.类方法和对象.类方法,而实例方法只能通过对象.实例方法访问。
5、类方法只能访问类方法,而实例方法可以访问类方法和实例方法。
6、类方法不能被覆盖,实例方法可以被覆盖。
小试牛刀
public static void test1_() {
List<String> strLst = new ArrayList<String>() {
{
add("adfkjsdkfjdskjfkds");
add("asdfasdfafgfgf");
add("public static void main");
}
};
Collections.sort(strLst, String::compareToIgnoreCase);//方法引用
System.out.println(strLst);
}
Stream
好了 我们开始学习Stream 流 ,先介绍一下Stream 的内容
看一个例子
@Before
public void init() {
random = new Random();
stuList = new ArrayList<Student>() {
{
for (int i = 0; i < 100; i++) {
add(new Student("student" + i, random.nextInt(50) + 50));
}
}
};
}
public class Student {
private String name;
private Integer score;
//-----getters and setters-----
}
//1列出班上超过85分的学生姓名,并按照分数降序输出用户名字
@Test
public void test1() {
List<String> studentList = stuList.stream()
.filter(x->x.getScore()>85)//过滤
.sorted(Comparator.comparing(Student::getScore).reversed())//排序
.map(Student::getName)//转换
.collect(Collectors.toList());//把Stream流转换为List
System.out.println(studentList);
}
列出班上分数超过85分的学生姓名,并按照分数降序输出用户名字,在java8之前我们需要三个步骤:
1)新建一个List newList,在for循环中遍历stuList,将分数超过85分的学生装入新的集合中
2)对于新的集合newList进行排序操作
3)遍历打印newList
这三个步骤在java8中只需要两条语句,如果紧紧需要打印,不需要保存新生产list的话实际上只需要一条,是不是非常方便。
补充 Collectors():
Collectors(): 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
stream 的特性
我们首先列出stream的三点特性,在之后我们会对照着详细说明
1.stream不存储数据
2.stream不改变源数据
3.stream的延迟执行特性
创建Stream
通过数组创建
//1.通过Arrays.stream
//1.1基本类型
int[] arr = new int[]{1,2,34,5};
IntStream intStream = Arrays.stream(arr);
//1.2引用类型
Student[] studentArr = new Student[]{new Student("s1",29),new Student("s2",27)};
Stream<Student> studentStream = Arrays.stream(studentArr);
//1.3 注意生成的是int[]的流
int [] [] arrs = new int[][]{arr,arr};
Stream<int []> arrsStream1 = Arrays.stream(arrs);
//2.通过Stream.of
Stream<Integer> stream1 = Stream.of(1,2,34,5,65);
//注意生成的是int[]的流
Stream<int[]> stream2 = Stream.of(arr,arr);
stream2.forEach(System.out::println);
通过集合创建
// 通过集合创建
List<String> arrayToList = Arrays.asList("a","b","c");//通过数组创建集合
List<String> stringList = new ArrayList<>();
// 创建普通流
Stream<String> stringStream = stringList.stream();
// 创建并行流
Stream<String> stringStream1 = stringList.parallelStream();
创建空的流
// 创建空的流
Stream<String> stringEmptyStream = Stream.empty();
Stream<Integer> intEmptyStream = Stream.empty();
Stream<Student> studentEmptyStream = Stream.empty();
创建无限流
//创建无限流 (通过limet中的数子限制创建流的数量)
Stream.generate(()->new Student(2,"l")).limit(200).forEach(System.out::println);
Stream.generate(()->"number"+new Random().nextInt()).limit(10).forEach(System.out::println);
创建规律的无限流
// 产生规律的数据
Stream.iterate(0,x->x+1).limit(10).forEach(System.out::println);//0,1,2,3,4,5,6,7,8,9
Stream.iterate(0,x->x).limit(10).forEach(System.out::println); //0,0,0,0,0,0,0,0,0,0
//Stream.iterate(0,x->x).limit(10).forEach(System.out::println);与如下代码意思是一样的
Stream.iterate(0, UnaryOperator.identity()).limit(10).forEach(System.out::println);
对Stream 的操作
最常使用
map:转换流,将一种类型的流转换为另外一种流
//map把一种类型的流转换为另外一种类型的流
//将String数组中字母转换为小写
String[] arr = new String[]{"yes", "YES", "no", "NO"};
Arrays.stream(arr).map(x -> x.toLowerCase()).forEach(System.out::println);//yes yes no no
Arrays.stream(arr).map(String::toLowerCase).forEach(System.out::println);//yes yes no no
filter:过滤流,过滤流中的元素
Integer[] arr = new Integer[]{1,2,3,4,5,6,7,8,9,10};
Arrays.stream(arr).filter(x->x>3&&x<8).forEach(System.out::println);//4567
flapMap:拆解流,将流中每一个元素拆解成一个流
String[] arr1 = {"a", "b", "c", "d"};
String[] arr2 = {"e", "f", "c", "d"};
String[] arr3 = {"h", "j", "c", "d"};
// Stream.of(arr1, arr2, arr3).flatMap(x -> Arrays.stream(x)).forEach(System.out::println);
Stream.of(arr1, arr2, arr3).flatMap(Arrays::stream).forEach(System.out::println);//12个流
sorted:对流进行排序
String[] arr1 = {"abc","a","bc","abcd"};
/**
* Comparator.comparing是一个键提取的功能
* 以下两个语句表示相同意义
*/
@Test
public void testSorted1_(){
/**
* 按照字符长度排序
*/
//语句一
Arrays.stream(arr1).sorted((x,y)->{
if (x.length()>y.length())
return 1;
else if (x.length()<y.length())
return -1;
else
return 0;
}).forEach(System.out::println);
//语句二
Arrays.stream(arr1).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
}
/**
* 倒序
* reversed(),java8泛型推导的问题,所以如果comparing里面是非方法引用的lambda表达式就没办法直接使用reversed()
*/
@Test
public void testSorted2_(){
Arrays.stream(arr1).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
}
/**
* thenComparing
* 先按照首字母排序
* 之后按照String的长度排序
*/
@Test
public void testSorted3_(){
Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length))
.forEach(System.out::println);
}
public char com1(String x){
return x.charAt(0);
}
提取流和组合流
@Before
public void init(){
arr1 = new String[]{"a","b","c","d"};
arr2 = new String[]{"d","e","f","g"};
arr3 = new String[]{"i","j","k","l"};
}
//提取流
/**
* limit,限制从流中获得前n个数据
*/
@Test
public void testLimit(){
Stream.iterate(1,x->x+2).limit(10).forEach(System.out::println);//1,3,5,7,9,11,13,15,17,19
}
/**
* skip,跳过前n个数据
*/
@Test
public void testSkip(){
Stream.of(arr1).skip(1).limit(2).forEach(System.out::println);//b,c
Stream.iterate(1,x->x+2).skip(1).limit(5).forEach(System.out::println);//3,5,7,9,11
}
//组合流
/**
* 可以把两个stream合并成一个stream(合并的stream类型必须相同)
* 只能两两合并
*/
@Test
public void testConcat(){
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream.concat(stream1,stream2).distinct().forEach(System.out::println);//abcdefg
//.distinct() 是Stream接口的方法。 返回由该流的不同元素组成的流。
//distinct()使用hashCode()和equals()方法来获取不同的元素。
}
聚合操作
@Before
public void init(){
arr = new String[]{"b","ab","abc","abcd","abcde"};
}
/**
* max、min
* 最大最小值
*/
@Test
public void testMaxAndMin(){
Stream.of(arr).max(Comparator.comparing(String::length)).ifPresent(System.out::println);
Stream.of(arr).min(Comparator.comparing(String::length)).ifPresent(System.out::println);
//.ifPresent
}
/**
* count
* 计算数量
*/
@Test
public void testCount(){
long count = Stream.of(arr).count();//arr 只能为数组
System.out.println(count);
}
/**
* findFirst
* 查找第一个
*/
@Test
public void testFindFirst(){
String str = Stream.of(arr).parallel().filter(x->x.length()>3).findFirst().orElse("noghing");
System.out.println(str);
}
/**
* findAny
* 找到所有匹配的元素
* 对并行流十分有效
* 只要在任何片段发现了第一个匹配元素就会结束整个运算
*/
@Test
public void testFindAny(){
Optional<String> optional = Stream.of(arr).parallel().filter(x->x.length()>3).findAny();
optional.ifPresent(System.out::println);
}
/**
* anyMatch
* 是否含有匹配元素
*/
@Test
public void testAnyMatch(){
Boolean aBoolean = Stream.of(arr).anyMatch(x->x.startsWith("a"));
System.out.println(aBoolean);
}
@Test
public void testStream1() {
Optional<Integer> optional = Stream.of(1,2,3).filter(x->x>1).reduce((x,y)->x+y);
System.out.println(optional.get());
}
Optional类型
通常聚合操作会返回一个Optional类型,Optional表示一个安全的指定结果类型,所谓的安全指的是避免直接调用返回类型的null值而造成空指针异常,调用optional.ifPresent()可以判断返回值是否为空,或者直接调用ifPresent(Consumer<? super T> consumer)在结果部位空时进行消费操作;调用optional.get()获取返回值。通常的使用方式如下:
@Test
public void testOptional() {
List<String> list = new ArrayList<String>() {
{
add("user1");
add("user2");
}
};
Optional<String> opt = Optional.of("andy with u");
opt.ifPresent(list::add);
list.forEach(System.out::println);
}