JDK1.8新特性简单介绍
2017-12-04 16:47
519 查看
Java8为容器新增一些有用的方法,这些方法有些是为完善原有功能,有些是为引入函数式编程(Lambda表达式),学习和使用这些方法有助于我们写出更加简洁有效的代码(性能上先不说
).本文分别以ArrayList和HashMap为例,讲解Java8集合框架(Java Collections Framework)中新加入方法的使用.在讲解这些的时候,顺带参杂一些jdk1.8新增加的其它特性
为引入Lambda表达式,Java8新增了
首先回顾一下Java集合框架的接口继承结构:
上图中绿色标注的接口类,表示在Java8中加入了新的接口方法,当然由于继承关系,他们相应的子类也都会继承这些新方法。下表详细列举了这些方法。
这些新加入的方法大部分要用到
需求:假设有一个字符串列表,需要打印出其中所有长度大于3的字符串.
Java7及以前我们可以用增强的for循环实现:
现在使用
上述代码调用
上述代码给
对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:
需求:假设有一个字符串列表,需要删除其中所有长度大于3的字符串。
我们知道如果需要在迭代过程冲对容器进行删除操作必须使用迭代器,否则会抛出
现在使用
上述代码使用
使用Lambda表达式不需要记忆
需求:假设有一个字符串列表,将其中所有长度大于3的元素转换成大写,其余元素不变。
Java7及之前似乎没有优雅的办法:
使用
上述代码调用
需求:假设有一个字符串列表,按照字符串长度增序对元素排序。
由于Java7以及之前
现在可以直接使用
// List.sort()方法结合Lambda表达式
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.sort((str1, str2) -> str1.length()-str2.length());
spliterator()
方法签名为
可通过(多次)调用
需求:假设有一个数字到对应英文单词的Map,请输出Map中的所有映射关系.
Java7以及之前经典的代码如下:// Java7以及之前迭代Map
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for(Map.Entry<Integer, String> entry : map.entrySet()){
System.out.println(entry.getKey() + "=" + entry.getValue());
}
使用
上述代码调用
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((k, v) -> System.out.println(k + "=" + v));
}
getOrDefault()
该方法跟Lambda表达式没关系,但是很有用。方法签名为
需求;假设有一个数字到对应英文单词的Map,输出4对应的英文单词,如果不存在则输出NoValue
// 查询Map中指定的值,不存在时使用默认值
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
// Java7以及之前做法
if(map.containsKey(4)){ // 1
System.out.println(map.get(4));
}else{
System.out.println("NoValue");
}
// Java8使用Map.getOrDefault()
System.out.println(map.getOrDefault(4, "NoValue")); // 2
putIfAbsent()
该方法跟Lambda表达式没关系,但是很有用。方法签名为
需求:假设有一个数字到对应英文单词的Map,请将原来映射关系中的单词都转换成大写.
Java7以及之前经典的代码如下:
使用
上述代码调用
简洁到让人难以置信.
如果
否则执行
参数中
要实现上述
使用
这个函数的功能跟如下代码是等效的:
函数接口虽然很多,但绝大多数时候我们根本不需要知道它们的名字,书写Lambda表达式时类型推断帮我们做了一切
附加:
1:Deafult Interface
上述中,我们讲解了Stream以及集合中的一些用法如:forEach,我们来看下他的源码:
接口中存在实现方法,对的
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,默认方法在子类上可以直接使用
2:Stream API
java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。
首先看看Stream是怎么用,首先创建实例代码的用到的数据List:
Collection.stream() 或者并行计算- Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:
1)Filter 过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
2)Sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
3)Map 映射
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
4)Match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。
5)Count 计数
计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。
6)Reduce 规约
这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的
3:[b]Annotation 注解
[/b]
在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
首先定义一个包装类Hints注解用来放置一组具体的Hint注解:
例 1: 使用包装类当容器来存多个注解(老方法)
第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:
另外Java 8的注解还增加到两种新的target上了:
).本文分别以ArrayList和HashMap为例,讲解Java8集合框架(Java Collections Framework)中新加入方法的使用.在讲解这些的时候,顺带参杂一些jdk1.8新增加的其它特性
前言
我们先从最熟悉的Java集合框架(Java Collections Framework, JCF)开始说起。为引入Lambda表达式,Java8新增了
java.util.funcion包,里面包含常用的函数接口,这是Lambda表达式的基础,Java集合框架也新增部分接口,以便与Lambda表达式对接。
首先回顾一下Java集合框架的接口继承结构:
上图中绿色标注的接口类,表示在Java8中加入了新的接口方法,当然由于继承关系,他们相应的子类也都会继承这些新方法。下表详细列举了这些方法。
接口名 | Java8新加入的方法 |
---|---|
Collection | removeIf() spliterator() stream() parallelStream() forEach() |
List | replaceAll() sort() |
Map | getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge() |
java.util.function包下的接口,这意味着这些方法大部分都跟Lambda表达式相关。我们将逐一学习这些方法。
Collection中的新方法
如上所示,接口Collection和
List新加入了一些方法,我们以是
List的子类
ArrayList为例来说明。了解Java7
ArrayList实现原理,将有助于理解下文。
forEach()
该方法的签名为void forEach(Consumer<? super E> action),作用是对容器中的每个元素执行
action指定的动作,其中
Consumer是个函数接口,里面只有一个待实现方法
void accept(T t)(后面我们会看到,这个方法叫什么根本不重要,你甚至不需要记忆它的名字)。
需求:假设有一个字符串列表,需要打印出其中所有长度大于3的字符串.
Java7及以前我们可以用增强的for循环实现:
// 使用曾强for循环迭代 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); for(String str : list){ if(str.length()>3) System.out.println(str); }
现在使用
forEach()方法结合匿名内部类,可以这样实现:
// 使用forEach()结合匿名内部类迭代 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.forEach(new Consumer<String>(){ @Override public void accept(String str){ if(str.length()>3) System.out.println(str); } });
上述代码调用
forEach()方法,并使用匿名内部类实现
Comsumer接口。到目前为止我们没看到这种设计有什么好处,但是不要忘记Lambda表达式,使用Lambda表达式实现如下:
// 使用forEach()结合Lambda表达式迭代 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.forEach( str -> { if(str.length()>3) System.out.println(str); });
上述代码给
forEach()方法传入一个Lambda表达式,我们不需要知道
accept()方法,也不需要知道
Consumer接口,类型推导帮我们做了一切。
对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:
//排序 Collections.sort(names, (a, b) -> b.compareTo(a)); //打印 Collections.forEach(System.out::println);
removeIf()
该方法签名为boolean removeIf(Predicate<? super E> filter),作用是删除容器中所有满足
filter指定条件的元素,其中
Predicate是一个函数接口,里面只有一个待实现方法
boolean test(T t),同样的这个方法的名字根本不重要,因为用的时候不需要书写这个名字。
需求:假设有一个字符串列表,需要删除其中所有长度大于3的字符串。
我们知道如果需要在迭代过程冲对容器进行删除操作必须使用迭代器,否则会抛出
ConcurrentModificationException,所以上述任务传统的写法是:
// 使用迭代器删除列表元素 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); Iterator<String> it = list.iterator(); while(it.hasNext()){ if(it.next().length()>3) // 删除长度大于3的元素 it.remove(); }
现在使用
removeIf()方法结合匿名内部类,我们可是这样实现:
// 使用removeIf()结合匿名名内部类实现 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.removeIf(new Predicate<String>(){ // 删除长度大于3的元素 @Override public boolean test(String str){ return str.length()>3; } });
上述代码使用
removeIf()方法,并使用匿名内部类实现
Precicate接口。相信你已经想到用Lambda表达式该怎么写了:
// 使用removeIf()结合Lambda表达式实现 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.removeIf(str -> str.length()>3); // 删除长度大于3的元素
使用Lambda表达式不需要记忆
Predicate接口名,也不需要记忆
test()方法名,只需要知道此处需要一个返回布尔类型的Lambda表达式就行了。
replaceAll()
该方法签名为void replaceAll(UnaryOperator<E> operator),作用是对每个元素执行
operator指定的操作,并用操作结果来替换原来的元素。其中
UnaryOperator是一个函数接口,里面只有一个待实现函数
T apply(T t)。
需求:假设有一个字符串列表,将其中所有长度大于3的元素转换成大写,其余元素不变。
Java7及之前似乎没有优雅的办法:
// 使用下标实现元素替换 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); for(int i=0; i<list.size(); i++){ String str = list.get(i); if(str.length()>3) list.set(i, str.toUpperCase()); }
使用
replaceAll()方法结合匿名内部类可以实现如下:
// 使用匿名内部类实现 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.replaceAll(new UnaryOperator<String>(){ @Override public String apply(String str){ if(str.length()>3) return str.toUpperCase(); return str; } });
上述代码调用
replaceAll()方法,并使用匿名内部类实现
UnaryOperator接口。我们知道可以用更为简洁的Lambda表达式实现:
// 使用Lambda表达式实现 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.replaceAll(str -> { if(str.length()>3) return str.toUpperCase(); return str; });
sort()
该方法定义在List接口中,方法签名为
void sort(Comparator<? super E> c),该方法根据
c指定的比较规则对容器元素进行排序。
Comparator接口我们并不陌生,其中有一个方法
int compare(T o1, T o2)需要实现,显然该接口是个函数接口。
需求:假设有一个字符串列表,按照字符串长度增序对元素排序。
由于Java7以及之前
sort()方法在
Collections工具类中,所以代码要这样写:
// Collections.sort()方法 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); Collections.sort(list, new Comparator<String>(){ @Override public int compare(String str1, String str2){ return str1.length()-str2.length(); } });
现在可以直接使用
List.sort()方法,结合Lambda表达式,可以这样写:
// List.sort()方法结合Lambda表达式
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.sort((str1, str2) -> str1.length()-str2.length());
spliterator()
方法签名为
Spliterator<E> spliterator(),该方法返回容器的可拆分迭代器。从名字来看该方法跟
iterator()方法有点像,我们知道
Iterator是用来迭代容器的,
Spliterator也有类似作用,但二者有如下不同:
Spliterator既可以像
Iterator那样逐个迭代,也可以批量迭代。批量迭代可以降低迭代的开销。
Spliterator是可拆分的,一个
Spliterator可以通过调用
Spliterator<T> trySplit()方法来尝试分成两个。一个是
this,另一个是新返回的那个,这两个迭代器代表的元素没有重叠。
可通过(多次)调用
Spliterator.trySplit()方法来分解负载,以便多线程处理。
stream()和parallelStream()
stream()和
parallelStream()分别返回该容器的
Stream视图表示,不同之处在于
parallelStream()返回并行的
Stream。
Stream是Java函数式编程的核心类,我们会在后面章节中学习。
Map中的新方法
相比Collection,
Map中加入了更多的方法,我们以
HashMap为例来逐一探秘。了解Java7
HashMap实现原理,将有助于理解下文。
forEach()
该方法签名为void forEach(BiConsumer<? super K,? super V> action),作用是对
Map中的每个映射执行
action指定的操作,其中
BiConsumer是一个函数接口,里面有一个待实现方法
void accept(T t, U u)。
BinConsumer接口名字和
accept()方法名字都不重要,请不要记忆他们。
需求:假设有一个数字到对应英文单词的Map,请输出Map中的所有映射关系.
Java7以及之前经典的代码如下:// Java7以及之前迭代Map
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for(Map.Entry<Integer, String> entry : map.entrySet()){
System.out.println(entry.getKey() + "=" + entry.getValue());
}
使用
Map.forEach()方法,结合匿名内部类,代码如下:
// 使用forEach()结合匿名内部类迭代Map HashMap<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.forEach(new BiConsumer<Integer, String>(){ @Override public void accept(Integer k, String v){ System.out.println(k + "=" + v); } });
上述代码调用
forEach()方法,并使用匿名内部类实现
BiConsumer接口。当然,实际场景中没人使用匿名内部类写法,因为有Lambda表达式:
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((k, v) -> System.out.println(k + "=" + v));
}
getOrDefault()
该方法跟Lambda表达式没关系,但是很有用。方法签名为
V getOrDefault(Object key, V defaultValue),作用是按照给定的
key查询
Map中对应的
value,如果没有找到则返回
defaultValue。使用该方法程序员可以省去查询指定键值是否存在的麻烦.
需求;假设有一个数字到对应英文单词的Map,输出4对应的英文单词,如果不存在则输出NoValue
// 查询Map中指定的值,不存在时使用默认值
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
// Java7以及之前做法
if(map.containsKey(4)){ // 1
System.out.println(map.get(4));
}else{
System.out.println("NoValue");
}
// Java8使用Map.getOrDefault()
System.out.println(map.getOrDefault(4, "NoValue")); // 2
putIfAbsent()
该方法跟Lambda表达式没关系,但是很有用。方法签名为
V putIfAbsent(K key, V value),作用是只有在不存在
key值的映射或映射值为
null时,才将
value指定的值放入到
Map中,否则不对
Map做更改.该方法将条件判断和赋值合二为一,使用起来更加方便.
remove()
我们都知道Map中有一个
remove(Object key)方法,来根据指定
key值删除
Map中的映射关系;Java8新增了
remove(Object key, Object value)方法,只有在当前
Map中
key正好映射到
value时才删除该映射,否则什么也不做.
replace()
在Java7及以前,要想替换Map中的映射关系可通过
put(K key, V value)方法实现,该方法总是会用新值替换原来的值.为了更精确的控制替换行为,Java8在
Map中加入了两个
replace()方法,分别如下:
replace(K key, V value),只有在当前
Map中
key的映射存在时才用
value去替换原来的值,否则什么也不做.
replace(K key, V oldValue, V newValue),只有在当前
Map中
key的映射存在且等于
oldValue时才用
newValue去替换原来的值,否则什么也不做.
replaceAll()
该方法签名为replaceAll(BiFunction<? super K,? super V,? extends V> function),作用是对
Map中的每个映射执行
function指定的操作,并用
function的执行结果替换原来的
value,其中
BiFunction是一个函数接口,里面有一个待实现方法
R apply(T t, U u).不要被如此多的函数接口吓到,因为使用的时候根本不需要知道他们的名字.
需求:假设有一个数字到对应英文单词的Map,请将原来映射关系中的单词都转换成大写.
Java7以及之前经典的代码如下:
// Java7以及之前替换所有Map中所有映射关系 HashMap<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for(Map.Entry<Integer, String> entry : map.entrySet()){ entry.setValue(entry.getValue().toUpperCase()); }
使用
replaceAll()方法结合匿名内部类,实现如下:
// 使用replaceAll()结合匿名内部类实现 HashMap<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.replaceAll(new BiFunction<Integer, String, String>(){ @Override public String apply(Integer k, String v){ return v.toUpperCase(); } });
上述代码调用
replaceAll()方法,并使用匿名内部类实现
BiFunction接口。更进一步的,使用Lambda表达式实现如下:
// 使用replaceAll()结合Lambda表达式实现 HashMap<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.replaceAll((k, v) -> v.toUpperCase());
简洁到让人难以置信.
merge()
该方法签名为merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction),作用是:
如果
Map中
key对应的映射不存在或者为
null,则将
value(不能是
null)关联到
key上;
否则执行
remappingFunction,如果执行结果非
null则用该结果跟
key关联,否则在
Map中删除
key的映射.
参数中
BiFunction函数接口前面已经介绍过,里面有一个待实现方法
R apply(T t, U u).
merge()方法虽然语义有些复杂,但该方法的用方式很明确,一个比较常见的场景是将新的错误信息拼接到原来的信息上,比如:
map.merge(key, newMsg, (v1, v2) -> v1+v2);
compute()
该方法签名为compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用是把
remappingFunction的计算结果关联到
key上,如果计算结果为
null,则在
Map中删除
key的映射.
要实现上述
merge()方法中错误信息拼接的例子,使用
compute()代码如下:
map.compute(key, (k,v) -> v==null ? newMsg : v.concat(newMsg));
computeIfAbsent()
该方法签名为V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction),作用是:只有在当前
Map中不存在
key值的映射或映射值为
null时,才调用
mappingFunction,并在
mappingFunction执行结果非
null时,将结果跟
key关联.
Function是一个函数接口,里面有一个待实现方法
R apply(T t).
computeIfAbsent()常用来对
Map的某个
key值建立初始化映射.比如我们要实现一个多值映射,
Map的定义可能是
Map<K,Set<V>>,要向
Map中放入新值,可通过如下代码实现:
Map<Integer, Set<String>> map = new HashMap<>(); // Java7及以前的实现方式 if(map.containsKey(1)){ map.get(1).add("one"); }else{ Set<String> valueSet = new HashSet<String>(); valueSet.add("one"); map.put(1, valueSet); } // Java8的实现方式 map.computeIfAbsent(1, v -> new HashSet<String>()).add("yi");
使用
computeIfAbsent()将条件判断和添加操作合二为一,使代码更加简洁.
computeIfPresent()
该方法签名为V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用跟
computeIfAbsent()相反,即,只有在当前
Map中存在
key值的映射且非
null时,才调用
remappingFunction,如果
remappingFunction执行结果为
null,则删除
key的映射,否则使用该结果替换
key原来的映射.
这个函数的功能跟如下代码是等效的:
// Java7及以前跟computeIfPresent()等效的代码 if (map.get(key) != null) { V oldValue = map.get(key); V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) map.put(key, newValue); else map.remove(key); return newValue; } return null;
总结
Java8为容器新增一些有用的方法,这些方法有些是为完善原有功能,有些是为引入函数式编程,学习和使用这些方法有助于我们写出更加简洁有效的代码.函数接口虽然很多,但绝大多数时候我们根本不需要知道它们的名字,书写Lambda表达式时类型推断帮我们做了一切
附加:
1:Deafult Interface
上述中,我们讲解了Stream以及集合中的一些用法如:forEach,我们来看下他的源码:
package java.lang; import java.util.Iterator; import java.util.Objects; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; public interface Iterable<T> { Iterator<T> iterator(); default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } default Spliterator<T> spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } }
接口中存在实现方法,对的
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,默认方法在子类上可以直接使用
2:Stream API
java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。
首先看看Stream是怎么用,首先创建实例代码的用到的数据List:
List<String> stringCollection = new ArrayList<>(); stringCollection.add("ddd2"); stringCollection.add("aaa2"); stringCollection.add("bbb1"); stringCollection.add("aaa1"); stringCollection.add("bbb3"); stringCollection.add("ccc"); stringCollection.add("bbb2"); stringCollection.add("ddd1");Java 8扩展了集合类,可以通过串行计算-
Collection.stream() 或者并行计算- Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:
1)Filter 过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa2", "aaa1"
2)Sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
stringCollection .stream() .filter((s) -> s.startsWith("a")) .sorted() .forEach(System.out::println); // aaa1 aaa2
3)Map 映射
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
stringCollection .stream() .filter((s) -> s.startsWith("a")) .map(String::toUpperCase) .sorted() .forEach(System.out::println); AAA1 AAA2
4)Match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。
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
5)Count 计数
计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。
long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3
6)Reduce 规约
这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的
Optional<String> reduced = stringCollection .stream() .sorted() .reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println); // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
3:[b]Annotation 注解
[/b]
在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
首先定义一个包装类Hints注解用来放置一组具体的Hint注解:
@interface Hints { Hint[] value(); } @Repeatable(Hints.class) @interface Hint { String value(); }Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。
例 1: 使用包装类当容器来存多个注解(老方法)
@Hints({@Hint("hint1"), @Hint("hint2")}) class Person {}例 2:使用多重注解(新方法)
@Hint("hint1") @Hint("hint2") class Person {}
第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:
Hint hint = Person.class.getAnnotation(Hint.class); System.out.println(hint); // null Hints hints1 = Person.class.getAnnotation(Hints.class); System.out.println(hints1.value().length); // 2 Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class); System.out.println(hints2.length); // 2即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。
另外Java 8的注解还增加到两种新的target上了:
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface MyAnnotation {}关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。后面有用到或发现,我会陆续补上!
相关文章推荐
- jdk各版本特性介绍(根据官网文档介绍)1.5-1.8
- jdk 1.8新特性
- Java基础之jdk1.8 JVM内存模型简述,含String常量池简单分析
- PHP 7的一些引人注目的新特性简单介绍
- 【笔记】RxJava2.0新特性简单介绍并实现RxBus
- Android 使用jdk1.8新特性之lambda表达式
- W3C Group的JavaScript1.8 新特性介绍
- JDK1.8新特性
- JAVA基础【1.5】《Java核心技术1》Java程序设计概述-Java JDK1.8新特性【收集】
- JDK1.8 十大新特性详解
- Windows Azure SDK1.3发布,简单介绍新特性
- JDK1.8的新特性之lambda表达式的三种引用
- iOS9新特性-UIStackView(简单介绍)
- JDK1.8新特性测试☞排序
- 总结:JDK1.5-JDK1.8各个新特性
- [Java]_[JDK1.8新特性]
- JDK1.5到JDK1.8增加的新特性
- JDK5.0新特性:拆箱/装箱 特性 介绍
- Open JDK 7 新特性(即JDK 1.8 新特性)
- 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比