堆内存中的永久区取消了。方法区静态常量划分到物理内存中。
hashmap底层变为数组加链表加红黑树了
lambda表达式
TreeSet<Integer> t=new TreeSet<Integer>((x,y)->x-y);
t.add(1);
t.add(31);
t.add(14);
System.err.println(t);
t.stream()
.forEach(System.out::println);
->左侧对应的是参数列表,右侧是实现。
类型推断功能,可以根据上下文推断类型
内置四大核心函数式接口
consumer<T>:消费型接口
void accept(T t)
@Test
void test() {
constomer("张三",(s) ->System.out.println("我叫"+s));
}
//消费型
public void constomer(String str,Consumer<String> s) {
s.accept(str);
}
}
supplier<T>:供给型接口
T get( )
@Test
void test() {
List<Integer> L=new ArrayList<Integer>();
List<Integer> s=supplier(L,() ->(int)(Math.random()*100));
System.out.println(s);
}
//供给型
public List<Integer> supplier(List<Integer> a,Supplier<Integer> s) {
for(int i=0;i<10;i++) {
a.add(s.get());
}
return a;
}
}
function<T,R>:函数型接口
R apply(T t)
@Test
void test() {
String func = func(" 爱java ",(s) ->s.trim());
System.out.println(func);
}
//函数型
public String func(String s,Function<String, String> f) {
return f.apply(s);
}
predicate(T t):断言型接口
Boolean test(T t)
@Test
void test() {
List<Integer> L=Arrays.asList(12,56,4,2,865,45,45,32);
List<Integer> func = func(L,(x) ->x>16);
System.out.println(func);
}
//段言型
public List<Integer> func(List<Integer> L,Predicate<Integer> p) {
List<Integer> s=new ArrayList<Integer>();
for(int i=0;i<L.size();i++) {
Integer integer = L.get(i);
if(p.test(integer)){
s.add(integer);
}
}
return s;
}
方法引用,lambda方法体中的方法已经实现了,我们可以使用方法引用(可以理解为lambda表达式的另一种引用)
对象::实例方法名
类::静态方法名
类::实例方法名
@Test
void test() {
Consumer<String> c=(x)->System.out.println(x);
c.accept("张三");
PrintStream p=System.out;
Consumer<String> c1=p::println;
c1.accept("李四");//方法的参数列表与返回值与调用的要一致
Consumer<String> c2=System.out::println;
c2.accept("王五");
Student s=new Student();
Supplier<String> s1=()->s.getName();
System.out.println(s1.get());
// 类名调用
Supplier<String> s2=s::getName;
System.out.println(s2.get());
Comparator<Integer> c=(x,y)->Integer.compare(x, y);
Comparator<Integer> c1=Integer::compare;//静态调用,实际是给方法赋值了。
BiPredicate<String, String> b=(x,y)->x.equals(y);
BiPredicate<String, String> b1=String::equals;//必须是某参数调用另一个参数为形参才可以用类名调用
}
}
构造器引用:classname::new
@Test
void test() {
Student s=new Student();
Supplier<Student> s1=()->new Student();
Supplier<Student> s2=Student::new;//无参
Function<String, Student> f=(x)->new Student(x);//带参
Function<String, Student> f1=Student::new;
f1.apply("张三");
System.out.println(f1);
}
数组引用Type::new
@Test
void test() {
Function<Integer, String[]> f=(x)->new String[x];
String[] apply = f.apply(20);
Function<Integer, String[]> f1=String[]::new;
String[] apply1 = f1.apply(20);
}
stream流:是数据渠道,用于操作数据(集合,数组)的所生成的序列
集合讲究的是数据,流讲究的是计算。
stream不会存储元素,不会改变源对象,会返回一个持有结果的stream,操作时延迟执行的,意味着等到
结果才会执行。
1 . 不是数据结构,不会保存数据。
2. 不会修改原来的数据源,它会将操作后的数据保存到另外一个对象中。(保留意见:毕竟peek方法可以修改流中元素)
3. 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算。
-
Intermediate:一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
-
Terminal:一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
在对一个Stream进行多次转换操作(Intermediate 操作),每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是N(转换次数)个for循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是lazy的,多个转换操作只会在Terminal操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal 操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。
中间操作,允许流打开状态,并调用后序方法。
filter(predictate p)
distinct
sorted
limit
末端方法:对流的终止操作,当stream执行末端方法后,会被消耗
foreach,reduce,min,max,count
anymatch(predictate p).findfirst
allmatch(predictate p),findany
1.可以通过collection集合创建stream与paprallstream
ArrayList<Integer> l=new ArrayList<Integer>();
Stream<Integer> stream1 = l.stream();
// 2.Arrays静态方法获取流
int[] a=new int[6];
IntStream stream2 = Arrays.stream(a);
// 3.Stream类静态方法of
Stream<String> stream3 = Stream.of("xx","dsad");
//4创建无限流
Stream<Integer> stream4 = Stream.iterate(0, (x)->x+2);
stream4.limit(10).forEach(System.out::println);//从流中限制条件
}
filter里面是断言型接口,进行筛选,传入的是lambda表达式
limit截断
skip跳过
distinct根据hashcode与equals计算找一样的去除
map集合:map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong。其实很好理解,如果想将原Stream中的数据类型,转换为double,int或者是long是可以调用相对应的方法。
peek:如同于map,能得到流中的每一个元素。但map接收的是一个Function表达式,有返回值;而peek接收的是Consumer表达式,没有返回值。
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
List<Student> studentList = Arrays.asList(s1, s2);
studentList.stream()
.peek(o -> o.setAge(100))
.forEach(System.out::println);
//结果:
Student{name='aa', age=100}
Student{name='bb', age=100}
flatmapflatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。
List<String> list = Arrays.asList("a,b,c", "1,2,3");
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println); // abc 123
Stream<String> s3 = list.stream().flatMap(s -> {
//将每个元素转换成一个stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
});
s3.forEach(System.out::println); // a b c 1 2 3
@Test
void test() {
List<String> words = Arrays.asList("hellow","world","world");
words.stream().map(w ->w.split("")).distinct().collect(Collectors.toList());
System.out.println(words);
List<String> wordList = words.stream()
.map(w ->w.split(""))
.flatMap(Arrays::stream).distinct().collect(Collectors.toList());
System.out.println(wordList);
}
}//[hellow, world, world]
//[h, e, l, o, w, r, d]
List<String> asList = Arrays.asList("aaa","vvvv","xscd");
asList.stream().map((str)->str.toUpperCase()).forEach(System.out::println);
中间操作:sorted与日常排序是一样使用
student s1=new student("二狗",26);
student s2=new student("张三",25);
student s3=new student("李四",16);
student s4=new student("王五",27);
student s5=new student("赵六",22);
List<student> l=Arrays.asList(s1,s2,s3,s4,s5);
//收集
List<Integer> reduce = l.stream().map(student::getAge).collect(Collectors.toList());
reduce.forEach(System.out::println);
collect将流转化为其他形式,用于给元素汇总的方法。
reduce归约,可以将流中元素反复结合起来得到一个值。
Optional<T> reduce(BinaryOperator<T> accumulator):第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;依次类推。
T reduce(T identity, BinaryOperator<T> accumulator):流程跟上面一样,只是第一次执行时,accumulator函数的第一个参数为identity,而第二个参数为流中的第一个元素。
List<Integer> asList = Arrays.asList(1,2,3,4,5,6,7,8,9);
Integer r = asList.stream().reduce(0,(x,y)->x+y);
System.out.println(r);
map-reduce筛选模式
void test() {
List<Integer> l=Arrays.asList(11,12,13,14,15,16,17,58,58,12,16);
Map<Integer, Long> collect = l.stream().collect(Collectors.groupingBy(p->p,Collectors.counting()));
collect.forEach((k,v)->System.out.println(k+"--"+v));
}
student s1=new student("二狗",26);
student s2=new student("张三",25);
student s3=new student("李四",16);
student s4=new student("王五",27);
student s5=new student("赵六",22);
List<student> l=Arrays.asList(s1,s2,s3,s4,s5);
Optional<Integer> reduce = l.stream().map(student::getAge).reduce(Integer::sum);
System.out.println(reduce.get());
判断一个操作是惰性求值还是及早求值,其实很简单,只需要看其返回值即可:如果返回值是Stream,那么就是惰性求值;如果返回值不是Stream或者是void,那么就是及早求值。
iterate方法,其返回的也是一个无限长度的Stream,与generate方法不同的是,其是通过函数f迭代对给指定的元素种子而产生无限连续有序Stream,其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环。
Stream.iterate(1, item -> item + 1)
.limit(10)
.forEach(System.out::println);
// 打印结果:1,2,3,4,5,6,7,8,9,10
collectors
Student s1 = new Student("aa", 10,1);
Student s2 = new Student("bb", 20,2);
Student s3 = new Student("cc", 10,3);
List<Student> list = Arrays.asList(s1, s2, s3);
//装成list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); // [10, 20, 10]
//转成set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); // [20, 10]
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10}
//字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
//聚合操作
//1.学生总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 20
//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); // 40
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
//分组
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
//规约
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40
java 7 forkjoin
在fork/join框架中,若某个子问题由于等待另一个子问题的完成而无法继续执行。那么处理该子问题的线程会主动寻找其他尚未运行完成的子问题来执行。这种方式减少了线程的等待时间,提高了性能。子问题中应该避免使用synchronized关键词或其他方式方式的同步。也不应该是一阻塞IO或过多的访问共享变量。在理想情况下,每个子问题的实现中都应该只进行CPU相关的计算,并且只适用每个问题的内部对象。唯一的同步应该只发生在子问题和创建它的父问题之间。
public class forkjoin extends RecursiveTask<Long> {
private long start;
private long end;
public forkjoin(long start,long end) {
this.start=start;
this.end=end;
}
public static long THRESHOLD=1000L;
@Override
protected Long compute() {
long length=end-start;
if(length<=THRESHOLD) {
long sum=0;
for(long i=start;i<end;i++) {
sum+=i;
}
return sum;
}else {
long middile=(end+start)/2;
forkjoin left=new forkjoin(start, middile);
left.fork();//拆分子任务压到线程队列
forkjoin right=new forkjoin(middile+1, end);
right.fork();
return left.join()+right.join();
}
}
}
public class testforkjoin {
public static void main(String[] args) {
Instant now = Instant.now();
ForkJoinPool f=new ForkJoinPool();
forkjoin fj=new forkjoin(0L, 100000000000L);
Long sum = f.invoke(fj);
System.out.println(sum);
Instant end = Instant.now();
System.out.println(Duration.between(now, end).toMillis());
}
}
java8并行流。底层是forkjoin框架
Instant now = Instant.now();
LongStream.rangeClosed(0, 100000000000L)
.parallel()
.reduce(0,Long::sum);
Instant end = Instant.now();
System.out.println(Duration.between(now, end).toMillis());
··optional是一个容器类,代表一个值存在或者不存在原来用null值表示不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。比如说当某个对象存在空指针异常后面一系列操作都会报,但是在容器中,直接锁定那一句代码。
public static <T> Optional<T> of(T value);// 为Optional设值,值为空抛异常
public static <T> Optional<T> ofNullable(T value) ;// 为Optional设值,值为空不抛异常
public T get();// 如果Optional中有值,则返回值,否则抛出 NoSuchElementException
public boolean isPresent() ; // 如果存在值,返回 true,否则返回 false
public void ifPresent(Consumer<? super T> consumer) ;// 如果存在值,则使用该值调用指定的消费者,否则不执行任何操作。
public T orElse(T other) ; //如果创建的Optional中有值存在,则返回此值,否则返回一个默认值
public T orElseGet(Supplier<? extends T> other) ; //如果Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
接口中默认方法
public interface DefaultFuncInter {
int getInt();
default String getString(){
return "Default String";
}
}
默认方法的主要优势是提供一种拓展接口的方法,而不破坏现有代码。加入我们有一个已经投入使用接口需要拓展一个新的方法,在JDK8以前,如果为一个使用的接口增加一个新方法,则我们必须在所有实现类中添加该方法的实现,否则编译会出现异常。如果实现类数量少并且我们有权限修改,可能会工作量相对较少。如果实现类比较多或者我们没有权限修改实现类源代码,这样可能就比较麻烦。而默认方法则解决了这个问题,它提供了一个实现,当没有显示提供其他实现时就采用这个实现。这样新添加的方法将不会破坏现有代码。
新的线程安全的时间方法与类注解与可重复注解。