初识Stream作用:
创建集合添加元素,完成以下需求:
1.把所有以“张”开头的元素存储到新集合中
2.把“张"开头的,长度为3的元素再存储到新集合中
3.遍历打印最终结果
以前的做法:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三", "赵六", "张四", "李四", "张老五");
//把所有以“张”开头的元素存储到新集合中
ArrayList<String> list1 = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")) {
list1.add(s);
}
}
//把“张"开头的,长度为3的元素再存储到新集合中
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if (s.length() == 3) {
list2.add(s);
}
}
System.out.println(list2);
[张老五]
Stream的写法:
//初识Stream流
list.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() == 3)
.forEach(s -> System.out.println(s));
张老五
明显更加简便
Stream流的作用:
结合了Lambda表达式,简化集合、数组的操作。
Stream流的使用步骤:
- 先得到一条Stream流(流水线),并把数据放上去
- 利用Stream流中的API进行各种操作
- 如中间方法:过滤、转换。 方法调用完毕之后,还可以调用其他方法
- 如终结方法:统计、打印 最后一步,调用完毕之后,不能调用其他方法
1.获取Stream流
注意:
其中双列集合不能直接获取Stream流,必须先利用keySet或EntrySet变成Set集合
单列集合 -获取方式演示:
//创建单列集合
ArrayList<Integer>list=new ArrayList<>();
//添加元素
Collections.addAll(list,1,2,3,4);
//获取stream流(流水线),并把数据放在了流水线上,使用终结方法遍历流水线上的数据
list.stream().forEach(new Consumer<Integer>() {
@Override
public void accept(Integer i) {
System.out.println(i);
}
});
//当然也可以用Lambda表达式简化(forEach是函数式接口)
//list.stream().forEach(i -> System.out.println(i));
控制台:
1
2
3
4
双列集合获取stream流:
HashMap<String,Integer>hm=new HashMap<>();
hm.put("a",1);
hm.put("b",2);
hm.put("c",3);
//方式一:先转成keySet
Set<String> set1 = hm.keySet();
set1.stream().forEach(s -> System.out.println(s));//遍历键
//方式二:先转成entrySet
Set<Map.Entry<String, Integer>> entries = hm.entrySet();
entries.stream().forEach(new Consumer<Map.Entry<String, Integer>>() {
@Override
public void accept(Map.Entry<String, Integer> s) {
System.out.println(s);//直接打印可以,调用getKey和getValue方法可以用
}
});
//也可以用Lambda表达式简化,Consumer是函数式接口
//entries.stream().forEach( s-> System.out.println(s));
控制台:
a
b
c
a=1
b=2
c=3
数组获取stream流:
//定义集合
int []arr1={1,2,3};
String []arr2={"a","b","c"};
//获取
Arrays.stream(arr1).forEach(i-> System.out.println(i));
System.out.println("----------------------------------");
Arrays.stream(arr2).forEach(s -> System.out.println(s));
控制台:
1
2
3a
b
c
一堆零散数据获取stream流:
Stream接口中静态方法of的细节:
形参不仅仅可以传一堆零散数据,还可以传递数组,但仅限引用数据类型
如果传递基本数据类型,是会把整个数组当做一个元素,放到stream当中,如下:
Stream.of(1,2,3).forEach(s-> System.out.println(s));
System.out.println("----------------");
Stream.of("a","b").forEach(s-> System.out.println(s));
1
2
3a
b
Stream流的中间方法:
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
注意concat方法是静态的
注意第一点:不能重复使用,否则报错:
filter过滤方法演示:
filter的形参:
Predicate是函数式接口:可以用Lambda简化
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) {
//s,就依次表示流中的每一个数据.
//返回值:false表示当前数据不留下
// true表示留下
boolean results = s.startsWith("张");
return results;
}
}).forEach(s -> System.out.println(s));
注意test方法的返回值是boolean
limit和skip方法:
ArrayList<String>list=new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("张三");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//1.skip跳过前几个元素
//list.stream().skip(3).forEach(s -> System.out.println(s));//跳过前三个
//2.limit获取前几个元素
//list.stream().limit(3).forEach(s-> System.out.println(s));//只获取前三个
//要求:要求截取 "王祖贤"
//方式一:先跳过前三个,再获取第一个
list.stream().skip(3).limit(1).forEach(s -> System.out.println(s));//王祖贤
//方式二:先获取前四个,再跳过前三个
list.stream().limit(4).skip(3).forEach(s -> System.out.println(s));//王祖贤
distinct:去重和concat(静态):合流 方法
distinct底层是HashSet,元素不重复,且要重写hashCode和equals方法,
若要给自定义对象去重一定要重写hashCode和equals方法
concat方法,要传递两个stream流对象
//distinct 元素去重,依赖(hashCode和equals方法)
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"王祖贤","王祖贤","李四");
list.stream().distinct().forEach(s -> System.out.println(s));
System.out.println("-----------------");
//concat 合并a和b两个流为一个流
ArrayList<String>list1=new ArrayList<>();
ArrayList<String>list2=new ArrayList<>();
Collections.addAll(list1,"zhangsan");
Collections.addAll(list2,"lisi");
//合流并遍历
Stream.concat(list1.stream(),list2.stream()).forEach(s-> System.out.println(s));
王祖贤
李四zhangsan
lisi
map:转换流中的数据类型
函数式接口:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三-23", "李四-24", "王五-25");
//需求:只获取里面的年龄并进行打印
//先获取年龄字符串 ,再String->int
//第一个类型:流中原本的数据类型
//第二个类型:要转成之后的类型
//apply的形参s:依次表示流里面的每一个数据
//返回值:表示转换之后的数据
list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
String[] str = s.split("-");//张三 23
String s1 = str[1];
int i = Integer.parseInt(s1);
return i;
}
}).forEach(s-> System.out.println(s));
//也可用Lambda简化
// list.stream()
// .map( s -> Integer.parseInt(s.split("-")[1]))
// .forEach(s-> System.out.println(s));
23
24
25
Stream流终结操作方法:
forEach遍历:
形参:
是函数式接口,可用Lambda简化
//创建集合:
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"zhangsan","lisi","wangwu");
//获取stream流,并打印
list.stream().forEach(new Consumer<String>() {
/**
* <String>表示流上的数据类型
* accept的形参s表示当前操作的数据
* accept方法体:打印
*/
@Override
public void accept(String s) {
System.out.println(s);
}
});
//当然可以用Lambda表达式简化:
//list.stream().forEach( s-> System.out.println(s));
zhangsan
lisi
wangwu
Count统计个数:
返回值是long:
//创建集合:
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"zhangsan","lisi","wangwu");
//获取stream流:
long count = list.stream().count();
System.out.println(count);
3
toArray收集流中的数据,放到数组中:
有重载方法:
- 将数据放入 Object类型的数组中
- 将数据放入 指定类型的数组中
- 可以指定数组类型的toArray方法的形参:
- 函数式接口,可用Lambda简化
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"zhangsan","lisi","wangwu");
//1.toArray---放入Object集合
Object[] arr = list.stream().toArray();
System.out.println(Arrays.toString(arr));//直接打印arr会打印地址值
//2.toArray--放入指定类型
/*
注意点:
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));
//Lambda简化后:
// String[] arr2 = list.stream().toArray(value-> new String[value]);
//System.out.println(Arrays.toString(arr2));
[zhangsan, lisi, wangwu]
注意点:
toArray方法的参数的作用:负责创建一个指定类型的数组
toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组中
toArray方法的返回值:是一个装着流里面所有数据的数组
collect收集流中的数据,放到集合(list,set ,map)中:
形参:
1.收集到list:底层使用ArrayList收集
//创建集合
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"zhangsan-男-23","zhangsan-男-23","lisi-女-24","wangwu-男-25");
//要求:将男生收集到list集合中
//先过滤,再收集
List<String> list1 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))//过滤最后要返回Boolean类型
.collect(Collectors.toList());
System.out.println(list1);
[zhangsan-男-23, zhangsan-男-23, wangwu-男-25]
2.收集到Set:底层使用HashSet’收集
ArrayList<String>list=new ArrayList<>();
Collections.addAll(list,"zhangsan-男-23","zhangsan-男-23","lisi-女-24","wangwu-男-25");
//要求:将男生收集到list集合中
Set<String> set = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
System.out.println(set);
[wangwu-男-25, zhangsan-男-23]
注意点:
ArrayList不去重复,HashSet会去重
3.收集到Map集合:使用HashMap收集
to Map:
- 参数一表示键的生成规则:
- Function泛型一:表示流中每一个数据的类型
- 泛型二:表示Map集合中键的数据类型
- apply方法形参依次表示每个数据
- 方法体:生产键的代码
- 返回值:已经生成的键
- 参数二表示值的生成规则
- Function泛型—:表示流中每一个数据的类型
- 泛型二:表示Map集合中值的数据类型
- apply方法形参依次表示每个数据
- 方法体:生产值的代码
- 返回值:已经生成的值
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "zhangsan-男-23", "lisi-女-24", "wangwu-男-25" ;
//要求:将男生收集到Map集合中,建放姓名,值放年龄
//先过滤出男生,再去设置键和值生成规则
Map<String, Integer> map = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[0];//zhangsan
}
}
,
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);//23
}
}
));
System.out.println(map);
//Lambda可简化
// Map<String, Integer> map = list.stream()
// .filter(s -> "男".equals(s.split("-")[1]))
// .collect(Collectors.toMap(
// s -> s.split("-")[0],
// s -> Integer.parseInt(s.split("-")[2])
// ));
// System.out.println(map);
{zhangsan=23, wangwu=25}
注意点:
如果我们要收集到Map集合当中,键不能重复,否则会报错