javac -source 1.7 -target 1.8 T.java 表示源文件版本为1.7,编译成的字节码使用1.8
lambda表达式,即由用户来提供行为的具体实现.
/**
* @author Sunset
* Created on 2016/05/15.
*/
public class T {
public static void main(String[] args) {
/**
* lambda语法,提供的是相应函数式接口的目标方法实现,并根据该实现创建一个接口的对象。
* 关于该目标方法的具体参数值,lambda表达式并不提供!!!!
* lambda表达式的标准写法为 (arg1,arg2) -> {statements;}
* 对于只有一个参数可以写为 arg -> {statements}
* 对于只有一条返回语句的实现可以写为 arg -> arg + "sss",其自动加return
*/
act(new Person("love"),p -> System.out.println(p.getName()));
// ClassName::method方法引用。
// 可以看做lambda表达式的一个特殊用法,该语法在stream的map、groupingBy等方法中特别有用。
// 其等价于classInstance -> classInstance.method()或
// classInstance -> {classInstance.method();}
// classInstance为ClassName类型的对应参数,并根据目标函数是否有返回值决定不同的调用方式。
// 以下等价于act(new Person("fff"),p -> p.getName());
act(new Person("fff"),Person::getName);
}
public static void act(Person p,MyLambda ml){
ml.sayHello();
ml.doSomthing(p);
ml.sayGoodBye();
}
}
/**
* 支持函数式接口的接口可以使用lambda表达式
*/
@FunctionalInterface
interface MyLambda{
//必须刚好提供一个目标方法(无default),lambda表达式将实际传入该方法,并创建对象,使用@FunctionalInterface进行约束.
void doSomthing(Person s);
default void sayHello(){
System.out.println("hello");
}
default void sayGoodBye(){
System.out.println("GoodBye");
}
}
class Person{
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
}
- Stream类,它提供了类似于python中generator的概念,即只保存算法的一个类。是一个高版本的iterator。
//优势,可以提供自动并行化处理,可以产生无限元素。
/**
*可以由以下几种方式产生
*Collection.stream()
*静态方法LongStream.of(1,2,3,4);
*自定义
**/
/**
*流操作分为两种类型:
*Intermediate:一个流后面可一个跟多个Intermediate操作,其作用是打开流,对其数据进行某种程度的映射,并返回一个新流。
*该类操作都是lazy的,只有在terminal方法触发后,才开始遍历元素,也就是仅仅调用该类方法只是将操作放到stream的操作序
*列中,等到调用terminal时再对每一个元素调用执行。该类操作有,
*map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
*terminal,将流中的每一个元素读出,并操作,一个流最多只能有一个terminal操作,且必须为最后一个操作。
*该类操作有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、
*noneMatch、 findFirst、 findAny、 iterator
**/
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Sunset
* Created on 2016/05/15.
*/
public class T {
public static void main(String[] args) {
/**
* -*- Intermiate方法 -*-
*filter过滤,接收一个Predicate
* map将每一个元素进行 1<->1 映射并返回新元素(可以返回不同的类型),接收一个Function
* flatmap,将一个元素进行1 -> *映射,多端结果用一个Stream保存
* peek获得每一个元素,不能改变元素,接收一个Consumer
* distinct去除重复元素
*
* limit(n)表示取前n個元素,skip(n)表示跳過前n個元素,這將#導致该操作前面#
* Intermediate操作所對應的元素數量減少。除非前面有sorted操作(不知道排序后的顺序,无法应用short-cut)
* eg:len(s) == 10,s.peek().sorted().peek().limit(5),则第一个peek以及sorted处理的元素个数为10,
* 第二个peek处理的元素个数为5
*
* -*- Terminal方法 -*-
* collect方法将其作为了个Collection返回
*
* reduce(start,Function),使用start作为初始种子,两两进行reduce操作
* min,max,sum,findFirst,findAny,count,forEach,forEachOrdered
* findFirst/findAny也是一个short-cut操作,可能skip后面的元素
*
* 以下三个Match方法并不一定遍历所有元素,只要足够返回条件,则skip剩余元素。
* allMatch,判断是否全部符合
* anyMatch,有一个符合即可
* noneMatch,无符合
*
**/
Stream<String> stringStream = Stream.of("a", "b", "c", "a");
//此时由于都是Intermediate操作所以并不执行,所以peek操作不会打印
Stream<Integer> ins = stringStream.filter(x -> x != null).map(x -> x.hashCode()).distinct().peek(x -> System.out.println(x));
//此时调用了一个terminal方法,所以前面的Intermediate操作也被执行,所以peek打印hash值
//anyMatch有skip功能,所以上面的peek不是打印全部元素。
ins.anyMatch(x -> x == 98);
/**
* 自定义生成流,先实现Supplier接口,然后调用Stream.generate()来生成
* 以下生成一个等差数列
*/
Stream<Integer> dc = Stream.generate(new Supplier<Integer>() {
int x = 0;
@Override
public Integer get() {
x += 3;
return x;
}
});
//不是一个并行流返回false
System.out.println(dc.isParallel());
//count用于终结触发前面的中间方法。
dc.limit(4).peek(x -> System.out.println(x)).count();
/**
* Collectors提供了很多有用的reduction方法,可以将stream通过collect转成集合
* Person::getAge为lambda表达式的特殊用法,等价于 person -> person.getAge()
*/
Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).
limit(100).
collect(Collectors.groupingBy(Person::getAge));
System.out.println(personGroups);
System.out.println();
}
}
class Person{
private int age;
private String name;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public Person setName(String name) {
this.name = name;
return this;
}
public Person setAge(int age) {
this.age = age;
return this;
}
}
class PersonSupplier implements Supplier<Person>{
private Random random = new Random();
@Override
public Person get() {
Person p = new Person();
p.setAge(random.nextInt(20));
p.setName("sss");
return p;
}
}
//再看reduce系列方法
1. Optional reduce(BinaryOperator<T> bo);
该方法在stream元素为空时,返回Optional.EMPTY,在元素个数为一时直接将该元素添加到Optional中,否则进行reduce操作,初始选择头两个元素,然后以两个元素的reduce结果作为下一次reduce的第一个元素,进行下一次reduce操作。
实际流程类似下方:
boolean foundAny = false;
T result = null;
for (T element : this stream) {
if (!foundAny) {
foundAny = true;
result = element;
}
else
result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
2. T reduce(T identity,BinaryOperator<T> bo);
该方法可以直接返回T,而不是一个Optional,因为提供了第一个元素identity,所以不会返回空结果,其余规则与一相同。
3.<R, A> R collect(Collector<? super T, A, R> collector);
可以注意到一二两个reduce操作,其要求每次reduce操作的结果(中间结果)以及最终返回结果都与T一致,所以系统提供一个灵活的collect方法。
其中R表示最终返回结果,A表示中间结果。
Collector接口的方法如下
//该方法返回一个Supplier对象(可用lambda),该对象用于提供一个中间结果,会用于实际reduce
Supplier<A> supplier()
//用于执行实际的reduce操作
BiConsumer<A, T> accumulator()
//将中间结果转换为返回结果类型
Function<A, R> finisher()
//用于标识流类型,Characteristics.CONCURRENT/UNORDERED有且只有一个在集合中。
Set<Characteristics> characteristics()
//暂时忽略
BinaryOperator<T> combiner()
collect的实际工作流程如下
1)调用collector.supplier().get()返回一个中间结果类型对象a。
2)对每个元素e调用collector.accumulator.accept(a,e)进行reduce操作,该调用每次都是用第一步产生的a,返回值是void,这意味着a是一个可变对象reduce操作才有意义。
3)调用collector.finisher().apply(a)返回真正的结果对象。
4.Collectors提供了许多方法可以快速生成Collector对象。总结如下:
groupingBy
partitionBy
toList
toMap
toSet
toConcurrentMap
averageing*
summing*
summarizing*//最终返回统计对象,可以获得avg/sum/min等数据
joining//连接元素
一个小demo,计算九个字符串的hashcode加和
/**
* @author Sunset
* Created on 2016/07/29.
*/
public class X {
public static void main(String[] args) {
List<List<String>> lists = new ArrayList<>();
List<String> l1 = new ArrayList<>();
l1.add("l1_1");
l1.add("l1_2");
l1.add("l1_3");
List<String> l2 = new ArrayList<>();
l1.add("l2_1");
l1.add("l2_2");
l1.add("l2_3");
List<String> l3 = new ArrayList<>();
l1.add("l3_1");
l1.add("l3_2");
l1.add("l3_3");
lists.add(l1);
lists.add(l2);
lists.add(l3);
Integer i = lists.stream().flatMap(Collection::stream).collect(new Collector<String, IntegerHoler, Integer>() {
@Override
public Supplier<IntegerHoler> supplier() {
return IntegerHoler::new;
}
@Override
public BiConsumer<IntegerHoler, String> accumulator() {
return (ih,s) -> ih.setI(ih.getI()+s.hashCode());
}
@Override
public BinaryOperator<IntegerHoler> combiner() {
return null;
}
@Override
public Function<IntegerHoler, Integer> finisher() {
return IntegerHoler::getI;
}
@Override
public Set<Characteristics> characteristics() {
Set<Characteristics> ss = new HashSet<Characteristics>();
ss.add(Characteristics.CONCURRENT);
return ss;
}
});
System.out.println(i);
}
}
class IntegerHoler{
private Integer i = new Integer(0);
public Integer getI() {
return i;
}
public IntegerHoler setI(Integer i) {
this.i = i;
return this;
}