改动
- hashmap的存储方式
java8在hashmap的链表长度达到8时,就会自动转换为红黑树进行存储。
可以参考一下这个HashMap与HashTable与并发容器。
- 接口中可以有非抽象的方法
接口中可以有两种有方法体的方法
1.static修饰的方法
接口被继承时,static修饰的有方法体的方法不会被继承
接口被实现时,static修饰的有方法体的方法与实现类无关,不能被重写
该方法只能被自身调用,通过类名来引用,继承或实现的都不行
2.default修饰的方法
接口被继承时,default修饰的有方法体的方法可以被继承并重写
接口被实现时,default修饰的有方法体的方法可以被实现类重写
该方法可以被继承或实现类引用
public interface FirstInterface {
//接口中的成员变量默认是静态常量
String STRING = "First Interface";
static void funStatic(){
System.out.println(STRING + " : I am the static method!");
}
default void funDefault(){
System.out.println(STRING + " : I am the default method!");
}
void fun();
}
/**
* 继承第一个接口
*/
public interface SecondInterface extends FirstInterface{
String STRING = "Second Interface";
//无法重写第一个接口的static方法
static void funStatic(){
System.out.println(STRING + " : I am the static method!");
}
//可以重写第一个接口的default方法
@Override
default void funDefault(){
System.out.println(STRING + " : I am the default method!");
}
//普通方法可以被重写,emmmm并没有什么用,又没有方法体
@Override
void fun();
}
//重写default
public class ImplementFirst implements FirstInterface{
@Override
public void funDefault() {
System.out.println("I have implement the First Interface!");
}
@Override
public void fun() {
System.out.println("I have implement the First Interface!");
}
}
//不重写default
public class ImplementSecond implements SecondInterface{
@Override
public void fun() {
System.out.println("I have implement the Second Interface!");
}
}
public class Main {
public static void main(String[] args){
FirstInterface firstInterface = new ImplementFirst();
//无法调用static方法
//无法调用STRING常量
firstInterface.fun();
//重写的
firstInterface.funDefault();
SecondInterface secondInterface = new ImplementSecond();
secondInterface.fun();
//没有重写的
secondInterface.funDefault();
//通过自身调用
FirstInterface.funStatic();
System.out.println(FirstInterface.STRING);
}
}
输出:
I have implement the First Interface!
I have implement the First Interface!
I have implement the Second Interface!
Second Interface : I am the default method!
First Interface : I am the static method!
First Interface
新增
- Lambda表达式
基本语法:
<函数式接口> <变量名> = (参数1,参数2...) -> {
方法体
}
引用实例方法:
<函数式接口> <变量名> = <实例>::<实例方法名>
调用:
<变量名>.接口方法([实际参数...])
引用类方法:
<函数式接口> <变量名> = <类>::<类方法名称>
调用:
<变量名>.接口方法([实际参数...])
引用类的实例方法:
定义接口
interface <函数式接口>{
<返回值> <方法名>(<类><类名称>,[其他参数...]);
}
<函数式接口> <变量名> = <类>::<类实例方法名>
调用:
<变量名>.接口方法(类的实例,[实际参数...])
引用构造器方法:
<函数式接口> <变量名> = <类>::<new>
调用:
<变量名>.接口方法([实际参数...])
public class Lambda {
//无参,无返回值
private interface NoReturn{
void foo();
}
//无参,有返回值
private interface AReturn{
int foo();
}
//有参,无返回值
private interface NoReturnParam{
void foo(int a);
}
//有参,有返回值
private interface AReturnParam{
int foo(int a);
}
public static void main(String[] args){
//无参,无返回值
//旧的实现方式,此处显示的为实现接口中方法
NoReturn noReturn = new NoReturn() {
@Override
public void foo() {
System.out.println("hello old");
}
};
noReturn.foo();
//Lambda表达式,此处显示的为重写接口中的方法
NoReturn noReturn1 = () -> System.out.println("hello lambda");
noReturn1.foo();
//无参,有返回值
int y = 0;
AReturn aReturn = () -> {
System.out.println("the y will be final:" + y);
return y + 10;
};
//此处使用y会有异常,y在return之后变成了final,在内部可以进行改变
System.out.println(aReturn.foo());
//有参,无返回值
NoReturnParam NoReturnParam = (a) -> System.out.println(a);
NoReturnParam NoReturnParam1 = b -> System.out.println(b);
NoReturnParam.foo(1);
NoReturnParam1.foo(2);
//上面2中方式的更简单表达
NoReturnParam NoReturnParam3 = System.out::println;
NoReturnParam3.foo(3);
//有参,有返回值
AReturnParam aReturnParam = a -> {
System.out.println(a);
return a % 10;
};
System.out.println(aReturnParam.foo(66));
}
}
输出:
hello old
hello lambda
the y will be final:0
10
1
2
3
66
6
总结:有返回值的应该只能使用箭头的方式,无返回值的可以使用箭头,也可以使用双冒号。箭头的方式可以写多行代码,双冒号的应该只能写一行的。
- Predicate 接口
Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
- Function 接口
Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen)
- Consumer 接口
Consumer 接口表示执行在单个参数上的操作。接口只有一个参数,且无返回值
- Comparator 接口
Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法
- Supplier 接口
Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
- Optional 接口
Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:
Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。
- Stream 接口
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class NewStream {
private static void createStream(){
//通过Stream.of来创建
Stream<Integer> integerStream = Stream.of(1,2,4,8);
Stream<String> stringStream = Stream.of("hello");
//通过Stream.generate来创建,创建的是无限长度的Stream,三种一样,写法不同
//由于生成的长度无限,所以采用的懒加载,并且一般与limit配合使用,如第3个
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream<Double> doubleStream1 = Stream.generate(() -> Math.random());
Stream<Double> doubleStream2 = Stream.generate(Math::random).limit(10);
//通过Stream.iterate来创建,也是生成无限长度的Stream,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的
//也要配合limit使用,取出前10个数字打印
Stream.iterate(46, i -> i + 1).limit(10).forEach(System.out::println);
//Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。
//Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。
//Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
int max = 1000000;
List<String> list = new ArrayList<>(max);
for (int i = 0; i < max; i++){
list.add(UUID.randomUUID().toString().replaceAll("-", ""));
}
//串行排序
long t0 = System.nanoTime();
long count = list.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));
//并行排序
long t2 = System.nanoTime();
long count1 = list.parallelStream().sorted().count();
System.out.println(count1);
long t3 = System.nanoTime();
long millis1 = TimeUnit.NANOSECONDS.toMillis(t3 - t2);
System.out.println(String.format("parallel sort took: %d ms", millis1));
//长度为18,大于50的有8个数
List<Integer> integerList = Arrays.asList(8, 25, 91, 35, 74, 0, 27, 36, 95, 59, 19, 22, 4, 25, 59, 84, 82, 66);
//count方法,统计stream的长度
System.out.println("count:" + integerList.parallelStream().count());
//max方法,获取最大值,调用Comparator接口
System.out.println("max:" + integerList.parallelStream().max(Comparator.comparingInt(a -> a)).get());
//min方法,获取最小值,调用Comparator接口
System.out.println("min:" + integerList.parallelStream().min(Comparator.comparingInt(a -> a)).get());
//filter方法,通过一个predicate接口来过滤并只保留符合条件的元素,中间操作
System.out.println("count filter > 50:" + integerList.stream().filter(Objects::nonNull).filter(num -> num > 50).count());
//distinct方法,去除stream中的重复元素,中间操作
integerList.stream().distinct().forEach(System.out::println);
System.out.println("after distinct:" + integerList.toString());
//sorted方法,排序stream,可以使用filter,中间操作
//需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据integerList是不会被修改的
integerList.stream().sorted().filter(num -> num > 10).forEach(System.out::println);
System.out.println("after sorted:" + integerList.toString());
//map方法,根据筛选创建一个新的stream
//自带mapToInt,mapToLong,mapToDouble方法
List<String> strings = new ArrayList<>();
strings.add("123");
strings.add("312");
strings.add("231");
strings.stream().mapToInt(Integer::valueOf).forEach(System.out::println);
strings.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
//limit方法,对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素,中间操作
//skip方法,返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream,中间操作
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(null);
numbers.add(5);
numbers.add(5);
numbers.add(9);
numbers.add(11);
System.out.println("sum is:" + numbers.stream().filter(Objects::nonNull).distinct().mapToInt(num -> num * 2).peek(System.out::println).skip(2).limit(4).sum());
System.out.println("after limit or skip:" + numbers.toString());
//match匹配,Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。返回一个boolean类型的值,所有的匹配操作都是最终操作
List<String> stringList = new ArrayList<>();
stringList.add("hello");
stringList.add("world");
stringList.add("i");
stringList.add("need");
stringList.add("to");
stringList.add("improve");
boolean isMatch = stringList.stream().allMatch(s -> s.startsWith("i"));
System.out.println("allMatch:" + isMatch);
System.out.println("anyMatch:" + stringList.stream().anyMatch(s -> s.startsWith("i")));
System.out.println("noneMatch:" + stringList.stream().noneMatch(s -> s.length() > 9));
//Reduce 规约,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的,最终操作
Optional<String> optional = stringList.stream().reduce((s1, s2) -> s1 + s2);
optional.ifPresent(System.out::println);
}
public static void main(String[] args){
createStream();
}
}
输出:
46
47
48
49
50
51
52
53
54
55
1000000
sequential sort took: 880 ms
1000000
parallel sort took: 505 ms
count:18
max:95
min:0
count filter > 50:8
8
25
91
35
74
0
27
36
95
59
19
22
4
84
82
66
after distinct:[8, 25, 91, 35, 74, 0, 27, 36, 95, 59, 19, 22, 4, 25, 59, 84, 82, 66]
19
22
25
25
27
35
36
59
59
66
74
82
84
91
95
after sorted:[8, 25, 91, 35, 74, 0, 27, 36, 95, 59, 19, 22, 4, 25, 59, 84, 82, 66]
123
312
231
123
312
231
2
10
18
22
sum is:40
after limit or skip:[1, null, 5, 5, 9, 11]
allMatch:false
anyMatch:true
noneMatch:true
helloworldineedtoimprove
总结:中间操作不会改变原来的数据源,最终操作会改变。
主要参考大佬:JAVA8 十大新特性详解