Java8 随笔记录(一)

前面也有写一篇jdk8 + guava的博文,都是一些零碎的记录,感觉不是很好。

都说java8运行速度比之前的其他版本要快很多,这都要归功于Stream的概念,既然能那么快就从它与多线程的关系说起吧。

 

1、java 8 和多线程

在java8之前通过多线程来达到并行效果需要使用Thread API,syncharonized关键字,同时当线程访问并更新共享变量时,要是没有协调好线程之间的关系,可能就会出错。

而在java8中,会更多关注的是数据分块,而不是协调问题,所以基本上很少使用syncharonized,而是使用Stream的并行化方式来处理的。

如在两个CPU上刷选列表,可以先让一个CPU处理列表的前一半,另一个CPU处理后一半,即将大的数据流分块成小的数据流来处理,接着CPU会各自对自己的半个列表进行筛选,最后将两个结果合并到一个CPU上,在此基础上,当CPU越多,处理能力当然就会越强,既可以横向扩展。所以在处理时,基本上都会先将其转换成stream(parallelStream):

asList.stream().filter((String s) -> s.contains(“v")).collect(Collectors.toSet());

 

2、行为参数化

依旧使用上一个例子吧,行为参数化简单的来说就是把一个行为(即一段逻辑代码)封装起来,通过传递的方式将方法的行为参数化,即:上例中的(String s) -> s.contains(“v”)就表示一段行为,将其通过lambda方式作为参数传递至filter函数中,可以很大程度上简洁代码,且逻辑也很清晰。当lambda主体(即 -> 之后的内容)很多时,需要使用{}花括号将其包裹。

 

3、在java8中lambda当然是重点之一了

先看一下例子吧:

(Integer i) -> return “Study” + i;

(Strings) -> {“Study”;}

上面这两个其实都不是正确的lambda,第一个中必须使用花括号将这个控制流语句给包起来才可以,即(Integer i) ->{return “Study” + i};

至于第二个后面的部分是一个表达式,而不是语句,可以直接去掉花括号,或采用显式返回语句,即(Strings) -> {return “Study”;}

 

4、函数式接口

即只定义了一个抽象方法的接口(并不是之后一个方法,还可以有其他默认方法,或静态方法,仅仅是抽象方法只有一个而已)。java8中会大量的用到此接口,并且你可以看到一般像这种接口都会使用@FunctionalInterface来注解,简单的介绍几个最常用的吧:

1)java.util.function.Predicate<T>

     它包含一个boolean test(T t);抽象方法,所以一般是在需要处理boolean问题时使用它:

     stream.filter(vo -> vo.getAge()>25).distinct().skip(3).limit(6).collect(Collectors.toList());

2)java.util.function.Consumer<T>

     void accept(T t);   即对给定的参数T执行操作,最常用的莫过于forEach了:

     asList.forEach(System.out::print);

3)java.util.function.Function<T, R>

     R apply(T t); 接受一个参数T,还需要返回一个类型R,所以一般为将某一个对象的信息映射输出。

     str.chars().mapToObj(ch -> Character.valueOf((char)ch)).forEach(System.out::print); 

  
函数式接口 函数描述符 原始类型特化
Predicate<T>        T -> boolean              IntPredicate, LongPredicate, DoublePredicate                       
Consumer<T>      T -> void IntConsumer, LongConsumer, DoubleConsumer
Fuction<T,R> T -> R

IntFunction<R>, IntToDoubleFunction, IntToLongDouble

LongFunction<R>, LongToDoubleFunction, 

LongToIntFunction, 

DoubleFunction<R>

ToIntFunction<T>, ToDoubleFunction<T>,

ToLongFunction<T>

Suppliter<T> () -> T

BooleanSupplier, IntSupplier, LongSuppliter,

DoubleSuppliter

UnaryOperator<T> T -> T

IntUnaryOperator, LongUnaryOperator,

DoubleUnaryOperator

BinaryOperator<T> (T, T) -> T

IntBinaryOperator, LongBinaryOperator,

DoubleBinaryOperator

BiPredicate<L, R> (L, R) -> boolean  
BiConsumer<T, U> (T, U) -> void

ObjIntConsumer<T>, ObjLongConsumer<T>,

ObjDoubleConsumer<T>

BiFunction<T, U, R> (T, U) -> R

ToIntBiFunction<T, U>, ToLongBiFunction<T, U>,

ToDoubleBiFunction<T, U>

 

5、在java8中,因为泛型只能绑定到引用类型,所以就会存在一个装箱 和 拆箱的问题:

装箱:将原始类型转换为引用类型(int -> Integer)。

拆箱:将引用类型转换为原始类型(Integer -> int)。

虽然有提供自动装箱过程,但由于装箱之后的存储值会需要更多的内存,且在搜索时也需要更多的内存来获取被包裹的原始值,在性能方面会消耗,所以提供了原始类型前缀的接口DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction、ToIntFunction、IntToFunction等。

 

6、lambda与局部变量

要在lambda中使用局部变量,需要为变量添加final修饰符,guava中也存在。

对于局部变量的限制是因为:实例变量存储在队中,而局部变量存在在栈中,如果lambda可以直接访问局部变量,而lambda又在线程中使用,则会使用到lambda的线程,可能会在分配该变量的线程将这个变量收回之后才去访问该变量。因此当java访问自由局部变量时,实际上访问的是它的副本,而不是原始值。

 

7、lambda匿名类与闭包

所谓闭包,也就是一个函数的实例,只是可以将这个实例作为参数传递给另一个函数,并可以无限制的访问和修改其作用域之外的变量。而lambda虽然也可以作为参数传递给另一个函数,但它不能修改定义lambda的方法的局部变量,它和闭包还是有区别的。

 

8、lambda与方法引用,即使用::方式直接调用对应的方法

lambda中的方法引用也用的蛮多的,如下Math::random即Math.random()函数的引用:

Stream.generate(Math::random).limit(5);

 

构造函数的引用:

//无参构造函数

Supplier<UserVO> su = UserVO::new;

UserVO vo01 = su.get();

 

//一个参数的构造函数

Function<String, UserVO> fun = UserVO::new;

UserVO vo02 = fun.apply("xiaomi");

 

//两个参数的构造函数

BiFunction<String, Integer, UserVO> bifun = UserVO::new;

UserVO vo03 = bifun.apply("xiaomi", 4);

 

//多个参数的构造函数需要自己去自定义了

DataFunction<String, String, String, String, Record> data = Record::new;

Record record = data.apply("title", "content.....", "https://www.xiaomi.com", "search");

猜你喜欢

转载自study121007.iteye.com/blog/2297894