JDK1.8 新特性(未完待续)
本文主要介绍了JDK1.8版本中的一些新特性,乃作者视频观后笔记,仅供参考。
jdk1.8新特性知识点:
- Lambda表达式
- 函数式接口
- *方法引用和构造器调用
- Stream API
- 接口中的默认方法和静态方法
- 新时间日期API
在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化 原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode 方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容 如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表 无限下去,那么效率极低,碰撞是避免不了的 加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生 在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入 除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾 ConcurrentHashMap (锁分段机制),concurrentLevel,jdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用
Lambda表达式
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
先来体验一下lambda最直观的优点:简洁代码
//匿名内部类 Comparator<Integer> cpt = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; TreeSet<Integer> set = new TreeSet<>(cpt); System.out.println("========================="); //使用lambda表达式 Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y); TreeSet<Integer> set2 = new TreeSet<>(cpt2);
只需要一行代码,极大减少代码量!!
这样一个场景,在商城浏览商品信息时,经常会有条件的进行筛选浏览,例如要选颜色为红色的、价格小于8000千的
// 筛选颜色为红色 public List<Product> filterProductByColor(List<Product> list){ List<Product> prods = new ArrayList<>(); for (Product product : list){ if ("红色".equals(product.getColor())){ prods.add(product); } } return prods; } // 筛选价格小于8千的 public List<Product> filterProductByPrice(List<Product> list){ List<Product> prods = new ArrayList<>(); for (Product product : list){ if (product.getPrice() < 8000){ prods.add(product); } } return prods; }
我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?
优化一:使用设计模式
定义一个MyPredicate接口
public interface MyPredicate <T> { boolean test(T t); }
如果想要筛选颜色为红色的商品,定义一个颜色过滤类
public class ColorPredicate implements MyPredicate <Product> { private static final String RED = "红色"; @Override public boolean test(Product product) { return RED.equals(product.getColor()); }
定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。
public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){ List<Product> prods = new ArrayList<>(); for (Product prod : list){ if (mp.test(prod)){ prods.add(prod); } } return prods; }
例如,如果想要筛选价格小于8000的商品,那么新建一个价格过滤类既可
public class PricePredicate implements MyPredicate<Product> { @Override public boolean test(Product product) { return product.getPrice() < 8000; } }
这样实现的话可能有人会说,每次变更需求都需要新建一个实现类,感觉还是有点繁琐呀,那么再来优化一下
优化二:使用匿名内部类
定义过滤方法:
public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){ List<Product> prods = new ArrayList<>(); for (Product prod : list){ if (mp.test(prod)){ prods.add(prod); } } return prods; }
调用过滤方法的时候:
// 按价格过滤 public void test2(){ filterProductByPredicate(proList, new MyPredicate<Product>() { @Override public boolean test(Product product) { return product.getPrice() < 8000; } }); } // 按颜色过滤 public void test3(){ filterProductByPredicate(proList, new MyPredicate<Product>() { @Override public boolean test(Product product) { return "红色".equals(product.getColor()); } }); }
使用匿名内部类,就不需要每次都新建一个实现类,直接在方法内部实现。看到匿名内部类,不禁想起了Lambda表达式。
优化三:使用lambda表达式
定义过滤方法:
public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){ List<Product> prods = new ArrayList<>(); for (Product prod : list){ if (mp.test(prod)){ prods.add(prod); } } return prods; }
使用lambda表达式进行过滤
@Test public void test4(){ List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000); for (Product pro : products){ System.out.println(pro); } }
在jdk1.8中还有更加简便的操作 Stream API
优化四:使用Stream API
甚至不用定义过滤方法,直接在集合上进行操作
// 使用jdk1.8中的Stream API进行集合的操作 @Test public void test(){ // 根据价格过滤 proList.stream() .fliter((p) -> p.getPrice() <8000) .limit(2) .forEach(System.out::println); // 根据颜色过滤 proList.stream() .fliter((p) -> "红色".equals(p.getColor())) .forEach(System.out::println); // 遍历输出商品名称 proList.stream() .map(Product::getName) .forEach(System.out::println); }
Lmabda表达式的语法总结: () -> ();
口诀:左右遇一省括号,左侧推断类型省
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念
函数式接口
函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
什么是函数式接口?
简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface
常见的四大函数式接口
- Consumer 《T》:消费型接口,有参无返回值
@Test public void test(){ changeStr("hello",(str) -> System.out.println(str)); } /** * Consumer<T> 消费型接口 * @param str * @param con */ public void changeStr(String str, Consumer<String> con){ con.accept(str); }
- Supplier 《T》:供给型接口,无参有返回值
@Test public void test2(){ String value = getValue(() -> "hello"); System.out.println(value); } /** * Supplier<T> 供给型接口 * @param sup * @return */ public String getValue(Supplier<String> sup){ return sup.get(); }
- Function 《T,R》::函数式接口,有参有返回值
@Test public void test3(){ Long result = changeNum(100L, (x) -> x + 200L); System.out.println(result); } /** * Function<T,R> 函数式接口 * @param num * @param fun * @return */ public Long changeNum(Long num, Function<Long, Long> fun){ return fun.apply(num); }
- Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
public void test4(){ boolean result = changeBoolean("hello", (str) -> str.length() > 5); System.out.println(result); } /** * Predicate<T> 断言型接口 * @param str * @param pre * @return */ public boolean changeBoolean(String str, Predicate<String> pre){ return pre.test(str); }
在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。
总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了
方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
(a) 方法引用
三种表现形式:
1. 对象::实例方法名
2. 类::静态方法名
3. 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)
public void test() { /** *注意: * 1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致! * 2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method * */ Consumer<Integer> con = (x) -> System.out.println(x); con.accept(100); // 方法引用-对象::实例方法 Consumer<Integer> con2 = System.out::println; con2.accept(200); // 方法引用-类名::静态方法名 BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y); BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare; Integer result = biFun2.apply(100, 200); // 方法引用-类名::实例方法名 BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2); BiFunction<String, String, Boolean> fun2 = String::equals; Boolean result2 = fun2.apply("hello", "world"); System.out.println(result2); }
(b)构造器引用
格式:ClassName::new
public void test2() { // 构造方法引用 类名::new Supplier<Employee> sup = () -> new Employee(); System.out.println(sup.get()); Supplier<Employee> sup2 = Employee::new; System.out.println(sup2.get()); // 构造方法引用 类名::new (带一个参数) Function<Integer, Employee> fun = (x) -> new Employee(x); Function<Integer, Employee> fun2 = Employee::new; System.out.println(fun2.apply(100)); }
(c)数组引用
格式:Type[]::new
public void test(){ // 数组引用 Function<Integer, String[]> fun = (x) -> new String[x]; Function<Integer, String[]> fun2 = String[]::new; String[] strArray = fun2.apply(10); Arrays.stream(strArray).forEach(System.out::println); }