jdk1.8中新特性java Stream Api的呈现是java函数式编程引入之后的魔法糖之一。Stram API主要对流进行函数操作,使你的代码更简洁易读,更灵活,性能更好。我们通过以下几方面介绍Stream Api如何让我们的代码更加丝滑。
1.创建流
流的创建直接使用Stream类的方法进行创建。
1.1.创建空流
Stream<Integer> emptyStream = Stream.empty();
1.2.根据数组和集合创建流
//根据数组创建流
int[] intArray = new int[]{1,3,4,5,7};
IntStream intStream = Arrays.stream(intArray);
Stream<Integer> integerStream = Stream.of(1,2,34,5,6);
//通过集合创建数组
List<String> strList = Arrays.asList("a","b","c");
Stream<String> stringStream = strList.stream();
Set<String> strSet = new HashSet<>(strList);
Stream<String> stringStream1 = strSet.stream();
1.3.创建无限流
Stream<Integer> generateStream = Stream.generate(()->new Random().nextInt());
Stream<Integer> iterateStream=Stream.iterate(1,x->x+1);
2.流的操作
关于流的操作主要有map,sorted,filter,collect等方法具体的使用细节,以下通过一个实例来讲解一下。
Trader.java
public class Trader {
private final String name;
private final String city;
public Trader(String n,String c){
this.name=n;
this.city=c;
}
public String getName() {
return name;
}
public String getCity() {
return city;
}
public String toString(){
return "Trader:"+this.name+" in "+this.city;
}
}
Transaction.java
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 trader;
}
public int getYear() {
return year;
}
public int getValue() {
return value;
}
public String toString(){
return "{"+this.trader+", "+"year: "+this.year+", "+"value:"+this.value+"}";
}
}
TransactionUtil.java
public class TransactionUtil {
private static List<Transaction> init(){
Trader raoul=new Trader("Raoul", "Cambridge");
Trader mario=new Trader("Mario", "Milan");
Trader alan=new Trader("Alan", "Cambridge");
Trader brian=new Trader("Brian", "Cambridge");
List<Transaction> transactions= Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950),
new Transaction(mario, 2013, 710),
new Transaction(mario, 2013, 700),
new Transaction(raoul, 2013, 950)
);
return transactions;
}
public static void main(String[] args){
List<Transaction> transactionList = init();
List<Integer> yearList = new ArrayList<>();
//获取所有交易对应的年份
yearList = transactionList.stream()
.map(Transaction::getYear)
.collect(toList());
//排序
List<Transaction> sortedTransactionList = new ArrayList<>();
sortedTransactionList = transactionList.stream()
.sorted(comparing(Transaction::getValue))
.collect(toList());//comparing作为比较函数
System.out.println("sortedTransactionList:"+sortedTransactionList);
//分块
Map<Boolean,List<Transaction>> partitionMap = transactionList.stream()
.collect(partitioningBy(transaction -> transaction.getValue()>600));
System.out.println("partitionMap:"+partitionMap);
//分组
Map<Integer,List<Transaction>> map = new HashMap<>();
map = transactionList.stream()
.collect(groupingBy(Transaction::getYear));//collect作为收集器,groupingBy作为分组函数
Map<Integer,Long> map1 = transactionList.stream().collect(groupingBy(Transaction::getValue,counting()));//分组之后计数
System.out.println("groupMap:"+map);
System.out.println("groupCountMap:"+map1);
//找到额度最小的交易
Optional<Transaction> minestTransaction = transactionList.stream().min(comparing(Transaction::getValue));
System.out.println("minestTransaction:"+minestTransaction.get());
Optional<Transaction> maxTransaction = transactionList.stream()
.collect(maxBy(comparingInt(Transaction::getValue)));
System.out.println("maxTransaction:"+maxTransaction);
//
//获取所有交易员名词的连接字符串
String traderStr = transactionList.stream()
.map(transaction -> transaction.getTrader().getName())
.distinct()
.peek(System.out::println)
.sorted()
.collect(joining());
System.out.println("traderStr:"+traderStr);
boolean existed = transactionList.stream().map(transaction ->transaction.getTrader().getName()).distinct()
.anyMatch(name->name.equals("Raoul"));
System.out.println("existed:"+existed);
IntSummaryStatistics intSummaryStatistics = transactionList.stream().mapToInt(Transaction::getValue).summaryStatistics();
System.out.println("intSummaryStatistics:"+intSummaryStatistics);
int totalValue = transactionList.stream().collect(reducing(0,Transaction::getValue,(i,j)->i+j));
System.out.println("totalValue:"+totalValue);
}
}
以上示例讲解了Stream常见的方法的具体使用方法。
另外,map作为映射,但是一些场景可能需要使用flatMap才能实现,flatMap如同方法名作为平面,将两组流都打散进行计算。以下我们通过一个例子进行讲解,勾股定律大家一定比较熟,那么我们如果写一个方法,如果是用map只能对一个参数做映射,没办法将三角形的三个边进行公示关联。但是flatMap可以的,把两条边的数据分别当做流,然后打散进行公示关联,就可以计算。具体如下:
//勾股定律
public static Stream<int[]> getTriples(int start,int end,int limit){
Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(start,end).boxed()
.flatMap(a->
IntStream.rangeClosed(a,end)
.filter(b->Math.sqrt(a*a+b*b)%1==0)
.mapToObj(b->new int[]{a,b,(int)Math.sqrt(a*a+b*b)})).limit(limit);
return pythagoreanTriples;
}
3.流的特性
3.1不存储数据
Stream<Integer> iterateStream=Stream.iterate(1,x->x+1);
//generateStream.limit(100).forEach(x->System.out.println(x));
iterateStream.limit(10).forEach(System.out::println);
iterateStream.limit(10).forEach(System.out::println);
执行之后,如以下结果,第二次报错
1
2
3
4
5
6
7
8
9
10
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatefulOp.<init>(ReferencePipeline.java:647)
at java.util.stream.SliceOps$1.<init>(SliceOps.java:120)
at java.util.stream.SliceOps.makeRef(SliceOps.java:120)
at java.util.stream.ReferencePipeline.limit(ReferencePipeline.java:401)
at com.carson.commons.stool.stream.StreamUtil.main(StreamUtil.java:70)
上述例子可以看出流不具备存储数据的功能,都是一次性的。
3.2延迟特性
Stream在使用collect,forEach等终止方法之前都不会去执行数据输出操作。比如以下示例
Stream<Integer> arrayStream = Stream.of(1,2,4,6,7).filter(x->{
System.out.println("test_"+x);
return true;
});
System.out.println("check");
arrayStream.collect(Collectors.toList());
执行输出结果:
check
test_1
test_2
test_4
test_6
test_7
4.原始类型流
目录
Stream原始类型流有IntStream、DoubleStream,这个基本类型流都是与Stream一样继承BaseStream的,所以很多特性都是一致的,另外还增加了一些原始类型流的特性。
IntStream stream1 = IntStream.rangeClosed(0,10);
IntStream stream2 = IntStream.range(0,10);
stream1.forEach(System.out::println);
stream2.forEach(System.out::println);
rangeClosed是右区间闭合的,range是右区间不闭合的。
IntStream->Stream<Integer>是通过boxed()方法进行转换的。
IntStream stream1 = IntStream.rangeClosed(0,10);
Stream<Integer> stream = stream1.boxed();
Stream->IntStream通过IntStream mapToInt(ToIntFunction<? super T> mapper);进行转换
IntStream stream1 = IntStream.rangeClosed(0,10);
Stream<Integer> stream = stream1.boxed();
IntStream stream2 = stream.mapToInt(x->x*x);
以上操作同样适用于DoubleStream。