由需求初识Stream
有一份菜单,需求在其中找出卡路里低于指定值的菜单,且按照指定顺序对菜单排序,要求仅输出满足条件的菜单的名字
//菜单类
@Data
@Builder
@AllArgsConstructor
public class Dish{
private final String name;
private final boolean vegetarian;
private final int calories;
private final Type type;
public enum Type {MEAT,OTHER,FISH}
}
//
public class SimpleStream {
public static void main(String[] args) {
//菜中选择某一指定calories菜,并按照指定顺序排列输出
List<Dish> menu = Arrays.asList(
Dish.builder().name("pork").vegetarian(false).calories(800).type(Dish.Type.MEAT).build(),
Dish.builder().name("beef").vegetarian(false).calories(700).type(Dish.Type.MEAT).build(),
Dish.builder().name("chicken").vegetarian(false).calories(400).type(Dish.Type.MEAT).build(),
Dish.builder().name("french").vegetarian(true).calories(530).type(Dish.Type.OTHER).build(),
Dish.builder().name("rice").vegetarian(true).calories(350).type(Dish.Type.OTHER).build(),
Dish.builder().name("season fruit").vegetarian(true).calories(120).type(Dish.Type.OTHER).build(),
Dish.builder().name("pizza").vegetarian(true).calories(550).type(Dish.Type.OTHER).build(),
Dish.builder().name("prawns").vegetarian(false).calories(300).type(Dish.Type.FISH).build(),
Dish.builder().name("salmon").vegetarian(false).calories(450).type(Dish.Type.FISH).build()
);
//过滤卡路里小于400,排序输出
//1.1
List<String> dishNamesByCollections = getDishNamesByCollections(menu);
System.out.println(dishNamesByCollections);
//1.2
List<String> dishNamesByStream = getDishNamesByStream(menu);
System.out.println(dishNamesByStream);
//2.流具有中断特性,以下操作会报错stream has already been operated upon or closed
/*Stream stream = menu.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println);*/
//3.Stream流中的filter和map
//filter用来过滤满足条件的数据数据类型不会发生改变,只是用来筛选满足条件的数据
//map用来匹配,可以输出不同的类型。如只想要Dish中的名字,当然,也可以毫不改变的输出数据
List<String> result = menu.stream().filter(d -> {
System.out.println("filtering->" + d.getName());
return d.getCalories() > 300;
}).map(d -> {
System.out.println("map->" + d.getName());
return d.getName();
}).limit(3).collect(toList());
//4.Stream流中的遍历
Stream<Dish> stream = Stream.of(
Dish.builder().name("pork").vegetarian(false).calories(800).type(Dish.Type.MEAT).build(),
Dish.builder().name("beef").vegetarian(false).calories(700).type(Dish.Type.MEAT).build(),
Dish.builder().name("chicken").vegetarian(false).calories(400).type(Dish.Type.MEAT).build(),
Dish.builder().name("french").vegetarian(true).calories(530).type(Dish.Type.MEAT).build(),
Dish.builder().name("rice").vegetarian(true).calories(350).type(Dish.Type.MEAT).build());
stream.forEach(System.out::println);
}
private static List<String> getDishNamesByCollections(List<Dish> menu){
List<String> temp = new ArrayList<>();
for(Dish dish:menu){
if(dish.getCalories()<400){
temp.add(dish);
}
}
//之前的排序方式
temp.sort(new Comparator<Dish>() {
@Override
public int compare(Dish o1, Dish o2) {
return o1.getCalories()>o2.getCalories();
}
});
//使用lambda后
Collections.sort(temp, (d1, d2) -> Integer.compare(d1.getCalories(), d2.getCalories()));
//Collections.sort(temp,Comparator.comparing(Dish::getCalories));
List<String> dishNameList = new ArrayList<>();
for (Dish d : temp) {
dishNameList.add(d.getName());
}
return dishNameList;
}
private static List<String> getDishNamesByStream(List<Dish> menu) {
//Predicate: boolean test(T t);
return menu.stream().filter(d -> d.getCalories() < 400).sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName).collect(toList());
}
}
Stream流简介
Stream流相关知识点
Stream流中涉及的几个概念
Stream流的操作类型:
a. 中断的流操作(Trabersable only once):Internal iteration
Stream<Dish> res = menuList.stream()
res.forEach(System.out::println); //正确输出
res.forEach(System.out::println); //报错,上一个流已被操作或者关闭
b. 不可中断的操作(External):External iteration
Stream流常用的操作分类
Intermediate operations:操作会产生一个新的stream,相同类型,可以进行链式操作。此类操作有:filter/map/limit/sorted/distinct等
menu.stream().filter(d->{
System.out.printIn("filter"+d.getName());
return d.getCalories()>300;
})
.map(d->{
System.out.printIn("map"+d.getName());
return d.getName();
})
.limit(3).collect(toList());
输出结果:
filter->xxx
map->xxx
filter->yyy
map->yyy
...
Terminal operations:操作会中断,如上面例子中的res.forEach操作,此类操作有:reduce,forEach,count,collect等等。
Stream流的创建
通过Collection接口,其中提供了Stream stream()创建stream,可以通过Collection创建
private static Stream<String> getStreamFromCollection(){
List<String> list = Arrays.asList("hello","java");
return list.stream();
}
通过可变长度values创建
private static Stream<String> getStreamFromValues(){
return Stream.of("hello","java");
}
通过arrays创建
private static Stream<String> getStreamFromArrays(){
return Arrays.stream(new String[]{"hello","stream"});7
}
通过文件创建
private static Stream<String> getStreamFromFile(){
Path path = Paths.get("具体路径");
try(Stream<String> lines = Files.lines(path)){
lines.forEach(System.out::println);
return lines;
}catch(IOException e){
throw new RuntimeException(e);
}
}
通过iterator迭代创建
private static Stream<Integer> getStreamFromIterator(){
//0,2,4,6,8,10
Stream<Integer> stream = Stream.iterate(0,n->n+2).limit(6);
return stream;
}
通过Generate创建,generate传入一个Supplier即可
private static Stream<Double> createStreamFromGenerate(){
return Stream.generate(Math::random).limit(5);
}
自定义对象获取stream流
static class Obj{
private int id;
private String name;
//ignore getter and setter
}
static class ObjSupplier implements Supplier<Obj>{
private int index = 0;
private Random random = new Random(System.currentTimeMillis());
@Override
public Obj get(){
index = random.nextInt(100);
return new Obj(index,"name is "+index);
}
}
private static Stream<Obj> createObjStreamFromGenerate(){
return Stream.generate(new ObjSupplier()).limit(10);
}
Stream流中涉及的方法
filter,map,limit,skip,distinct,flatmap方法
//1.filter:传入一个Predicate,用来过滤
List<Integer> elements = Arrays.asList(1,2,3,3,4,5,6,7,8);
//过滤偶数元素
List<Integer> res = elements.stream().filter(a->a%2==0).collect(toList());
//元素的去重
res = elements.stream().distinct().collect(toList());
//元素的截断
res = elements.stream().skip(5).collect(toList()); //[5,6,7,8]
res = elements.stream().limit(5).collect(toList());//[1,2,3,3,4]
//2.map,传入一个Function,用来匹配
List<Integer> elements = Arrays.asList(1, 2, 2, 3, 3, 4, 5, 5, 5, 6);
//对元素进行加工,乘以2
List<Integer> res = elements.stream().map(a -> a*2).collect(toList());
//条件过滤
@Data
@Builder
class Person{
private String name;
private Integer age;
}
List<Person> persons = Arrays.asList(
Person.builder().name("zs").age(21).build(),
Person.builder().name("ls").age(22).build(),
Person.builder().name("ww").age(23).build(),
Person.builder().name("zl").age(24).build(),
Person.builder().name("wb").age(25).build()
);
List<String> pers = persons.stream().map(a->a.getName()).collect(toList());
List<String> pers = persons.stream().map(a->a.getName()).collection(toList());
persons.stream().map(a->a.getName).collection(toList()).forEach(System.out::println);
//3.flatmap,扁平化处理,flatMap[ Stream flatMap(Function super T,? extends Stream extends R>> mapper);]
////简而言之,传入一个Function,返回一个Stream
//需求:一个字符串数组,借助stream去除其中的重复字符
String[] words = {"hello","world"};
Stream<String[]> stream = Arrays.stream(words).map(w -> w.split(""));//1.数组中的每个元素字符串变为字符数组,Stream
//可以通过以下输出测试,结果是helloword
//Arrays.stream(words).map(w -> w.split("")).collect(toList()).forEach(a-> Arrays.stream(a).forEach(System.out::print));
//扁平化处理{H,e,l,l,o,W,o,r,l,d}
Stream<String> streamStream = stream.flatMap(Arrays::stream); //2.传入数组流,处理其中的元素,传出Stream字符串流
streamStream.distinct().forEach(System.out::print); //去重,得Heldword
find、match、reduce方法:
//1.match方法,用来过滤
Stream<Integer> stream = Arrays.stream(new Integer[]{1,2,3,4,5,6,7});
//1.1 过滤大于0的元素
//allMatch:每个元素都满足传入的Predicate条件,Predicate:boolean test(T t);
boolean matched = stream.allMatch(i -> i>0);
assert matched : "some elements not matched";
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//1.2 存在一个大于6,anyMatch
matched = stream.anyMatch(integer -> integer>6);
System.out.println(matched);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//1.3 没有存在小于0的,noneMatch
matched = stream.noneMatch(integer -> integer<0);
System.out.println(matched);
//2.find:返回值为Optional类型
Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
Optional<Integer> optional0 = stream.filter(i -> i % 2 == 0).findAny();
//2.1 根据条件查找某个值,打印存在的满足条件的第一个值
stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7});
Optional<Integer> optional1 = stream.filter(i -> i < 10).findAny();
System.out.println(optional1.get()); //打印满足条件的数据,1
//2.2 查询条件为空时,常用来解决空指针异常
stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7});
Optional<Integer> optional3 = stream.filter(i -> i > 10).findAny();
System.out.println(optional3.orElse(-1)); //找不到返回默认值-1
//类似于:根据某个条件去找,满足条件的返回,未满足返回默认值
int result = find(new Integer[]{1, 2, 3, 4, 5, 6, 7}, -1, i -> i > 100);
System.out.println(result);
private static int find(Integer [] values, int defdefaultValue, Predicate<Integer> predicate){
for (Integer value : values) {
if(predicate.test(value)){
return value;
}
}
return defdefaultValue;
}
//2.3判空操作
stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7});
Optional<Integer> optional4 = stream.filter(i -> i%2 == 0).findFirst();
option4.isPresent(); //判空操作,存在返回true,不存在,返回false;
option4.ifPresent(System.out::println); //ifPresent(Consumer),判断结果为非空时,执行某个操作。
//filter再一次过滤,得到Functional
System.out.print(option4.filter(i->i==2).get()); //public Optional filter(Predicate super T> predicate)...
//2.4传入自定义的Supplier
option4.orElseGet(传入自定义的supplier); //默认值为自定义的supplier,注:Supplier接口,涉及方法:T get();
stream = Arrays.stream(new Integer [] {1,2,3,4,5,6,7});
Optional<Integer> optional5 = stream.filter(i -> i>8).findFirst();
optional5.orElseGet(new customSupplier());
static class customSupplier implements Supplier<Integer>{
@Override
public Integer get() {
int i = new Random().nextInt(10);
System.out.println("没有找到满足条件的值,返回一个默认值:"+i);
return i;
}
}
//其中的map方法,可以自定义一个optional,返回optional
//3.reduce:聚合操作,类似元素的聚合操作,传入参数BiFunction,terminate类型的操作reduce,聚合作用根据你传入的Function进行对应的操作
Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//3.1 打印所有元素的和操作
//T reduce(T identity, BinaryOperator accumulator);
//0是初始值,0+1+2+3+...+7
Integer result = stream.reduce(0,(i,j)->i+j);
//等价于
Integer result = stream.reduce(0,Integer::sum);
System.out.print(result);
//3.2不给初始值,reduce操作的返回值为Optional,即Optional reduce(BinaryOperator accumulator);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//计算,如果结果存在,输出它,Optional reduce(BinaryOperator accumulator);
stream.reduce((i, j) -> i + j).ifPresent(System.out::println);
//3.3取最大值
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
stream.reduce((i,j) -> {
return i > j? i : j;
}).ifPresent(System.out::println);
//等价于
stream.reduce(Integer::max).ifPresent(System.out::println);
//3.4偶数相乘,先过滤,在相乘
Integer reduce = stream.filter(i -> i%2 == 0).reduce(1,(a,b)->a*b);
System.out.print(reduce);
//通过optional继续操作
Optional.of(reduce).ifPresent(System.out::println);
常见的基本数据类型的操作:Numeric streams
Stream<Integer> stream = Arrays.stream(new Integer[]{1,2,3,4,5,6,7});
//需求1:filter >3 and get sum
Stream<Integer> integerStream = stream.filter(i->i.intValue()>3);
integerStream.reduce(0,Integer::sum).ifPresent(System.out::println);
//或:stream.filter(i->i.intValue()>3).reduce(0,Integer::sum).ifPresent(System.out::println);
//或:stream.filter(i->i.intValue()>3).reduce(Integer::sum).ifPresent(System.out::println);
//存在的问题:
//Integer的内存占用量大,int(4byte/32bit),比Integer内存占用量小,java提供了IntegerStream,方便使用拆箱后的数据
//解决方法:
//将Integer转为int,使用mapToInt,需要传入参数ToIntFunction
//Integer result = stream.filter(i -> i.intValue() > 3).reduce(0, Integer::sum);
int sum = stream.mapToInt(Integer::intValue).filter(i -> i > 3).sum();
//int reduce = intStream.filter(i -> i > 3).reduce(0, (a, b) -> a + b);
//int类型转为Integer,即装箱操作
//给定a=9,在1-1000范围内找到与a结合满足勾股定理的数,并放入到数组中result[1,2,3...]
//类似切片,产生1-100的intStream
int a = 9;
/*IntStream intStream = IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a * a + b * b) % 1 == 0);
intStream.forEach(System.out::println);*/
//boxed装箱为对象,利用对象,map(Function super T, ? extends R> mapper)
//map(Function super T, ? extends R> mapper)
IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
.boxed().map(x->new int[]{a,x,(int)Math.sqrt(a*a+x*x)})
.forEach(r->System.out.println("a="+r[0]+", b="+r[1]+", c="+r[2]));
//或直接转为对应的对象操作,省略装箱
IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a * a + b * b) % 1 == 0)
.mapToObj(b->new int[]{a,b,(int)Math.sqrt(a*a+b*b)})
.forEach(r->System.out.println("a="+r[0]+", b="+r[1]+", c="+r[2]));
Stream流练习
//数据准备:
@Data
@Builder
@ToString
public class Trader {
private final String name;
private final String city;
public Trader(String n,String c){
this.name = n;
this.city = c;
}
}
@Data
@Builder
@ToString
public class Transaction {
private final Trader trader;
private final int year;
private final int value;
public Transaction(Trader trader, int year, int value) {
this.trader = trader;
this.year = year;
this.value = value;
}
public Trader getTrader(){
return this.trader;
}
}
public class StreamInAction {
public static void main(String[] args) {
Trader raoul = Trader.builder().name("Raoul").city("Cambridge").build();
Trader mario = Trader.builder().name("Mario").city("Milan").build();
Trader alan = Trader.builder().name("Alan").city("Cambridge").build();
Trader brian = Trader.builder().name("Brian").city("Cambridge").build();
List<Transaction> transactions = Arrays.asList(
Transaction.builder().trader(brian).year(2011).value(300).build(),
Transaction.builder().trader(raoul).year(2012).value(1000).build(),
Transaction.builder().trader(raoul).year(2011).value(400).build(),
Transaction.builder().trader(mario).year(2012).value(710).build(),
Transaction.builder().trader(mario).year(2012).value(700).build(),
Transaction.builder().trader(alan).year(2012).value(950).build()
);
//1.获取所有在2011年的交易,并且按照交易的量由小到大对它们进行排序
/*List result = transactions.stream().filter(transaction -> transaction.getYear() == 2011)
.sorted(Comparator.comparing(Transaction::getValue))
.collect(toList());
System.out.println(result);*/
//2.在贸易产品中,那些是唯一的城市?
/*transactions.stream().map(t->t.getTrader().getCity())
.distinct().forEach(System.out::println);*/
//3.查找出所有来自于Cambridge的交易员,并按照名字对他们进行排序
/*transactions.stream().map(Transaction::getTrader)
.filter(trader -> trader.getCity().equals("Cambridge"))
.distinct()
.sorted(Comparator.comparing(Trader::getName))
.forEach(System.out::println);*/
//4.按照ascII码排序,返回所有交易员的名字
String value = transactions.stream().map(transaction -> transaction.getTrader().getName())
.distinct()
.sorted()
.reduce("", (name1, name2) -> name1 + name2);
System.out.println(value);
//5.是否存在来自Milan的交易员
boolean liveInMilan1 = transactions.stream().anyMatch(t -> t.getTrader().getCity().equals("Milan"));
boolean liveInMilan2 = transactions.stream().map(Transaction::getTrader).anyMatch(t -> t.getCity().equals("Milan"));
System.out.println(liveInMilan1);
System.out.println(liveInMilan2);
//6.打印出所有来自于Cambridge交易员的商品的值
transactions.stream()
.filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getValue)
.forEach(System.out::println);
//7.求所有商品中最高价值的产品
Optional<Integer> maxValue = transactions.stream().map(Transaction::getValue).reduce(Integer::max);//(i,j)->i>j?i:j
System.out.println(maxValue.get());
//8.找出所有商品中价格最低的商品
Optional<Integer> minValue = transactions.stream().map(Transaction::getValue).reduce(Integer::min);
System.out.println(minValue.get());
}
}
代码在这儿