一、lambda表达式
Lambda表达式相当于是对接口抽象方法的重写
对比匿名内部类与lambda表达式
package com.bz.jdk8.demo01_Lambda;
public class Demo01LambdaIntro {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("执行一个线程");
}
}).start();
new Thread(()->{
System.out.println("使用了Lambda表达式");
}).start();
}
}
标准格式
()->{}
package com.bz.jdk8.demo01_Lambda;
public class Demo02LambdaUse {
public static void main(String[] args) {
goSwimming(new Swimmable() {
@Override
public void swimming() {
System.out.println("练习无参数无返回值的匿名内部类");
}
});
System.out.println("===============");
goSwimming(()-> System.out.println("练习无参数无返回值的Lambda"));
}
public static void goSwimming(Swimmable s) {
s.swimming();
}
}
省略写法
在Lambda标准格式的基础上,使用省略写法的规则为:
- 小括号内参数的类型可以省略。
- 如果小括号内有且仅有一个参数,则小括号可以省略。
- 如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号。
Lambda表达式的前提条件
Lambda表达式的前提条件:
- 方法的参数或变量的类型是接口。
- 这个接口中只能有一个抽象方法。
package com.bz.jdk8.demo01_Lambda;
public class Demo04LambdaCondition {
public static void main(String[] args) {
test(()-> System.out.println("吃饭了吗?"));
System.out.println("===========");
Flyable f = ()->{
System.out.println("飞呀飞。。。");
};
}
public static void test(Flyable flyable){
System.out.println("使用了lambda表达式");
}
}
@FunctionalInterface
interface Flyable {
public abstract void eat();
}
二、接口增加默认方法和静态方法
接口引入默认方法的背景
如果给接口新增抽象方法,所有实现类都必须重写这个抽象方法。 不利于接口的扩展。
package com.bz.jdk8.demo02interfaceupgrade;
public class Demo01InterfaceDefaultIntro{
}
interface A {
public abstract void test01();
}
class B implements A {
@Override
public void test01() {
System.out.println("B test01");
}
}
class C implements A {
@Override
public void test01() {
System.out.println("C test01");
}
}
默认方法
package com.bz.jdk8.demo02interfaceupgrade;
public class Demo02UseDefaultFunction {
public static void main(String[] args) {
AA aa = new BB();
aa.test1();
System.out.println("===========");
AA ac = new CC();
ac.test1();
}
}
interface AA{
default void test1(){
System.out.println("jdk8新增的默认的方法");
}
}
class BB implements AA{
}
class CC implements AA{
@Override
public void test1() {
System.out.println("重写了方法");
}
}
静态方法
package com.bz.jdk8.demo02interfaceupgrade;
public class Demo03UseStaticFunction {
public static void main(String[] args) {
DD dd = new EE();
DD.test();
}
}
interface DD{
public static void test(){
System.out.println("jdk8新增的静态方法");
}
}
class EE implements DD{
}
接口默认方法和静态方法的区别
- 默认方法通过实例调用,静态方法通过接口名调用。
- 默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法。
- 静态方法不能被继承,实现类不能重写接口静态方法,只能使用接口名调用。
如何选择呢?如果这个方法需要被实现类继承或重写,使用默认方法; 如果接口中的方法不需要被继承就使用静态方法。
三、函数式接口
内置函数式接口的由来
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
Supplier接口
java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。
package com.bz.jdk8.demo03functionalinterface;
import java.util.Arrays;
import java.util.function.Supplier;
public class Demo02Supplier {
public static void main(String[] args) {
printMax(()->{
int[] arr = new int[]{23,25,11,56,45};
Arrays.sort(arr);
return arr[arr.length - 1];
});
}
public static void printMax(Supplier<Integer> supplier){
System.out.println("supplier函数式接口");
Integer max = supplier.get();
System.out.println("max: "+max);
}
}
Consumer接口
java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定。
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Consumer;
public class Demo03Consumer {
public static void main(String[] args) {
System.out.println("开始了");
printHello((s)->{
System.out.println(s.toUpperCase());
});
}
public static void printHello(Consumer<String> consumer){
System.out.println("consumer");
consumer.accept("holle world");
}
}
默认方法:andThen 如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Consumer;
public class Demo04ConsumerAndThen {
public static void main(String[] args) {
System.out.println("开始啦");
printHello((String str) -> {
System.out.println(str.toLowerCase());
}, (String str) -> {
System.out.println(str.toUpperCase());
});
}
public static void printHello(Consumer<String> c1, Consumer<String> c2) {
System.out.println("aa");
String str = "Hello World";
c1.andThen(c2).accept(str);
}
}
Function接口
java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。
Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。使用的场景例如:将 String 类型转换为 Integer
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Function;
public class Demo05Function {
public static void main(String[] args) {
System.out.println("开始了...");
getNumber(str->{
return Integer.parseInt(str);
});
}
public static void getNumber(Function<String,Integer> function){
System.out.println("输入字符串得到int数据");
Integer apply = function.apply("98");
System.out.println("num:"+apply);
}
}
默认方法:andThen Function 接口中有一个默认的 andThen 方法,用来进行组合操作。
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Function;
public class Demo06FunctionAndThen {
public static void main(String[] args) {
getNumber(str->{
return Integer.parseInt(str);
},i->{
return i * 6;
});
}
public static void getNumber(Function<String,Integer> f1,Function<Integer,Integer> f2){
System.out.println("先转为int类型,如何再乘以6");
Integer count = f1.andThen(f2).apply("5");
System.out.println("结果:"+count);
}
}
Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate 接口。
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo07Predicate {
public static void main(String[] args) {
System.out.println("开始啦");
isLongName((String name) -> {
return name.length() > 3;
});
}
public static void isLongName(Predicate<String> predicate) {
System.out.println("aa");
boolean isLong = predicate.test("迪丽热巴");
System.out.println("是否是长名字: " + isLong);
}
}
默认方法:and,使用“与”逻辑实现“并且”的效果。 默认方法:or,实现逻辑关系中的“或”。 默认方法:negate,“非”(取反)。
package com.bz.jdk8.demo03functionalinterface;
import java.util.function.Predicate;
public class Demo08Predicate_And_Or_Negate {
public static void main(String[] args) {
test((String str) -> {
return str.contains("W");
}, (String str) -> {
return str.contains("H");
});
}
public static void test(Predicate<String> p1, Predicate<String> p2) {
String str = "Hello World";
boolean b = p1.and(p2).test(str);
if (b) {
System.out.println("即包含W,也包含H");
}
boolean b1 = p1.or(p2).test(str);
if (b1) {
System.out.println("包含W或者包含H");
}
boolean b2 = p1.negate().test("Hello W");
if (b2) {
System.out.println("不包含W");
}
}
}
四、方法引用
方法引用的格式
符号表示 ::
符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用。 应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用,
package com.bz.jdk8.demo04methodref;
import java.sql.SQLOutput;
import java.util.function.Consumer;
public class Demo01MethodRefIntro {
public static void printMax(Consumer<int[]> consumer) {
int[] arr = {11, 22, 33, 44, 55};
consumer.accept(arr);
}
public static void getSum(int[] arr){
int sum = 0;
for (int i : arr){
sum += i;
}
System.out.println("和:"+sum);
}
public static void main(String[] args) {
printMax(Demo01MethodRefIntro::getSum);
}
}
方法引用的注意事项:
方法引用是对Lambda表达式符合特定情况下的一种缩写,它使得我们的Lambda表达式更加的精简,也可以理解为 Lambda表达式的缩写形式 , 不过要注意的是方法引用只能"引用"已经存在的方法!
对象名::引用成员方法
@Test
public void test1(){
Date now = new Date();
Supplier<Long> supplier = ()->{
return now.getTime();
};
Supplier<Long> sp = now::getTime;
Long aLong = sp.get();
System.out.println("时间:"+aLong);
}
类名::静态方法
@Test
public void test2(){
Supplier<Long> supplier = ()->{
return System.currentTimeMillis();
};
Supplier<Long> sup = System::currentTimeMillis;
Long aLong = supplier.get();
Long aLong1 = sup.get();
System.out.println(aLong + "---"+aLong1);
}
类名::实例方法
@Test
public void test3(){
Function<String, Integer> f1 = String::length;
int length = f1.apply("hello");
System.out.println("length = " + length);
BiFunction<String, Integer, String> f2 = (String str, Integer index) -> {
return str.substring(index);
};
String str2 = f2.apply("helloworld", 3);
System.out.println("str2 = " + str2);
}
类名::new引用类的构造器
@Test
public void test04() {
Supplier<Person> su1 = Person::new;
Person person = su1.get();
System.out.println("person = " + person);
BiFunction<String, Integer, Person> bif = Person::new;
Person p2 = bif.apply("凤姐", 18);
System.out.println("p2 = " + p2);
}
类型[]::new
@Test
public void test05() {
Function<Integer, int[]> f1 = int[]::new;
int[] arr1 = f1.apply(10);
System.out.println(Arrays.toString(arr1));
}
五、Stream流
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象! Stream是流式思想,相当于工厂的流水线,对集合中的数据进行加工处理
体验集合操作数据的弊端
package com.bz.jdk8.demo05stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Demo01Intro {
public static void main(String[] args) {
List<String> arr = new ArrayList<>();
Collections.addAll(arr,"张无忌","周芷若","张强","张三丰");
List<String> list = new ArrayList<>();
for (String str : arr){
if (str.contains("张")){
list.add(str);
}
}
List<String> three = new ArrayList<>();
for (String str : arr){
if (str.length() == 3){
three.add(str);
}
}
System.out.println(Arrays.toString(list.toArray()));
System.out.println(Arrays.toString(three.toArray()));
System.out.println("=================");
arr.stream().filter(str->{
return str.startsWith("张");
}).filter(str->{
return str.length() == 3;
}).forEach(s -> System.out.println(s));
}
}
获取流
Collection 集合通过 stream 默认方法获取流
package com.bz.jdk8.demo05stream;
import java.util.*;
import java.util.stream.Stream;
public class Demo02GetStream {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Map<String, String> map = new HashMap<>();
Stream<String> stream3 = map.keySet().stream();
Stream<String> stream4 = map.values().stream();
Stream<Map.Entry<String, String>> stream5 = map.entrySet().stream();
}
}
Stream中的静态方法of获取流
Stream<String> stream6 = Stream.of("aa", "bb", "cc");
String[] strs = {"aa", "bb", "cc"};
Stream<String> stream7 = Stream.of(strs);
int[] arr = {11, 22, 33};
Stream<int[]> stream8 = Stream.of(arr);
stream方法分为:终结方法和函数拼接方法
package com.bz.jdk8.demo05stream;
import java.util.stream.Stream;
public class Demo03StreamNotice {
public static void main(String[] args) {
Stream<String> stream = Stream.of("aa", "bb", "cc");
stream.filter(s->{
System.out.println(s);
return true;
}).count();
}
}
常用方法
forEach 用来遍历流中的数据
@Test
public void testForEach() {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
one.stream().forEach(System.out::println);
}
count 方法来统计其中的元素个数
@Test
public void testCount() {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
long count = one.stream().count();
System.out.println("总数:"+count);
}
filter用于过滤数据,返回符合过滤条件的数据
@Test
public void testFilter() {
List<String> arr = new ArrayList<>();
Collections.addAll(arr, "宋远桥", "苏星河", "老子", "庄子", "孙子");
arr.stream().filter(str->str.length() == 3).forEach(System.out::println);
}
limit 方法可以对流进行截取,只取用前n个。
@Test
public void testLimit() {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
one.stream().limit(3).forEach(System.out::println);
}
skip 方法跳过前几个元素,获取一个截取之后的新流
@Test
public void testSkip() {
List<String> one = new ArrayList<>();
Collections.addAll(one, "迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子");
one.stream().skip(2).forEach(System.out::println);
}
map 方法将流中的元素映射到另一个流中
该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
@Test
public void testMap() {
Stream<String> original = Stream.of("11", "22", "33");
original.map(Integer::parseInt).forEach(System.out::println);
}
sorted 方法将数据排序
@Test
public void testSorted() {
Stream<Integer> stream = Stream.of(33, 22, 11, 55);
stream.sorted((o1, o2) -> o2-o1).forEach(System.out::println);
}
distinct 方法去除重复数据
@Test
public void testDistinct() {
Stream<Integer> stream = Stream.of(22, 33, 22, 11, 33);
stream.distinct().forEach(System.out::println);
Stream<String> streamStr = Stream.of("aa","bb","cc","bb");
streamStr.distinct().forEach(System.out::println);
System.out.println("===============");
Stream<Person> streamPer = Stream.of(
new Person("貂蝉", 18),
new Person("杨玉环", 20),
new Person("杨玉环", 20),
new Person("西施", 16),
new Person("西施", 16),
new Person("王昭君", 25)
);
streamPer.distinct().forEach(System.out::println);
}
Match 相关方法,判断数据是否匹配指定的条件
@Test
public void testMatch() {
Stream<Integer> stream = Stream.of(5, 3, 6, 1);
boolean none = stream.noneMatch(i -> i < 0);
System.out.println("是否都不满足:"+none);
}
find 方法,要找到某些数据
@Test
public void testFind() {
Stream<Integer> stream = Stream.of(33, 11, 22, 5);
Optional<Integer> any = stream.findAny();
System.out.println(any.get());
}
max 和 min 方法,获取最大和最小值
@Test
public void testMax_Min() {
Integer max = Stream.of(7, 5, 3, 8, 2, 6).max((o1, o2) -> o1 - o2).get();
System.out.println("最大值:"+max);
Integer min = Stream.of(7, 5, 3, 8, 2, 6).min((o1, o2) -> o1 - o2).get();
System.out.println("最小值:"+min);
}
reduce 方法将所有数据归纳得到一个数据
@Test
public void testReduce() {
Integer reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
System.out.println("x:" + x + ", y: " + y);
return x + y;
});
System.out.println("reduce:"+reduce);
System.out.println("==============");
Integer max = Stream.of(6,3,7,9).reduce(0,(x,y)->{
return x > y ? x : y;
});
System.out.println("最大值:"+max);
}
Stream流的map和reduce组合使用
@Test
public void testMapReduce() {
Integer totalAge = Stream.of(
new Person("刘德华", 58),
new Person("张学友", 56),
new Person("郭富城", 54),
new Person("黎明", 52))
.map(Person::getAge).reduce(0, Integer::sum);
System.out.println("totalAge = " + totalAge);
Integer maxAge = Stream.of(
new Person("刘德华", 58),
new Person("张学友", 56),
new Person("郭富城", 54),
new Person("黎明", 52))
.map(Person::getAge)
.reduce(0, Math::max);
System.out.println("maxAge = " + maxAge);
Integer count = Stream.of("a", "c", "b", "a", "b", "a")
.map(s -> {
if (s == "a") {
return 1;
} else {
return 0;
}
})
.reduce(0, Integer::sum);
System.out.println("count = " + count);
}
mapToInt 方法,将Stream中的Integer类型数据转成int类型
@Test
public void testNumericStream() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
stream.filter(i -> i > 3).forEach(System.out::println);
System.out.println("=========先将流中的Integer数据转成int,后续都是操作int类型=======");
IntStream intStream = Stream.of(1, 2, 3, 4, 5).mapToInt(Integer::intValue);
intStream.filter(i -> i > 3).forEach(System.out::println);
}
concat方法将两个流合并成为一个流
@Test
public void testContact() {
Stream<String> streamA = Stream.of("张三");
Stream<String> streamB = Stream.of("李四");
Stream<String> newStream = Stream.concat(streamA, streamB);
newStream.forEach(System.out::println);
}
综合案例
package com.bz.jdk8.demo05stream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
public class Demo05 {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
Collections.addAll(one,"迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七公");
List<String> two = new ArrayList<>();
Collections.addAll(two,"古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱", "张三");
Stream<String> streamA = one.stream().filter(str -> str.length() == 3).limit(3);
Stream<String> streamB = two.stream().filter(str -> str.startsWith("张")).skip(2);
Stream<String> stream = Stream.concat(streamA,streamB);
stream.map(Person2::new).forEach(System.out::println);
}
}
收集Stream流中的结果
Stream流中的结果到集合中
@Test
public void testStreamToCollection() {
Stream<String> stream = Stream.of("aa", "bb", "cc", "bb");
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));
System.out.println("hashSet="+hashSet);
}
Stream流中的结果到数组中
@Test
public void testStreamToArray() {
Stream<String> stream = Stream.of("aa", "bb", "cc");
String[] strings = stream.toArray(String[]::new);
for (String string : strings) {
System.out.println("string = " + string + ", 长度: " + string.length());
}
}
对流中数据进行聚合计算
@Test
public void testStreamToOther() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 58, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
Long count = studentStream.collect(Collectors.counting());
System.out.println("统计数量: " + count);
}
对流中数据进行分组
@Test
public void testGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
Map<String, List<Student>> socreGroup = studentStream.collect(Collectors.groupingBy(s -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
}));
socreGroup.forEach((k,v)-> System.out.println(k + "::" +v));
}
对流中数据进行多级分组
@Test
public void testCustomGroup() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
Map<Integer, Map<String, List<Student>>> map = studentStream.collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy((s) -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
})));
map.forEach((k, v) -> {
System.out.println(k);
v.forEach((k2, v2) -> {
System.out.println("\t" + k2 + " == " + v2);
});
});
}
对流中数据进行分区
@Test
public void testPartition() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 55),
new Student("柳岩", 52, 33));
Map<Boolean, List<Student>> map = studentStream.collect(Collectors.partitioningBy(s -> {
return s.getSocre() > 60;
}));
map.forEach((k,v)-> System.out.println(k + "=="+v));
}
对流中数据进行拼接
@Test
public void testJoining() {
Stream<Student> studentStream = Stream.of(
new Student("赵丽颖", 52, 95),
new Student("杨颖", 56, 88),
new Student("迪丽热巴", 56, 99),
new Student("柳岩", 52, 77));
String name = studentStream.map(Student::getName).collect(Collectors.joining("_", "^_^", "V_V"));
System.out.println(name);
}
并行的Stream流
串行的Stream流
@Test
public void test0Serial() {
Stream.of(4, 5, 3, 9, 1, 2, 6)
.filter(s -> {
System.out.println(Thread.currentThread() + "::" + s);
return s > 3;
}).count();
}
获取并行Stream流的两种方式
- 直接获取并行的流
- 将串行流转成并行流
@Test
public void testgetParallelStream() {
List<String> list = new ArrayList<>();
Stream<String> stream = list.parallelStream();
Stream<String> parallel = list.stream().parallel();
}
并行和串行Stream流的效率对比
private static final int times = 500000000;
long start;
@Before
public void init() {
start = System.currentTimeMillis();
}
@After
public void destory() {
long end = System.currentTimeMillis();
System.out.println("消耗时间:" + (end - start));
}
@Test
public void testParallelStream() {
LongStream.rangeClosed(0, times).parallel().reduce(0, Long::sum);
}
@Test
public void testStream() {
LongStream.rangeClosed(0, times).reduce(0, Long::sum);
}
@Test
public void testFor() {
int sum = 0;
for (int i = 0; i < times; i++) {
sum += i;
}
}
parallelStream线程安全问题
@Test
public void parallelStreamNotice() {
ArrayList<Integer> list = new ArrayList<>();
List<Integer> collect = IntStream.rangeClosed(1, 1000)
.parallel()
.boxed()
.collect(Collectors.toList());
System.out.println("collect.size = " + collect.size());
}
Fork/Join框架
parallelStream使用的是Fork/Join框架。Fork/Join框架自JDK 7引入。Fork/Join框架可以将一个大任务拆分为很多小任务来异步执行。
Fork/Join框架主要包含三个模块:
- 线程池:ForkJoinPool
- 任务对象:ForkJoinTask
- 执行任务的线程:ForkJoinWorkerThread
Fork/Join原理-分治法
ForkJoinPool主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题。典型的应用比如快速排序算法,ForkJoinPool需要使用相对少的线程来处理大量的任务。比如要对1000万个数据进行排序,那么会将这个任务分割成两个500万的排序任务和一个针对这两组500万数据的合并任务。以此类推,对于500万的数据也会做出同样的分割处理,到最后会设置一个阈值来规定当数据规模到多少时,停止这样的分割处理。比如,当元素的数量小于10时,会停止分割,转而使用插入排序对它们进行排序。那么到最后,所有的任务加起来会有大概2000000+个。问题的关键在于,对于一个任务而言,只有当它所有的子任务完成之后,它才能够被执行。
Fork/Join原理-工作窃取算法
Fork/Join最核心的地方就是利用了现代硬件设备多核,在一个操作时候会有空闲的cpu,那么如何利用好这个空闲的cpu就成了提高性能的关键,而这里我们要提到的工作窃取(work-stealing)算法就是整个Fork/Join框架的核心理念Fork/Join工作窃取(work-stealing)算法是指某个线程从其他队列里窃取任务来执行。
package com.bz.jdk8.demo05stream;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class Demo08ForkJoin {
public static void main(String[] args) {
long start = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
SumRecursiveTask task = new SumRecursiveTask(1, 99999999999L);
Long result = pool.invoke(task);
System.out.println("result = " + result);
long end = System.currentTimeMillis();
System.out.println("消耗时间: " + (end - start));
}
}
class SumRecursiveTask extends RecursiveTask<Long> {
private static final long THRESHOLD = 3000L;
private final long start;
private final long end;
public SumRecursiveTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length < THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
long middle = (start + end) / 2;
SumRecursiveTask left = new SumRecursiveTask(start, middle);
left.fork();
SumRecursiveTask right = new SumRecursiveTask(middle + 1, end);
right.fork();
return left.join() + right.join();
}
}
}
总结:
- parallelStream是线程不安全的。
- parallelStream适用的场景是CPU密集型的,只是做到别浪费CPU,假如本身电脑CPU的负载很大,那还到处用并行流,那并不能起到作用。
- I/O密集型 磁盘I/O、网络I/O都属于I/O操作,这部分操作是较少消耗CPU资源,一般并行流中不适用于I/O密集型的操作,就比如使用并流行进行大批量的消息推送,涉及到了大量I/O,使用并行流反而慢了很多。
- 在使用并行流的时候是无法保证元素的顺序的,也就是即使你用了同步集合也只能保证元素都正确但无法保证其中的顺序。
六、Optional类
Optional是一个没有子类的工具类,Optional是一个可以为null的容器对象。它的作用主要就是为了解决避免Null检查,防止NullPointerException。
package com.bz.jdk8.demo06optional;
import org.junit.Test;
import java.util.Optional;
public class Demo01 {
public static void main(String[] args) {
}
@Test
public void test05() {
User u = new User("Hello", 18);
Optional<User> op = Optional.of(u);
System.out.println(getUpperUserName2(op));
}
public String getUpperUserName2(Optional<User> op) {
String upperName = op.map(User::getUserName)
.map(String::toUpperCase)
.orElse("null");
return upperName;
}
public String getUpperUserName1(User u) {
if (u != null) {
String userName = u.getUserName();
if (userName != null) {
return userName.toUpperCase();
} else {
return null;
}
} else {
return null;
}
}
@Test
public void test02() {
Optional<String> op1 = Optional.of("凤姐");
if (op1.isPresent()) {
System.out.println(op1.get());
} else {
System.out.println("没有值");
}
}
@Test
public void test01() {
String userName = "凤姐";
if (userName != null) {
System.out.println("姓名为: " + userName);
} else {
System.out.println("姓名不存在");
}
}
}
七、新的日期和时间 API
旧版日期时间 API 存在的问题
- 设计很差: 在java.util和java.sql的包中都有日期类,java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期。此外用于格式化和解析的类在java.text包中定义。
- 非线程安全:java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
- 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
package com.bz.jdk8.demo07newdatetimeapi;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01 {
public static void main(String[] args) {
Date date = new Date(2022,9,23);
System.out.println(date);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for (int i = 0;i < 50;i++){
new Thread(()->{
Date now = null;
try {
now = sdf.parse("2022-12-10");
} catch (ParseException e) {
throw new RuntimeException(e);
}
System.out.println(now);
}).start();
}
}
}
JDK 8的日期和时间类
@Test
public void testLocalDate() {
LocalDate localDate = LocalDate.of(2022, 12, 20);
System.out.println(localDate);
LocalDate now = LocalDate.now();
System.out.println(now.getYear());
System.out.println(now.getMonthValue());
System.out.println(now.getDayOfMonth());
}
@Test
public void testLocalTime() {
LocalTime localTime = LocalTime.of(15, 43, 56);
System.out.println(localTime);
LocalTime now = LocalTime.now();
System.out.println("当前时间:"+now);
System.out.println("时:"+now.getHour()+"分:"+now.getMinute()+"秒:"+now.getSecond()+"纳秒:"+now.getNano());
}
@Test
public void testLocalDateTime() {
LocalDateTime localDateTime = LocalDateTime.of(2022, 12, 20, 15, 48, 30);
System.out.println(localDateTime);
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期:"+now);
System.out.println("当前年:"+now.getYear());
System.out.println("当前时:"+now.getHour());
}
@Test
public void testLocalDateTime2() {
LocalDateTime now = LocalDateTime.now();
LocalDateTime withYear = now.withYear(9102);
System.out.println("修改后的时间:"+withYear);
System.out.println(now == withYear);
System.out.println("增加了2年后:"+now.plusYears(2));
System.out.println("减少了5个月:"+now.minusMonths(5));
}
@Test
public void testEquals() {
LocalDateTime dateTime = LocalDateTime.of(2022, 12, 20, 16, 00, 00);
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间是否在指定时间之前:"+now.isBefore(dateTime));
System.out.println("当前时间是否在指定时间之后:"+now.isAfter(dateTime));
System.out.println("当前时间是否等于指定时间:"+now.isEqual(dateTime));
}
JDK 8的时间格式化与解析
@Test
public void test04() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分SS秒");
String format = now.format(dtf);
System.out.println("格式化后的日期:"+format);
for (int i = 0; i < 50; i++) {
new Thread(()->{
LocalDateTime parse = LocalDateTime.parse("2022年09月20 15时16分16秒", dtf);
System.out.println("parse:"+parse);
}).start();
}
}
这里解析错误:
Exception in thread “Thread-1” Exception in thread “Thread-13” Exception in thread “Thread-8” Exception in thread “Thread-6” Exception in thread “Thread-2” Exception in thread “Thread-15” Exception in thread “Thread-18” Exception in thread “Thread-11” Exception in thread “Thread-12” java.time.format.DateTimeParseException: Text ‘2022年09月20 15时16分16秒’ could not be parsed at index 10 at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) at java.time.LocalDateTime.parse(LocalDateTime.java:492)
Instant 类,时间戳
@Test
public void test07() {
Instant now = Instant.now();
System.out.println("Instant:"+now);
Instant plus = now.plusSeconds(20);
System.out.println("plus:"+plus);
Instant minusSeconds = now.minusSeconds(20);
System.out.println("minus:"+minusSeconds);
long epochSecond = now.getEpochSecond();
System.out.println("epoch:"+epochSecond);
}
JDK 8的计算日期时间差类
@Test
public void test08() {
LocalTime now = LocalTime.now();
LocalTime localTime = LocalTime.of(16, 17, 20);
Duration duration = Duration.between(now, localTime);
System.out.println("相差的天数:"+duration.toDays());
System.out.println("相差的小时数:"+duration.toHours());
System.out.println("相差的分钟数:"+duration.toMinutes());
System.out.println("相差的秒数:"+duration.toMillis());
System.out.println("================");
LocalDate nowDate = LocalDate.now();
LocalDate localDate = LocalDate.of(1998, 11, 14);
Period period = Period.between(localDate,nowDate);
System.out.println("相差的年:" + period.getYears());
System.out.println("相差的月:" + period.getMonths());
System.out.println("相差的天:" + period.getDays());
}
JDK 8的时间校正器
@Test
public void test09() {
LocalDateTime now = LocalDateTime.now();
TemporalAdjuster firstDayOfNextMonth = temporal -> {
LocalDateTime dateTime = (LocalDateTime)temporal;
return dateTime.plusMonths(1).withDayOfMonth(1);
};
LocalDateTime newDateTime = now.with(TemporalAdjusters.firstDayOfNextYear());
System.out.println("newDateTime = " + newDateTime);
}
JDK 8设置日期时间的时区
@Test
public void test10() {
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
System.out.println("bz = " + bz);
ZonedDateTime now1 = ZonedDateTime.now();
System.out.println("now1 = " + now1);
ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Vancouver"));
System.out.println("now2 = " + now2);
ZonedDateTime withZoneSameInstant = now2.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
System.out.println("withZoneSameInstant = " + withZoneSameInstant);
ZonedDateTime withZoneSameLocal = now2.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
System.out.println("withZoneSameLocal = " + withZoneSameLocal);
}
八、JDK 8重复注解与类型注解
重复注解的使用
自从Java 5中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8中使用@Repeatable注解定义重复注解。
package com.bz.jdk8.demo08annotation;
import org.junit.Test;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@MyTest("ta")
@MyTest("tb")
@MyTest("tc")
public class Demo01 {
@Test
@MyTest("ma")
@MyTest("mb")
public void test() {
}
public static void main(String[] args) throws NoSuchMethodException {
MyTest[] annotationsByType = Demo01.class.getAnnotationsByType(MyTest.class);
for (MyTest myTest : annotationsByType) {
System.out.println(myTest);
}
System.out.println("----------");
MyTest[] tests = Demo01.class.getMethod("test").getAnnotationsByType(MyTest.class);
for (MyTest test : tests) {
System.out.println(test);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface MyTests {
MyTest[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyTests.class)
@interface MyTest {
String value();
}
类型注解的使用
JDK 8为@Target元注解新增了两种类型: TYPE_PARAMETER , TYPE_USE 。 TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 TYPE_USE :表示注解可以再任何用到类型的地方使用。
package com.bz.jdk8.demo08annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayList;
public class Demo02 <@TypeParam T> {
private @NotNull int a = 10;
public void test(@NotNull String str, @NotNull int a) {
@NotNull double d = 10.1;
}
public <@TypeParam E extends Integer> void test01() {
}
}
@Target(ElementType.TYPE_USE)
@interface NotNull {
}
@Target(ElementType.TYPE_PARAMETER)
@interface TypeParam {
}
介绍!!
要散布阳光到别人心里,先得自己心里有阳光。 --罗曼罗兰
|