JDK8新特性(二)
四、Stream API1、简介Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。流(Stream) 到...
四、Stream API
1、简介
Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
2、示例
@Test
public void testStream(){
//1、创建strean流
Stream<Person> stream = persons.stream();
//2、进行中间操作
stream = stream.filter(p-> p.getAge()>20);
//3、终止操作
stream.forEach(System.out::println);
persons.stream().filter(p->p.getAge()>20).forEach(System.out::println);
}
3、创建strean流
Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
Stream创建无限流
//迭代
Stream<Integer> stream = Stream.iterate(0, (x) -> x + 2);
stream.forEach(System.out::println);
//生成
Stream<Double> stream = Stream.generate(Math::random);
stream.forEach(System.out::println);
4、中间操作
filter—从流中排除某些元素
persons.stream().filter(p->p.getAge()>20).forEach(System.out::println);
imit—截断流,使其元素不超过给定数量
persons.stream().filter(p->p.getAge()>20).limit(2).forEach(System.out::println);
skip(n) —跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流
persons.stream().filter(p->p.getAge()>20).skip(2).forEach(System.out::println);
distinct—筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
persons.stream().filter(p->p.getAge()>20).distinct().forEach(System.out::println);
映射map , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<String> stream = strList.stream().map(String::toUpperCase);
stream.forEach(System.out::println);
flatMap—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<Stream<Character>> stream2 = strList.stream()
.map(StreamTest::filterCharacter);
stream2.forEach(System.out::println);
System.out.println("---------------------------------------------");
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream<Character> stream3 = strList.stream()
.flatMap(StreamTest::filterCharacter);
stream3.forEach(System.out::println);
sorted()—自然排序
persons.stream().filter(p->p.getAge()>20).sorted().forEach(System.out::println);
sorted(Comparator com)—定制排序
persons.stream().filter(p->p.getAge()>20).sorted((x,y)->{
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
5、终止操作
allMatch—检查是否匹配所有元素
boolean b =persons.stream().filter(p->p.getAge()>20).allMatch(p->p.getAge()==35);
anyMatch—检查是否至少匹配一个元素
boolean b =persons.stream().filter(p->p.getAge()>20).anyMatch(p->p.getAge()==35);
noneMatch—检查是否没有匹配的元素
boolean b =persons.stream().filter(p->p.getAge()>20).noneMatch(p->p.getAge()==35);
findFirst—返回第一个元素
Optional<Person> op = persons.stream().filter(p->p.getAge()>20).findFirst();
findAny—返回当前流中的任意元素
Optional<Person> op = persons.stream().filter(p->p.getAge()>20).findAny();
count—返回流中元素的总个数
long count = persons.stream().filter(p->p.getAge()>20).count();
max—返回流中最大值
persons.stream().map(Person::getSalary).max(Double::compare);
min—返回流中最小值
persons.stream().map(Person::getSalary).min(Double::compare);
归约reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
collect—将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
List<String> liststr = persons.stream().map(Person::getName).collect(Collectors.toList());
Set<String> setstr = persons.stream().map(Person::getName).collect(Collectors.toSet());
五、接口中的默认方法与静态方法
JDK8之前,interface中可以定义常量和抽象方法,访问修饰符是public。
public interface A {
/** a1和a2写法是等价的 */
public static final int a1 = 0;
int a2 = 0;
/** methodA1和methodA2写法是等价的 */
public abstract void methodA1();
void methodA2();
}
JDK8起,允许我们在interface中使用static和default修饰方法(使用这两种修饰符中其一就不能使用abstract修饰符),default修饰的方法,通过接口的实现类的对象调用;static修饰的方法,直接通过接口名调用。
public interface MyConstruct<X,Y,Z,T> {
public T getValue(X x,Y y,Z z);
default String getValue(Person person){
return person.toString();
}
static Person getObject(){
return new Person();
}
六、新时间日期API
1、LocalDate、LocalTime、LocalDateTime
-
LocalDate专门表示日期
-
LocalTime专门表示时间
-
LocalDateTime可以同时表示日期和时间
// 1.获取当前时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
// 2.设置任意时间
LocalDateTime ldt = LocalDateTime.of(2018,12,8,12,11,13);
System.out.println(ldt);
// 3.增加或减少年月日,时分秒
LocalDateTime ldt2 = ldt.plusYears(2);
System.out.println(ldt2);
ldt.minusMonths(1);
// 4.获取年月日,时分秒
System.out.println(ldt.getYear());
System.out.println(ldt.getMonth());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
// 5.获取毫秒见
System.out.println(ldt.getNano());
System.out.println(ldt.getDayOfWeek());
System.out.println(ldt.getDayOfYear());
2、Instant : 时间戳。
使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值
@Test
public void testInstant(){
Instant ins = Instant.now(); //默认使用 UTC 时区
System.out.println(ins);
//北京时间(东八区)+8个小时
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
System.out.println(ins.getNano());
}
3、Duration Period
Duration : 用于计算两个“时间”间隔,Period : 用于计算两个“日期”间隔
@Test
public void testInterval(){
Instant ins1 = Instant.now();
Instant ins2 = Instant.now();
ins2 = ins2.plusSeconds(1000);
System.out.println("时间间隔为:"+Duration.between(ins1,ins2).toMillis());
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = LocalDate.of(2019,1,20);
Period period = Period.between(localDate,localDate1);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
}
4、TemporalAdjuster 时间校正器
-
dayOfWeekInMonth 创建一个新的日期,它的值为同一个月中每一周的第几天
-
firstDayOfMonth 创建一个新的日期,它的值为当月的第一天
-
firstDayOfNextMonth 创建一个新的日期,它的值为下月的第一天
-
firstDayOfNextYear 创建一个新的日期,它的值为明年的第一天
-
firstDayOfYear 创建一个新的日期,它的值为当年的第一天
-
firstInMonth 创建一个新的日期,它的值为同一个月中,第一个符合星期几要求的值
-
lastDayOfMonth 创建一个新的日期,它的值为当月的最后一天
-
lastDayOfNextMonth 创建一个新的日期,它的值为下月的最后一天
-
lastDayOfNextYear 创建一个新的日期,它的值为明年的最后一天
-
lastDayOfYear 创建一个新的日期,它的值为今年的最后一天
-
lastInMonth 创建一个新的日期,它的值为同一个月中,最后一个符合星期几要求的值
-
next/previous 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期
-
nextOrSame/previousOrSame 创建一个新的日期,并将其值设定为日期调整后或者调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);LocalDateTime ldt2 = ldt.withDayOfMonth(10);
System.out.println(ldt2);LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
5、DateTimeFormatter : 解析和格式化日期或时间
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
6、ZonedDate、ZonedTime、ZonedDateTime带时区的时间或日期
LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);
七、其他新特性
1、Optional 容器类:用于尽量避免空指针异常
-
Optional.of(T t) : 创建一个 Optional 实例
-
Optional.empty() : 创建一个空的 Optional 实例
-
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
-
isPresent() : 判断是否包含值
-
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
-
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
-
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
-
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
@Test
public void testOf(){
Optional op = Optional.of(new Person(“vesus1”,18,3333));
System.out.println(op.get());
}@Test
public void testEmpty(){
Optional op = Optional.empty();
System.out.println(op.get());
}@Test
public void testNullable(){
Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
System.out.println(op.get());
}@Test
public void testPresent(){
Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
System.out.println(op.isPresent());Optional<Person> op1 = Optional.ofNullable(null); System.out.println(op1.isPresent());
}
@Test
public void testOrElse(){
// Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
Optional op = Optional.ofNullable(null);
Person person = op.orElse(new Person(“vesus2”,18,5555));
System.out.println(person);}
@Test
public void testOrElseGet(){
// Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
Optional op = Optional.ofNullable(null);
Person person = op.orElseGet(Person::new);
System.out.println(person);
}@Test
public void testMap(){
Optional op = Optional.ofNullable(new Person(“vesus1”,18,3333));
Optional map = op.map(§->{
p.setName(“map”);
return p ;
});
System.out.println(map);
}
2、重复注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnos {
MyAnno[] value();
}
@Repeatable(MyAnnos.class)
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
String value();
}
public class AnnoTest {
@MyAnno("vesus")
@MyAnno("vesus11")
public void test(){
System.out.println("hello word");
}
}
3、jdk8中对jvm优化
1、虚拟机栈:每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误
2、本地方法栈:这部分主要与虚拟机用到的 Native 方法相关,一般情况下, Java 应用程序员并不需要关心这部分的内容。线程私有。
3、PC 寄存器:PC 寄存器,也叫程序计数器。JVM支持多个线程同时运行,每个线程都有自己的程序计数器。倘若当前执行的是 JVM 的方法,则该寄存器中保存当前执行指令的地址;倘若执行的是native 方法,则PC寄存器中为空。
4、堆:堆内存是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。当申请不到空间时会抛出 OutOfMemoryError。
5、方法区:方法区也是所有线程共享。主要用于存储类信息、常量池、静态变量、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
所有新创建的Object 都将会存储在新生代Yong Generation中。如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。
永久代存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息
jdk8中引入了元空间的概念
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
更多推荐
所有评论(0)