本文章转载出处来自民工哥的总结,链接地址: 民工哥之路
Java现在发布的版本很快,每年两个,但是真正会被大规模使用的是三年一个的TLS版本。
? ? ? ? ? 每3年发布一个TLS,长期维护版本。意味着Java 8 ,Java 11, Java 17 才可能被大规模使用。
? ? 每年发布两个正式版本,分别是3月份和9月份。
函数编程
? ? ? 在Java世界里面,面向对象还是主流思想,对于习惯了面向对象编程的开发者来说,抽象的概念并不陌生。面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,程序也是如此,因此这两种编程方式我们都得学。
? ? ? ?这种新的抽象方式还有其他好处。很多人不总是在编写性能优先的代码,对于这些人来说,函数式编程带来的好处尤为明显。程序员能编写出更容易阅读的代码——这种代码更多地表达了业务逻辑,而不是从机制上如何实现。易读的代码也易于维护、更可靠、更不容易出错。
? ? ? 在写回调函数和事件处理器时,程序员不必再纠缠于匿名内部类的冗繁和可读性,函数式编程让事件处理系统变得更加简单。能将函数方便地传递也让编写惰性代码变得容易,只有在真正需要的时候,才初始化变量的值。
面向对象编程是对数据进行抽象;函数式编程是对行为进行抽象。
? ? ? 核心思想: 使用不可变值和函数,函数对一个值进行处理,映射成另一个值。
对核心类库的改进主要包括集合类的API和新引入的流Stream。流使程序员可以站在更高的抽象层次上对集合进行操作。
lambda表达式
? ? ? lambda表达式仅能放入如下代码: 预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。
? ? ? ?lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用。
list.forEach(n?->?System.out.println(n));?list.forEach(System.out::println);??//?使用方法引用
然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,如下所示:
list.forEach((String?s)?->?System.out.println("*"?+?s?+?"*"));
事实上,可以省略这里的lambda参数的类型声明,编译器可以从列表的类属性推测出来。
lambda内部可以使用静态、非静态和局部变量,这称为lambda内的变量捕获。
Lambda表达式在Java中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。
? ? ? ?Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大致应该长这样:
private?static?java.lang.Object?lambda$0(java.lang.String);
lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。
List<Integer>?primes?=?Arrays.asList(new?Integer[]{2,?3,5,7});
int?factor?=?2;
primes.forEach(element?->?{?factor++;?});
? ? Compile time error : "local variables referenced from a lambda expression must be final or effectively final" 另外,只是访问它而不作修改是可以的,如下所示:
List<Integer>?primes?=?Arrays.asList(new?Integer[]{2,?3,5,7});
int?factor?=?2;
primes.forEach(element?->?{?System.out.println(factor*element);?});
分类
惰性求值方法
lists.stream().filter(f?->?f.getName().equals("p1"))
如上示例,这行代码并未做什么实际性的工作,filter只是描述了Stream,没有产生新的集合。
如果是多个条件组合,可以通过代码块{}
及早求值方法
List<Person>?list2?=?lists.stream().filter(f?->?f.getName().equals("p1")).collect(Collectors.toList());
如上示例,collect最终会从Stream产生新值,拥有终止操作。
理想方式是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果。与建造者模式相似,建造者模式先是使用一系列操作设置属性和配置,最后调用build方法,创建对象。
stream & parallelStream
每个Stream都有两种模式: 顺序执行和并行执行。
顺序流:
List?<Person>?people?=?list.getStream.collect(Collectors.toList());
并行流:
List?<Person>?people?=?list.getStream.parallel().collect(Collectors.toList());
顾名思义,当使用顺序方式去遍历时,每个item读完后再读下一个item。而使用并行去遍历时,数组会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
parallelStream原理:
List?originalList?=?someData;split1?=?originalList(0,?mid);//将数据分小部分
split2?=?originalList(mid,end);
new?Runnable(split1.process());//小部分执行操作
new?Runnable(split2.process());
List?revisedList?=?split1?+?split2;//将结果合并
大家对hadoop有稍微了解就知道,里面的 MapReduce 本身就是用于并行处理大数据集的软件框架,其 处理大数据的核心思想就是大而化小,分配到不同机器去运行map,最终通过reduce将所有机器的结果结合起来得到一个最终结果,与MapReduce不同,Stream则是利用多核技术可将大数据通过多核并行处理,而MapReduce则可以分布式的。
stream与parallelStream性能测试对比
如果是多核机器,理论上并行流则会比顺序流快上一倍,下面是测试代码
long?t0?=?System.nanoTime();//初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法int?a[]=IntStream.range(0,?1_000_000).filter(p?->?p?%?2==0).toArray();l
ong?t1?=?System.nanoTime();//和上面功能一样,这里是用并行流来计算
int?b[]=IntStream.range(0,?1_000_000).parallel().filter(p?->?p?%?2==0).toArray();
long?t2?=?System.nanoTime();//我本机的结果是serial:?0.06s,?parallel?0.02s,证明并行流确实比顺序流快
System.out.printf("serial:?%.2fs,?parallel?%.2fs%n",?(t1?-?t0)?*?1e-9,?(t2?-?t1)?*?1e-9);
Stream中常用方法如下:
? ? ? ?~ stream(),?parallelStream()
? ? ? ?~ filter()
? ? ? ?~ findAny()?findFirst()
? ? ? ? ~sort
? ? ? ?~ forEach?void
? ? ? ? ~map(), reduce()
? ? ? ? ~flatMap()?- 将多个Stream连接成一个Stream
? ? ? ? ~collect(Collectors.toList())
? ? ? ?~ distinct,?limit
? ? ? ? ~count
? ? ? ?~ min,?max,?summaryStatistics
看下所有API:
常用例子
匿名类简写
new?Thread(?()?->?System.out.println("In?Java8,?Lambda?expression?rocks?!!")?).start();
//?用法
(params)?->?expression
(params)?->?statement
(params)?->?{?statements?}
forEach
//?forEachList?features?=?Arrays.asList("Lambdas",?"Default?Method",?
"Stream?API",?"Date?and?Time?API");
features.forEach(n?->?System.out.println(n));?//?使用Java?8的方法引用更方便,方法引用由::双冒号操作符标示,
features.forEach(System.out::println);
方法引用
构造引用
//?Supplier<Student>?s?=?()?->?new?Student();Supplier<Student>?s?=?Student::new;
对象::实例方法 Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应
//?set.forEach(t?->?System.out.println(t));set.forEach(System.out::println);
类名::静态方法
//?Stream<Double>?stream?=?Stream.generate(()?->?Math.random());
Stream<Double>?stream?=?Stream.generate(Math::random);
类名::实例方法
//??TreeSet<String>?set?=?new?TreeSet<>((s1,s2)?->?s1.compareTo(s2));
/*??这里如果使用第一句话,编译器会有提示:?Can?be?replaced?with?Comparator.naturalOrder,这句话告诉我们??String已经重写了compareTo()方法,在这里写是多此一举,这里为什么这么写,是因为为了体现下面??这句编译器的提示: Lambda can be replaced with method reference。好了,下面的这句就是改写成方法引用之后:?*/
TreeSet<String>?set?=?new?TreeSet<>(String::compareTo);
Filter & Predicate
常规用法
public?static?void?main(args[]{????
? ? ? List?languages?=?Arrays.asList("Java",?"Scala",?"C++",?"Haskell",?"Lisp");????
? ? ? System.out.println("Languages?which?starts?with?J?:");????
? ? ? ?filter(languages,?(str)->str.startsWith("J"));?????
? ? ? ?System.out.println("Languages?which?ends?with?a?");????
? ? ? ? filter(languages,?(str)->str.endsWith("a"));?????
? ? ? ? ?System.out.println("Print?all?languages?:");????filter(languages,?(str)->true);????
? ? ? ? ?System.out.println("Print?no?language?:?");????filter(languages,?(str)->false);?????
? ? ? ? ?System.out.println("Print?language?whose?length?greater?than?4:");????
? ? ? ? ? filter(languages,?(str)->str.length()?>?4);
}?
public?static?void?filter(List?names,?Predicate?condition)?{???
? ? ? ?names.stream().filter((name)?->?(condition.test(name))).forEach((name)?->?{????????System.out.println(name?+?"?");????});
}
多个Predicate组合filter
//?可以用and()、or()和xor()逻辑函数来合并Predicate,
//?例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String>?startsWithJ?=?(n)?->?n.startsWith("J");
Predicate<String>?fourLetterLong?=?(n)?->?n.length()?==?4;
names.stream()????
.filter(startsWithJ.and(fourLetterLong))????
.forEach((n)?->?System.out.print("nName,?which?starts?with?'J'?and?four?letter?long?is?:?"?+?n));
Map&Reduce
map将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个
List?costBeforeTax?=?Arrays.asList(100,?200,?300,?400,?500);
double?bill?=?costBeforeTax.stream().map((cost)?->?cost?+?.12*cost).reduce((sum,?cost)?->?sum?+?cost).get();
System.out.println("Total?:?"?+?bill);
Collectors
//?将字符串换成大写并用逗号链接起来List<String>?G7?=?Arrays.asList("USA",?"Japan",?"France",?"Germany",?"Italy",?"U.K.","Canada");
String?G7Countries?=?G7.stream().map(x?->?x.toUpperCase()).collect(Collectors.joining(",?"));
System.out.println(G7Countries);
????????Collectors.joining(", ")
????????Collectors.toList()
????????Collectors.toSet() ,生成set集合
????????Collectors.toMap(MemberModel::getUid, Function.identity())
????????Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl())
flatMap
将多个Stream连接成一个Stream
List<Integer>?result=?Stream.of(Arrays.asList(1,3),Arrays.asList(5,6)).flatMap(a->a.stream()).collect(Collectors.toList());
结果: [1, 3, 5, 6]
distinct
去重
List<LikeDO>?likeDOs=new?ArrayList<LikeDO>();
List<Long>?likeTidList?=?likeDOs.stream().map(LikeDO::getTid)????????????????
.distinct().collect(Collectors.toList());
count
计总数
int?countOfAdult = persons.stream() .filter(p?->?p.getAge()?>?18).map(person?->?new?Adult(person)) .count();
Match
boolean?anyStartsWithA?=????stringCollection.stream().anyMatch((s)?->?s.startsWith("a"));? ? ? ?
System.out.println(anyStartsWithA);? //?true
boolean?allStartsWithA?=????stringCollection .stream().allMatch((s)?->?s.startsWith("a"));
System.out.println(allStartsWithA);??????//?false
boolean?noneStartsWithZ?=????stringCollection.stream().noneMatch((s)?->?s.startsWith("z"));
System.out.println(noneStartsWithZ);??????//?true
min,max,summaryStatistics
最小值,最大值
List<Person>?lists?=?new?ArrayList<Person>();
lists.add(new?Person(1L,?"p1"));
lists.add(new?Person(2L,?"p2"));
lists.add(new?Person(3L,?"p3"));
lists.add(new?Person(4L,?"p4"));
Person?a?=?lists.stream().max(Comparator.comparing(t?->?t.getId())).get();System.out.println(a.getId());
如果比较器涉及多个条件,比较复杂,可以定制
?Person?a?=?lists.stream().min(new?Comparator<Person>()?{??????
@Override? ? ?
????public?int?compare(Person?o1,?Person?o2)?{? ? ? ? ? ?
? ? ?????????if?(o1.getId()?>?o2.getId())?return?-1;??????????
? ? ?????????if?(o1.getId()?<?o2.getId())?return?1;???????????
? ? ?????????return?0;??????
????}?
}).get();
summaryStatistics
//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer>?primes?=?Arrays.asList(2,?3,?5,?7,?11,?13,?17,?19,?23,?29);
IntSummaryStatistics?stats?=?primes.stream().mapToInt((x)?->?x).summaryStatistics();
System.out.println("Highest?prime?number?in?List?:?"?+?stats.getMax());
System.out.println("Lowest?prime?number?in?List?:?"?+?stats.getMin());
System.out.println("Sum?of?all?prime?numbers?:?"?+?stats.getSum());
System.out.println("Average?of?all?prime?numbers?:?"?+?stats.getAverage());
peek
可以使用peek方法,peek方法可只包含一个空的方法体,只要能设置断点即可,但有些IDE不允许空,可以如下文示例,简单写一个打印逻辑。
注意,调试完后要删掉。
List<Person>?lists?=?new?ArrayList<Person>();
lists.add(new?Person(1L,?"p1"));
lists.add(new?Person(2L,?"p2"));
lists.add(new?Person(3L,?"p3"));
lists.add(new?Person(4L,?"p4"));
System.out.println(lists);
List<Person>?list2?=?lists.stream().filter(f?->?f.getName().startsWith("p")).peek(t?->?{? ? ? ? ? ? ? ? ? ??
????????System.out.println(t.getName());????????????????
}).collect(Collectors.toList());
System.out.println(list2);
FunctionalInterface
理解注解 @FunctionInterface
/**?
*?An?informative?annotation?type?used?to?indicate?that?an?interface?
*?type?declaration?is?intended?to?be?a?<i>functional?interface</i>?as?
*?defined?by?the?Java?Language?Specification.?
*?
*?Conceptually,?a?functional?interface?has?exactly?one?abstract
*?method.??Since?{@linkplain?java.lang.reflect.Method#isDefault()
*?default?methods}?have?an?implementation,?they?are?not?abstract.??If?
*?an?interface?declares?an?abstract?method?overriding?one?of?the?
*?public?methods?of?{@code?java.lang.Object},?that?also?does?
*?<em>not</em>?count?toward?the?interface's?abstract?method?count?
*?since?any?implementation?of?the?interface?will?have?an?
*?implementation?from?{@code?java.lang.Object}?or?elsewhere.?
*
?*?<p>Note?that?instances?of?functional?interfaces?can?be?created?with?
*?lambda?expressions,?method?references,?or?constructor?references.?
*?
*?<p>If?a?type?is?annotated?with?this?annotation?type,?compilers?are?
*?required?to?generate?an?error?message?unless:?
*
?*?<ul>?
*?????<li>?The?type?is?an?interface?type?and?not?an?annotation?type,?enum,?or?class.?
*?????<li>?The?annotated?type?satisfies?the?requirements?of?a?functional?interface.?
*?</ul>?
*?
*?<p>However,?the?compiler?will?treat?any?interface?meeting?the?
*?definition?of?a?functional?interface?as?a?functional?interface?
*?regardless?of?whether?or?not?a?{@code?FunctionalInterface}?
*?annotation?is?present?on?the?interface?declaration.?
*?
*?@jls?4.3.2.?The?Class?Object?
*?@jls?9.8?Functional?Interfaces?
*?@jls?9.4.3?Interface?Method?Body?
*?@since?1.8?
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public?@interface?FunctionalInterface{}
????interface做注解的注解类型,被定义成java语言规范
????一个被它注解的接口只能有一个抽象方法,有两种例外
????第一是接口允许有实现的方法,这种实现的方法是用default关键字来标记的(java反射中java.lang.reflect.Method#isDefault()方法用来判断是否是default方法)
????第二如果声明的方法和java.lang.Object中的某个方法一样,它可以不当做未实现的方法,不违背这个原则: 一个被它注解的接口只能有一个抽象方法, 比如:?java public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
????如果一个类型被这个注解修饰,那么编译器会要求这个类型必须满足如下条件:
这个类型必须是一个interface,而不是其他的注解类型、枚举enum或者类class
这个类型必须满足function interface的所有要求,如你给包含两个抽象方法的接口增加这个注解,会有编译错误。
????编译器会自动把满足function interface要求的接口自动识别为function interface,所以你才不需要对上面示例中的 ITest接口增加@FunctionInterface注解。
自定义函数接口
@FunctionalInterface
public?interface?IMyInterface?{????
????void?study();
}
package?com.isea.java;
public?class?TestIMyInterface?{????
????????public?static?void?main(String[]?args)?{???????
?????????????IMyInterface?iMyInterface?=?()?->?System.out.println("I?like?study");???????
?????????????iMyInterface.study();????
????????}
}
内置四大函数接口
消费型接口:Consumer< T> void accept(T t)有参数,无返回值的抽象方法;
????????比如: map.forEach(BiConsumer<A, T>)
Consumer<Person>?greeter?=?(p)?->?System.out.println("Hello,?"?+?p.firstName);
greeter.accept(new?Person("Luke",?"Skywalker"));
供给型接口:Supplier < T> T get()无参有返回值的抽象方法;
以stream().collect(Collector<? super T, A, R> collector)为例:
比如:
Supplier<Person>?personSupplier?=?Person::new;
personSupplier.get();???//?new?Person
再如:
//?调用方法
<R,?A>?R?collect(Collector<??super?T,?A,?R>?collector)
//?Collectors.toSet
public?static?<T>?Collector<T,??,?Set<T>>?toSet()?{?????
????return?new?CollectorImpl<>((Supplier<Set<T>>)?HashSet::new,?Set::add,???????????????????????????????
????(left,?right)?->?{?left.addAll(right);?return?left;?
},???????????????????????????????
????CH_UNORDERED_ID);
}
// CollectorImpl
private?final?Supplier<A>?supplier;private?final?BiConsumer<A,?T>?accumulator;
private?final?BinaryOperator<A>?combiner;private?final?Function<A,?R>?finisher;
private?final?Set<Characteristics>?characteristics;CollectorImpl(Supplier<A>?supplier,??????????????
BiConsumer<A,?T>?accumulator,??????????????
BinaryOperator<A>?combiner,??????????????
Function<A,R>?finisher,??????????????
Set<Characteristics>?characteristics)?{????
this.supplier?=?supplier;????
this.accumulator?=?accumulator;????
this.combiner?=?combiner;????
this.finisher?=?finisher;???
this.characteristics?=?characteristics;
}
CollectorImpl(Supplier<A>?supplier,BiConsumer<A,?T>?accumulatorBinaryOperator<A>?combiner,Set<Characteristics>?characteristics{????
????this(supplier,?accumulator,?combiner,?castingIdentity(),?characteristics);
}
//?collect()方法实现
public?final?<R,?A>?R?collect(Collector<??super?P_OUT,?A,?R>?collector)?{????
A?container;???
?if?(isParallel()????????????
&&?(collector.characteristics().contains(Collector.Characteristics.CONCURRENT))????????????
&&?(!isOrdered()?||?collector.characteristics().contains(Collector.Characteristics.UNORDERED)))?
{????????
????container?=?collector.supplier().get();???????
?????BiConsumer<A,???super?P_OUT>?accumulator?=?collector.accumulator();???????
?????forEach(u?->?accumulator.accept(container,?u));????
}????
else?{????????
????container?=?evaluate(ReduceOps.makeRef(collector));????
}????
return?collector.characteristics()
.contains(Collector.Characteristics.IDENTITY_FINISH)? ??(R)?container? :?collector.finisher().apply(container);}
????????断定型接口:Predicate<T> boolean test(T t):有参,但是返回值类型是固定的boolean
比如: steam().filter()中参数就是Predicate
Predicate<String>?predicate?=?(s)?->?s.length()?>?0;predicate.test("foo");?????????????
?//?true
predicate.negate().test("foo");?????
//?false
Predicate<Boolean>?nonNull?=?Objects::nonNull;
Predicate<Boolean>?isNull?=?Objects::isNull;
Predicate<String>?isEmpty?=?String::isEmpty;
Predicate<String>?isNotEmpty?=?isEmpty.negate();
????????????函数型接口:?Function<T,R> R apply(T t)有参有返回值的抽象方法;
比如:?steam().map()中参数就是Function<? super T, ? extends R>;reduce()中参数BinaryOperator<T> (ps: BinaryOperator<T> extends BiFunction<T,T,T>)
Function<String,?Integer>?toInteger?=?Integer::valueOf;
Function<String,?String>?backToString?=?toInteger.andThen(String::valueOf);
backToString.apply("123");?????//?"123"
一些例子
????输出 年龄>25的女程序员中名字排名前3位的姓名
javaProgrammers.stream()??????????
.filter((p)?->?(p.getAge()?>?25))?????????
?.filter((p)?->?("female".equals(p.getGender())))??????????
.sorted((p,?p2)?->?(p.getFirstName().compareTo(p2.getFirstName())))??????????
.limit(3)??????????
//.forEach(e?->?e.setSalary(e.getSalary()?/?100?*?5?+?e.getSalary()))//涨工资? ? ? ? ??
.forEach((p)?->?System.out.printf("%s?%s;?",?p.getFirstName(),?p.getLastName()));
工资最高的 Java programmer
Person?person?=?javaProgrammers??????????
.stream()??????????
.max((p,?p2)?->?(p.getSalary()?-?p2.getSalary()))?????????
?.get()
将 Java programmers 的 first name 存放到 TreeSet
TreeSet<String>?javaDevLastName?=?javaProgrammers??????????
.stream()?????????
?.map(Person::getLastName)??????????
.collect(toCollection(TreeSet::new))
计算付给 Java programmers 的所有money
int?totalSalary?=?javaProgrammers?????????
?.parallelStream()??????????
.mapToInt(p?->?p.getSalary())??????????
.sum();
Comparator多属性排序: 先按名字不分大小写排,再按GID倒序排,最后按年龄正序排
public?static?void?main(String[]?args)?{?
????????????List<Person>?personList?=?getTestList();
?????????personList.sort(Comparator.comparing(Person::getName,?String.CASE_INSENSITIVE_ORDER)???
????????.thenComparing(Person::getGid,?(a,?b)?->?b.compareTo(a))???
????????.thenComparingInt(Person::getAge));?
????????personList.stream().forEach(System.out::println);
}
public?static?List<Person>?getTestList()?{?
????return?Lists.newArrayList(new?Person("dai",?"301",?10),?
????new?Person("dai",?"303",?10),???new?Person("dai",?"303",?8),?
????new?Person("dai",?"303",?6),?new?Person("dai",?"303",?11),??
????new?Person("dai",?"302",?9),?new?Person("zhang",?"302",?9),
?????new?Person("zhang",?"301",?9),???new?Person("Li",?"301",?8));
}
//?输出结果
//?Person?[name=dai,?gid=303,?age=6]//?Person?[name=dai,?gid=303,?age=8]
//?Person?[name=dai,?gid=303,?age=10]//?Person?[name=dai,?gid=303,?age=11]
//?Person?[name=dai,?gid=302,?age=9]//?Person?[name=dai,?gid=301,?age=10]
//?Person?[name=Li,?gid=301,?age=8]//?Person?[name=zhang,?gid=302,?age=9]
//?Person?[name=zhang,?gid=301,?age=9]
????处理字符串
两个新的方法可在字符串类上使用: join和chars。第一个方法使用指定的分隔符,将任何数量的字符串连接为一个字符串。
String.join(":",?"foobar",?"foo",?"bar");//?=>?foobar:foo:bar
第二个方法chars从字符串所有字符创建数据流,所以你可以在这些字符上使用流式操作。
"foobar:foo:bar".chars().distinct().mapToObj(c?->?String.valueOf((char)c)).sorted().collect(Collectors.joining());
//?=>?:abfor
不仅仅是字符串,正则表达式模式串也能受益于数据流。我们可以分割任何模式串,并创建数据流来处理它们,而不是将字符串分割为单个字符的数据流,像下面这样:
Pattern.compile(":")????
.splitAsStream("foobar:foo:bar")????
.filter(s?->?s.contains("bar"))????
.sorted()????
.collect(Collectors.joining(":"));
//?=>?bar:foobar
此外,正则模式串可以转换为谓词。这些谓词可以像下面那样用于过滤字符串流:
Pattern?pattern?=?Pattern.compile(".*@gmail\\.com");
Stream.of("bob@gmail.com",?"alice@hotmail.com")????
.filter(pattern.asPredicate())???
?.count();
//?=>?1
上面的模式串接受任何以@gmail.com结尾的字符串,并且之后用作Java8的Predicate来过滤电子邮件地址流。
Local Cache实现
public?class?TestLocalCache?{?
private?static?Concurrent
HashMap<Integer,?Long>?cache?=?new?ConcurrentHashMap<>();?
????static?long?fibonacci(int?i)?{??
????????if?(i?==?0)???return?i;??
????????if?(i?==?1)???return?1;??
????????return?cache.computeIfAbsent(i,?(key)?->?{???
????????System.out.println("Slow?calculation?of?"?+?key);???
????????return?fibonacci(i?-?2)?+?fibonacci(i?-?1);??});?
????}??
????public?static?void?main(String[]?args)?{?
?????????//?warm?up??
????????for?(int?i?=?0;?i?<?101;?i++)?????????
????????System.out.println(?????????????"f("?+?i?+?")?=?"?+?fibonacci(i));????
????????//?read?->?cal??
????????long?current?=?System.currentTimeMillis();??
????????System.out.println(fibonacci(100));??
????????System.out.println(System.currentTimeMillis()-current);?
????}
}
集合--》取元素的一个属性--》去重---》组装成List--》返回
List<LikeDO>?likeDOs=new?ArrayList<LikeDO>();
List<Long>?likeTidList?=?likeDOs.stream().map(LikeDO::getTid)????????????????
.distinct().collect(Collectors.toList());
集合--》按表达式过滤--》遍历、每个元系处理--》放入预先定义的集合中
Map<String,?StkProduct>?newStockName2Product?=?Maps.newConcurrentMap();? ? ? ??
????????stockProducts.stream().filter(stkProduct?->?stkProduct.enabled).forEach(stkProduct?->?{?
????????????????????????String?newName?=?BCConvert.bj2qj(StringUtils.replace(stkProduct.name,?"?",?"")); ????????????????????????newStockName2Product.put(newName,?stkProduct);
});
?Set<String>?qjStockNames;?qjStockNames.stream().filter(name?->?!acAutomaton.getKey2link().containsKey(name)).forEach(name?->?{????????????
????????????String?value?=?"";????????????
????????????StkProduct?stkProduct?=?stockNameQj2Product.get(name);???????????
?????????????if?(stkProduct?!=?null)?{????????????????value?=?stkProduct.name;????????????}???????????
?????????????acAutomaton.getKey2link().put(name,?value);???????
});
? ??集合--》map
List<ImageModel>?imageModelList?=?null;Map<Long,?String>?imagesMap?=?null;
imagesMap?=?imageModelList.stream().collect(Collectors.toMap(ImageModel::getAid,?o?->?IMAGE_ADDRESS_PREFIX?+?o.getUrl()));???????????????????????????
Map<String,?String>?kvMap?=?postDetailCacheList.stream().collect(Collectors.toMap((detailCache)?->????????????????getBbsSimplePostKey(detailCache.getTid()),?JSON::toJSONString));
Map<Long, Long> pidToTid;List<String>?pidKeyList?=?pidToTid.entrySet().stream().map((o)?->?getKeyBbsReplyPid(o.getValue(),?o.getKey())).collect(Collectors.toList());
DO模型---》Model模型
List<AdDO>?adDOList;adDOList.stream().map(adDo?->?convertAdModel(adDo))????????????????
.collect(Collectors.toList());
phones 是一个List<String>,将相同的元素分组、归类
List<String>?phones=new?ArrayList<String>();????????
phones.add("a");???????
?phones.add("b");????????
phones.add("a");????????
phones.add("a");????????
phones.add("c");????????
phones.add("b");???????
?Map<String,?List<String>>?phoneClassify?=?phones.stream().collect(Collectors.groupingBy(item?->?item));????????
System.out.println(phoneClassify);
返回结果:?{a=[a,?a,?a],?b=[b,?b],?c=[c]}
Optional类深度解析
Optional类包含的方法
of
为非null的值创建一个Optional。
of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。
//调用工厂方法创建Optional实例
Optional<String>?name?=?Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String>?someNull?=?Optional.of(null);
ofNullable
为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。
ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。示例如下:
//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional?empty?=?Optional.ofNullable(null);
isPresent
非常容易理解:如果值存在返回true,否则返回false。
类似下面的代码:
//isPresent方法用来检查Optional实例中是否包含值
if?(name.isPresent())?{??
????//在Optional实例内调用get()返回已存在的值??
????System.out.println(name.get());//输出Sanaulla
}
get
如果Optional有值则将其返回,否则抛出NoSuchElementException。
上面的示例中,get方法用来得到Optional实例中的值。下面我们看一个抛出NoSuchElementException的例子:
//执行下面的代码会输出:?No?value?present?
try?{??
????//在空的Optional实例上调用get(),抛出NoSuchElementException?
????System.out.println(empty.get());
} catch?(NoSuchElementException?ex) {??
????System.out.println(ex.getMessage());
}
ifPresent
如果Optional实例有值则为其调用consumer,否则不做处理
要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。
如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:
//ifPresent方法接受lambda表达式作为参数 //lambda表达式对Optional的值调用consumer进行处理。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? name.ifPresent((value)?->?{??System.out.println("The?length?of?the?value?is:?"?+?value.length());});
orElse
如果有值则将其返回,否则返回指定的其它值。
如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。示例如下:
//如果值不为null,orElse方法返回Optional实例的值。//如果为null,返回传入的消息。//输出: There?is?no?value?present!
System.out.println(empty.orElse("There?is?no?value?present!"));//输出:?Sanaulla
System.out.println(name.orElse("There?is?some?value!"));
orElseGet
orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:
//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
//orElseGet可以接受一个lambda表达式生成默认值。
//输出:?Default?Value
System.out.println(empty.orElseGet(()?->?"Default?Value"));
//输出:?Sanaulla
System.out.println(name.orElseGet(()?->?"Default?Value"));
orElseThrow
如果有值则将其返回,否则抛出supplier接口创建的异常。
在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。示例如下:
try?{??
????????//orElseThrow与orElse方法类似。与返回默认值不同,?
?????????//orElseThrow会抛出lambda表达式或方法生成的异常???
????????empty.orElseThrow(ValueAbsentException::new);
}?catch?(Throwable?ex)?{?
?????????//输出:?No?value?present?in?the?Optional?instance??
????????System.out.println(ex.getMessage());
}
ValueAbsentException定义如下:
class?ValueAbsentException?extends?Throwable?{?
?????public?ValueAbsentException()?{????super();? }??
????public?ValueAbsentException(String?msg)?{????super(msg);??}??
????@Override??
????public?String?getMessage()?{???
?????????return?"No?value?present?in?the?Optional?instance";?
?????}
}
map
map方法文档说明如下:
如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。
map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。如果你不熟悉Function接口,可以参考我的这篇博客。map方法示例如下:
//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional<String>?upperName?=?name.map((value)?->?value.toUpperCase());
System.out.println(upperName.orElse("No?value?found"));
flatMap
如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。
flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。
参照map函数,使用flatMap重写的示例如下:
//flatMap与map(Function)非常类似,区别在于传入方法的lambda表达式的返回类型。
//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。
?//但flatMap方法中的lambda表达式返回值必须是Optionl实例。?
upperName?=?name.flatMap((value)?->?Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No?value?found"));//输出SANAULLA
filter
filter个方法通过传入限定条件对Optional实例的值进行过滤。文档描述如下:
如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。
读到这里,可能你已经知道如何为filter方法传入一段代码。是的,这里可以传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。如果你不熟悉Predicate接口,可以参考这篇文章。
现在我来看看filter的各种用法,下面的示例介绍了满足限定条件和不满足两种情况:
//filter方法检查给定的Option值是否满足某些条件。
//如果满足则返回同一个Option实例,否则返回空Optional。Optional<String>?longName?=?name.filter((value)?->?value.length()?>?6);
System.out.println(longName.orElse("The?name?is?less?than?6?characters"));
//输出Sanaulla//另一个例子是Optional值不满足filter指定的条件。
Optional<String>?anotherName?=?Optional.of("Sana");
Optional<String>?shortName?=?anotherName.filter((value)?->?value.length()?>?6);
//输出:?name长度不足6字符System.out.println(shortName.orElse("The?name?is?less?than?6?characters"));
一些例子
????一个综合例子
public?class?OptionalDemo?{??
public?static?void?main(String[]?args)?{????
????//创建Optional实例,也可以通过方法返回值得到。????
????Optional<String>?name?=?Optional.of("Sanaulla");????
????//创建没有值的Optional实例,例如值为'null'????
????Optional?empty?=?Optional.ofNullable(null);????
????//isPresent方法用来检查Optional实例是否有值。????
????if?(name.isPresent())?{??????
????????????//调用get()返回Optional值。??????
????????????System.out.println(name.get());???
?????}????try?{??????
????????????//在Optional实例上调用get()抛出NoSuchElementException。??????
????????????System.out.println(empty.get());???
? ? ? ? } catch?(NoSuchElementException?ex) {? ? ??
????????????????System.out.println(ex.getMessage());????
}???
?//ifPresent方法接受lambda表达式参数。????
//如果Optional值不为空,lambda表达式会处理并在其上执行操作。????
name.ifPresent((value)?->?{??????
????????System.out.println("The?length?of?the?value?is:?"?+?value.length());???
?});????
//如果有值orElse方法会返回Optional实例,否则返回传入的错误信息。????
????System.out.println(empty.orElse("There?is?no?value?present!"));????
????System.out.println(name.orElse("There?is?some?value!"));???
?????//orElseGet与orElse类似,区别在于传入的默认值。??
??????//orElseGet接受lambda表达式生成默认值。????
????System.out.println(empty.orElseGet(()?->?"Default?Value"));????
????System.out.println(name.orElseGet(()?->?"Default?Value"));???
?try?{??????
????//orElseThrow与orElse方法类似,区别在于返回值。??????
????//orElseThrow抛出由传入的lambda表达式/方法生成异常。?????
?????empty.orElseThrow(ValueAbsentException::new);????
}?catch?(Throwable?ex)?{??????
????System.out.println(ex.getMessage());????
}???
?????//map方法通过传入的lambda表达式修改Optonal实例默认值。?????
????//lambda表达式返回值会包装为Optional实例。????
????Optional<String>?upperName?=?name.map((value)?->?value.toUpperCase());????
????System.out.println(upperName.orElse("No?value?found"));????
????//flatMap与map(Funtion)非常相似,区别在于lambda表达式的返回值。????
????//map方法的lambda表达式返回值可以是任何类型,但是返回值会包装成Optional实例。???
?????//但是flatMap方法的lambda返回值总是Optional类型。????
????upperName?=?name.flatMap((value)?->?Optional.of(value.toUpperCase()));???
?????System.out.println(upperName.orElse("No?value?found"));???
?????//filter方法检查Optiona值是否满足给定条件。???
?????//如果满足返回Optional实例值,否则返回空Optional。????
????Optional<String>?longName?=?name.filter((value)?->?value.length()?>?6);????
????System.out.println(longName.orElse("The?name?is?less?than?6?characters"));????
????//另一个示例,Optional值不满足给定条件。???
?????Optional<String>?anotherName?=?Optional.of("Sana");????
????Optional<String>?shortName?=?anotherName.filter((value)?->?value.length()?>?6);????
????System.out.println(shortName.orElse("The?name?is?less?than?6?characters"));??
}
}
上述代码输出如下:
Sanaulla
No?value?present
The?length?of?the?value?is:?8
There?is?no?value?present!
Sanaulla
Default?Value
Sanaulla
No?value?present?in?the?Optional?instance
SANAULLA
SANAULLA
Sanaulla
The?name?is?less?than?6?characters
在 Java 8 中提高 Null 的安全性
假设我们有一个像这样的类层次结构:
class?Outer?{????
????????Nested?nested;???
?????????Nested?getNested()?{????????
????????????????eturn?nested;????
????????}
}
class?Nested?{???
?????????Inner?inner;???
?????????Inner?getInner()?{????????
????????????return?inner;????
????????}
}
class?Inner?{????
????????String?foo;???
?????????String?getFoo()?{???????
?????????return?foo;????
????????}
}
解决这种结构的深层嵌套路径是有点麻烦的。我们必须编写一堆 null 检查来确保不会导致一个 NullPointerException:
Outer?outer?=?new?Outer();
if?(outer?!=?null?&&?outer.nested?!=?null?&&?outer.nested.inner?!=?null)?
{????
????System.out.println(outer.nested.inner.foo);
}
我们可以通过利用 Java 8 的 Optional 类型来摆脱所有这些 null 检查。map 方法接收一个 Function 类型的 lambda 表达式,并自动将每个 function 的结果包装成一个 Optional 对象。这使我们能够在一行中进行多个 map 操作。Null 检查是在底层自动处理的。
Optional.of(new?Outer())????
????.map(Outer::getNested)????
????.map(Nested::getInner)???
?????.map(Inner::getFoo)????
????.ifPresent(System.out::println);
还有一种实现相同作用的方式就是通过利用一个 supplier 函数来解决嵌套路径的问题:
Outer?obj?=?new?Outer();resolve(()?->?obj.getNested().getInner().getFoo());????
.ifPresent(System.out::println);
调用 obj.getNested().getInner().getFoo()) 可能会抛出一个 NullPointerException 异常。在这种情况下,该异常将会被捕获,而该方法会返回 Optional.empty()。
public?static?<T>?Optional<T>?resolve(Supplier<T>?resolver)?{???
try?{???????
?????T?result?=?resolver.get();???????
?????????????return?Optional.ofNullable(result);???
?????}????
????catch?(NullPointerException?e)?{???????
?????????????return?Optional.empty();???
?????}
}
请记住,这两个解决方案可能没有传统 null 检查那么高的性能。不过在大多数情况下不会有太大问题。
翻译: ImportNew.com - 高俊阳 ?译文链接: http://www.importnew.com/6675.html
default方法解析
什么是默认方法,为什么要有默认方法
先上例子
一个接口A,Clazz类实现了接口A。
public?interface?A?{????
????????default?void?foo(){??????
?????????System.out.println("Calling?A.foo()");????
????????}
}
public?class?Clazz?implements?A?{???
?????public?static?void?main(String[]?args){???????
????????????Clazz?clazz?=?new?Clazz();???????clazz.foo();//调用A.foo()???
?????????}
}
代码是可以编译的,即使Clazz类并没有实现foo()方法。在接口A中提供了foo()方法的默认实现。
什么是默认方法
简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。
为什么出现默认方法
为什么要有这个特性? 首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
java 8抽象类与接口对比
这一个功能特性出来后,很多同学都反应了,java 8的接口都有实现方法了,跟抽象类还有什么区别? 其实还是有的,请看下表对比。
多重继承的冲突
由于同一个方法可以从不同接口引入,自然而然的会有冲突的现象,默认方法判断冲突的规则如下:
1.一个声明在类里面的方法优先于任何默认方法(classes always win)
2.否则,则会优先选取路径最短的。
举例子
????Case 1
????public?interface?A{?default?void?aa()?{??
????????System.out.println("A's?aa");?
????}
}
????public?interface?B{?default?void?aa()?{??
????????System.out.println("B's?aa");?
????}
}
public?static?class?D?implements?A,B{?}
报错 Duplicate default methods named aa with the parameters () and () are inherited from the types DocApplication.B and DocApplication.A
如果一定要这么写呢,同时实现A,B并且使用A中aa? 可以这么写:
public?static?class?D?implements?A,B{????
????@Override???
?????public?void?aa(){????????
????????????A.super.aa();????
????}
}
Case 2
public?interface?A{?default?void?aa()?{??
????????System.out.println("A's?aa");?
????????}
}
public?interface?B{?default?void?aa()?{??
????System.out.println("B's?aa");?
????}
}
public?interface?C?extends?A,?B{?default?void?aa()?{??
????System.out.println("C's?aa");?
????}
}
public?static?class?D?implements?A,B,C{?}
输出 C's aa
Case 3
public?interface?A{?default?void?aa()?{??
????System.out.println("A's?aa");?
????}
}
public?interface?C?extends?A{?default?void?aa()?{??
????System.out.println("C's?aa");
?????}
}
public?static?class?D?implements?C{?}
输出 C's aa
????通过Case1-3可以知道它是找唯一的最短路径的default,如果是多个那么报错。
????????Case 4 如果想调用A的默认函数,则用到新语法X.super.m(…),下面修改C类,实现A接口,重写一个hello方法,如下所示:
public?interface?A{?
????default?void?aa()?{??
????????System.out.println("A's?aa");
?????}
}
public?class?X?implements?A{????
????????@Override????
????????public?void?aa(){????????
????????????A.super.aa();???
?????????}
}
输出: A's aa
????Case 5
public?interface?A{?default?void?aa()?{??
????System.out.println("A's?aa");?
????}
}
public?interface?B{?
????default?void?aa()?{??
????????????System.out.println("B's?aa");?
????}
}
public?interface?C?extends?A,B{?
default?void?aa()?{?
?????System.out.println("C's?aa");
?????}
}
public?static?class?D?implements?C{?
????????@Override????public?void?aa(){????????
????????C.super.aa();????
????}
}
输出 C's aa 可见C.super表示的是C接口,同时D无法访问A,B的aa
通过Case 5也可以看出,C虽然有同一个两个最短路径的aa, 但是它自己有一个更高优先级的aa,所以不会报错; case 6 会报错
????Case 6
public?interface?A{?default?void?aa()?{??
????System.out.println("A's?aa");?
????}
}
public?interface?B{?default?void?aa()?{??
????System.out.println("B's?aa");?
????}
}
public?interface?C?extends?A,B{}
总结
默认方法给予我们修改接口而不破坏原来的实现类的结构提供了便利,目前java 8的集合框架已经大量使用了默认方法来改进了,当我们最终开始使用Java 8的lambdas表达式时,提供给我们一个平滑的过渡体验。也许将来我们会在API设计中看到更多的默认方法的应用。