佛山定制网站建设,制作公众号的软件,建做网站,个性logo图案设计我们先从最熟悉的*Java集合框架(Java Collections Framework, JCF)*开始说起。
为引入Lambda表达式#xff0c;Java8新增了java.util.funcion包#xff0c;里面包含常用的函数接口#xff0c;这是Lambda表达式的基础#xff0c;Java集合框架也新增部分接口#xff0c;以便…我们先从最熟悉的*Java集合框架(Java Collections Framework, JCF)*开始说起。
为引入Lambda表达式Java8新增了java.util.funcion包里面包含常用的函数接口这是Lambda表达式的基础Java集合框架也新增部分接口以便与Lambda表达式对接。
首先回顾一下Java集合框架的接口继承结构 上图中绿色标注的接口类表示在Java8中加入了新的接口方法当然由于继承关系他们相应的子类也都会继承这些新方法。下表详细列举了这些方法。
口名Java8新加入的方法CollectionremoveIf() spliterator() stream() parallelStream() forEach()ListreplaceAll() sort()MapgetOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()
这些新加入的方法大部分要用到java.util.function包下的接口这意味着这些方法大部分都跟Lambda表达式相关。我们将逐一学习这些方法。 Collection中的新方法
如上所示接口Collection和List新加入了一些方法我们以是List的子类ArrayList为例来说明。了解Java7ArrayList实现原理将有助于理解下文。
forEach()
该方法的签名为void forEach(Consumer? super E action)作用是对容器中的每个元素执行action指定的动作其中Consumer是个函数接口里面只有一个待实现方法void accept(T t)后面我们会看到这个方法叫什么根本不重要你甚至不需要记忆它的名字。
需求假设有一个字符串列表需要打印出其中所有长度大于3的字符串.
Java7及以前我们可以用增强的for循环实现
// 使用曾强for循环迭代
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
for(String str : list){if(str.length()3)System.out.println(str);
}
现在使用forEach()方法结合匿名内部类可以这样实现
// 使用forEach()结合匿名内部类迭代
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.forEach(new ConsumerString(){Overridepublic void accept(String str){if(str.length()3)System.out.println(str);}
});
上述代码调用forEach()方法并使用匿名内部类实现Comsumer接口。到目前为止我们没看到这种设计有什么好处但是不要忘记Lambda表达式使用Lambda表达式实现如下
// 使用forEach()结合Lambda表达式迭代
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.forEach( str - {if(str.length()3)System.out.println(str);});
上述代码给forEach()方法传入一个Lambda表达式我们不需要知道accept()方法也不需要知道Consumer接口类型推导帮我们做了一切。
removeIf()
该方法签名为boolean removeIf(Predicate? super E filter)作用是删除容器中所有满足filter指定条件的元素其中Predicate是一个函数接口里面只有一个待实现方法boolean test(T t)同样的这个方法的名字根本不重要因为用的时候不需要书写这个名字。
需求假设有一个字符串列表需要删除其中所有长度大于3的字符串。
我们知道如果需要在迭代过程冲对容器进行删除操作必须使用迭代器否则会抛出ConcurrentModificationException所以上述任务传统的写法是
// 使用迭代器删除列表元素
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
IteratorString it list.iterator();
while(it.hasNext()){if(it.next().length()3) // 删除长度大于3的元素it.remove();
}
现在使用removeIf()方法结合匿名内部类我们可是这样实现
// 使用removeIf()结合匿名名内部类实现
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.removeIf(new PredicateString(){ // 删除长度大于3的元素Overridepublic boolean test(String str){return str.length()3;}
});
上述代码使用removeIf()方法并使用匿名内部类实现Precicate接口。相信你已经想到用Lambda表达式该怎么写了
// 使用removeIf()结合Lambda表达式实现
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.removeIf(str - str.length()3); // 删除长度大于3的元素
使用Lambda表达式不需要记忆Predicate接口名也不需要记忆test()方法名只需要知道此处需要一个返回布尔类型的Lambda表达式就行了。
replaceAll()
该方法签名为void replaceAll(UnaryOperatorE operator)作用是对每个元素执行operator指定的操作并用操作结果来替换原来的元素。其中UnaryOperator是一个函数接口里面只有一个待实现函数T apply(T t)。
需求假设有一个字符串列表将其中所有长度大于3的元素转换成大写其余元素不变。
Java7及之前似乎没有优雅的办法
// 使用下标实现元素替换
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
for(int i0; ilist.size(); i){String str list.get(i);if(str.length()3)list.set(i, str.toUpperCase());
}
使用replaceAll()方法结合匿名内部类可以实现如下
// 使用匿名内部类实现
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.replaceAll(new UnaryOperatorString(){Overridepublic String apply(String str){if(str.length()3)return str.toUpperCase();return str;}
});
上述代码调用replaceAll()方法并使用匿名内部类实现UnaryOperator接口。我们知道可以用更为简洁的Lambda表达式实现
// 使用Lambda表达式实现
ArrayListString 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()方法
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
Collections.sort(list, new ComparatorString(){Overridepublic int compare(String str1, String str2){return str1.length()-str2.length();}
});
现在可以直接使用List.sort()方法结合Lambda表达式可以这样写
// List.sort()方法结合Lambda表达式
ArrayListString list new ArrayList(Arrays.asList(I, love, you, too));
list.sort((str1, str2) - str1.length()-str2.length());
spliterator()
方法签名为SpliteratorE spliterator()该方法返回容器的可拆分迭代器。从名字来看该方法跟iterator()方法有点像我们知道Iterator是用来迭代容器的Spliterator也有类似作用但二者有如下不同 Spliterator既可以像Iterator那样逐个迭代也可以批量迭代。批量迭代可以降低迭代的开销。 Spliterator是可拆分的一个Spliterator可以通过调用SpliteratorT trySplit()方法来尝试分成两个。一个是this另一个是新返回的那个这两个迭代器代表的元素没有重叠。
可通过多次调用Spliterator.trySplit()方法来分解负载以便多线程处理。
stream()和parallelStream()
stream()和parallelStream()分别返回该容器的Stream视图表示不同之处在于parallelStream()返回并行的Stream。Stream是Java函数式编程的核心类我们会在后面章节中学习。 Map中的新方法
相比CollectionMap中加入了更多的方法我们以HashMap为例来逐一探秘。了解Java7HashMap实现原理将有助于理解下文。
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
HashMapInteger, String map new HashMap();
map.put(1, one);
map.put(2, two);
map.put(3, three);
for(Map.EntryInteger, String entry : map.entrySet()){System.out.println(entry.getKey() entry.getValue());
}
使用Map.forEach()方法结合匿名内部类代码如下
// 使用forEach()结合匿名内部类迭代Map
HashMapInteger, String map new HashMap();
map.put(1, one);
map.put(2, two);
map.put(3, three);
map.forEach(new BiConsumerInteger, String(){Overridepublic void accept(Integer k, String v){System.out.println(k v);}
});
上述代码调用forEach()方法并使用匿名内部类实现BiConsumer接口。当然实际场景中没人使用匿名内部类写法因为有Lambda表达式
// 使用forEach()结合Lambda表达式迭代Map
HashMapInteger, 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中指定的值不存在时使用默认值
HashMapInteger, String map new HashMap();
map.put(1, one);
map.put(2, two);
map.put(3, three);
// Java7以及之前做法
if(map.containsKey(4)){ // 1System.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中所有映射关系
HashMapInteger, String map new HashMap();
map.put(1, one);
map.put(2, two);
map.put(3, three);
for(Map.EntryInteger, String entry : map.entrySet()){entry.setValue(entry.getValue().toUpperCase());
}
使用replaceAll()方法结合匿名内部类实现如下
// 使用replaceAll()结合匿名内部类实现
HashMapInteger, String map new HashMap();
map.put(1, one);
map.put(2, two);
map.put(3, three);
map.replaceAll(new BiFunctionInteger, String, String(){Overridepublic String apply(Integer k, String v){return v.toUpperCase();}
});
上述代码调用replaceAll()方法并使用匿名内部类实现BiFunction接口。更进一步的使用Lambda表达式实现如下
// 使用replaceAll()结合Lambda表达式实现
HashMapInteger, 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) - v1v2);
compute()
该方法签名为compute(K key, BiFunction? super K,? super V,? extends V remappingFunction)作用是把remappingFunction的计算结果关联到key上如果计算结果为null则在Map中删除key的映射
要实现上述merge()方法中错误信息拼接的例子使用compute()代码如下
map.compute(key, (k,v) - vnull ? 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的定义可能是MapK,SetV要向Map中放入新值可通过如下代码实现
MapInteger, SetString map new HashMap();
// Java7及以前的实现方式
if(map.containsKey(1)){map.get(1).add(one);
}else{SetString valueSet new HashSetString();valueSet.add(one);map.put(1, valueSet);
}
// Java8的实现方式
map.computeIfAbsent(1, v - new HashSetString()).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);elsemap.remove(key);return newValue;
}
return null; Java8为容器新增一些有用的方法这些方法有些是为完善原有功能有些是为引入函数式编程学习和使用这些方法有助于我们写出更加简洁有效的代码 函数接口虽然很多但绝大多数时候我们根本不需要知道它们的名字书写Lambda表达式时类型推断帮我们做了一切