JDK8 新特性详解,2014-03-18 正式发布

1、Lambda 演变过程

@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    //名字
    private String name;
    //性别
    private String sex;
    //薪水
    private int salary;
    //年龄
    private int age;
    //星座
    private String star;
}
1.1、普通筛选

将这个集合遍历,然后依次的判断,这是最为普通的一种方式。

@Test
public void test1(){
    //首先创建一个
    List<Student> list = Arrays.asList(
            new Student("九天","男",5000,18,"天秤座"),
            new Student("十夜","男",4000,16,"双鱼座"),
            new Student("十一郎","男",3000,24,"水瓶座")
    );

    List<Student> result = new ArrayList<>();
    for (Student student:list){
        if ("天秤座".equals(student.getStar())){
            result.add(student);
        }
    }
    System.out.println(result);
}
1.2、匿名内部类筛选

通过匿名内部类的方法,在内部类中添加判断条件进行筛选,首先创建一个公共接口:

public interface FilterProcess<T\> {
    boolean process(T t);
}

接下来通过一个公共函数,对集合以及筛选条件做一个共同方法,筛选到班级里星座是天秤星座的学生

public List<Student> filterStudent(List<Student> students, FilterProcess<Student> mp){
    List<Student> list = new ArrayList<>();

    for (Student student : students) {
        if(mp.process(student)){
            list.add(student);
        }
    }
    return list;
}

最后是通过匿名内部类和该方法得到结果:

@Test
public void test2(){
    List<Student> students = Arrays.asList(
            new Student("九天","男",5000,18,"天秤座"),
            new Student("十夜","男",4000,16,"双鱼座"),
            new Student("十一郎","男",3000,24,"水瓶座")
    );

    List<Student> list = filterStudent(students, new FilterProcess<Student>() {
        @Override
        public boolean process(Student student) {
            return student.getStar().equals("天秤座");
        }
    });
    for (Student student : list) {
        System.out.println(student);
    }
}

结果如图:

1.3、半 Lambda 方法

但是通过这两种代码都是很多,所以 java8 在这一点上提供了对集合筛选最大程度的删减代码,就是第三种方法。第三种方法:通过 Lambda 直接判断,一步到位,不需要在写其他的方法。

@Test
public void test3(){
    List<Student> list = Arrays.asList(
            new Student("九天","男",5000,18,"天秤座"),
            new Student("十夜","男",4000,16,"双鱼座"),
            new Student("十一郎","男",3000,24,"水瓶座")
    );
    List<Student> result = filterStudent(list,(e)->e.getStar().equals("天秤座"));
    System.out.println(result);
}

测试结果:

[Student(name=九天, sex=男, salary=5000, age=18, star=天秤座)]

但是现在又会有人会问这个问题,我的那个方法中是这样子的

filterStudent(List<Student> students, FilterProcess<Student> mp)

为什么我的代码参数却是这样子的呢

filterStudent(list,(e)->e.getStar().equals(“天秤座”)

其实 -> 这个是一个连接符,左边代表参数,而右边代表函数体(也就是我们说的条件),这个 e 就是代表 FilterProcess mp 这个参数的,只不过我们得 java8 中 lambda 可以给这个参数附加上了条件,这些条件筛选都是封装到 jdk8 中内部类中自己实现的,所以我们只要附加条件就可以了,那个 (e) 就代表传了参数。

1.4、真正运用 lambda 方法
@Test
public void test1() {
    List<Student> list = Arrays.asList(
            new Student("九天","男",5000,18,"天秤座"),
            new Student("十夜","男",4000,16,"双鱼座"),
            new Student("十一郎","男",3000,24,"水瓶座")
    );

    list.stream().filter((e) -> e.getStar().equals("天秤座"))
            .forEach(System.out::println);
}

结果依然是相同的答案,直到第 4 个方法出来,对比前三个方法,简单了很多,这就是我们 lambda 演练的过程。

总结:lambda 主要是针对集合中条件的筛选,包括数组等等。接下来我们介绍 Stream API , 这个和 Lambda 息息相关,论重要性,lambda 只是基础,Stream API 才是真正的升级版

2、StreamAPI 详解

2.0、功能

父类:BasicStream

子类:Stream、IntStream、LongStream、DoubleStream

包含两个类型,中间操作 (intermediate operations) 和结束操作 (terminal operations)

下面是所有方法的属于那一端操作的方法:

然后准备一个测试类,和一个静态变量,图下:

public class JdkTest {

    public static List<Student> list = Arrays.asList(
            new Student("九天", "男", 5000, 18, "天秤座"),
            new Student("十夜", "男", 4000, 16, "双鱼座"),
            new Student("十一郎", "男", 3000, 24, "水瓶座")
    );
}

接下来我们一个一个方法解析他们的作用

2.1、stream

将集合转换成流,一般会使用流继续后续操作。

@Test
public void test0() {
    list.stream();
}
2.2、forEach 遍历

forEach 遍历集合,System.out::println 等同于 System.out.println ()

@Test
public void test1() {
    list.forEach(System.out::println);
}

结果为:

2.3、filter 过滤

该方法中是一个筛选条件,等同于 sql 查询的 where 后面的筛选。

@Test
public void test2() {
    list.stream().filter((e) \-\> e.getStar().equals("天秤座"))
            .forEach(System.out::println);
}

2.4、map 转换集合

将 List 转换为 List, collect 是将结果转换为 List

@Test
public void test3() {
    List<String> names = list.stream().map(Student::getName).collect(Collectors.toList());
    names.stream().forEach(System.out::println);
}

结果:

map 的 key 直接转换 list,如下:

Map<String, Object>map = new HashMap <>(); 
map.put("key1","1"); 
map.put("key2","1"); 
map.put("key3","1"); 
map.put("key4","1"); 
List<String> cidList = map.keySet().stream().map(_String::toString).collect(Collectors.toList()); System.out.println(cidList);

结果:

[key1, key2, key3, key4]

2.5、mapToInt 转换数值流

转换数值流,等同 mapToLong、mapToDouble,如下这个是取最大值

@Test
public void test4() {
    IntStream intStream = list.stream().mapToInt(Student::getAge);
    Stream<Integer> integerStream = intStream.boxed();
    Optional<Integer> max   = integerStream.max(Integer::compareTo);
    System.out.println(max.get());
}

结果为:24

2.6、flatMap 合并成一个流

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

@Test
public void test5() {
    List<String> list2 = new ArrayList<>();
    list2.add("aaa bbb ccc");
    list2.add("ddd eee fff");
    list2.add("ggg hhh iii");
    list2 = list2.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(Collectors.toList());
    System.out.println(list2);
}
  结果为:[aaa, bbb, ccc, ddd, eee, fff, ggg, hhh, iii]
2.7、distinct 去重
@Test
public void test6() {
    List<String> list2 = new ArrayList<>();
    list2.add("aaa bbb ccc");
    list2.add("ddd eee fff");
    list2.add("ggg hhh iii");
    list2.add("ggg hhh iii");

    list2.stream().distinct().forEach(System.out::println);
}

结果:

aaa bbb ccc
ddd eee fff
ggg hhh iii

复杂去重,根据字段

List_<RedPacketRecord>_ newList = records.stream_()
_ .collect_(Collectors.collectingAndThen(Collectors.toCollection(()_ -> new TreeSet_<>(Comparator.comparing(RedPacketRecord::getRoomId))), ArrayList::new))_;

2.8、sorted 排序
@Test
public void test7() {
    //asc排序
    list.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
    System.out.println("------------------------------------------------------------------");
    //desc排序
    list.stream().sorted(Comparator.comparingInt(Student::getAge).reversed()).forEach(System.out::println);
}
  结果:
Student(name=十夜, sex=, salary=4000, age=16, star=双鱼座)
Student(name=九天, sex=, salary=5000, age=18, star=天秤座)
Student(name=十一郎, sex=, salary=3000, age=24, star=水瓶座)
------------------------------------------------------------------
Student(name=十一郎, sex=, salary=3000, age=24, star=水瓶座)
Student(name=九天, sex=, salary=5000, age=18, star=天秤座)
Student(name=十夜, sex=, salary=4000, age=16, star=双鱼座)
2.9、skip 跳过前 n 个
@Test
public void test8() {
    list.stream().skip(1).forEach(System.out::println);
}
2.10、limit 截取前 n 个
@Test
public void test10() {
    list.stream().limit(1).forEach(System.out::println);
}
  结果为:
Student(name=九天, sex=, salary=5000, age=18, star=天秤座)
2.11、anyMatch

只要有其中任意一个符合条件

@Test
public void test11() {
    boolean isHave = list.stream().anyMatch(student -> student.getAge() == 16);
    System.out.println(isHave);
}
2.12、allMatch

全部符合

@Test
public void test12() {
    boolean isHave = list.stream().allMatch(student -> student.getAge() == 16);
    System.out.println(isHave);
}
2.13、noneMatch

是否满足没有符合的

@Test
public void test13() {
    boolean isHave = list.stream().noneMatch(student -> student.getAge() == 16);
    System.out.println(isHave);
}
2.14、findAny

找到其中一个元素 (使用 stream () 时找到的是第一个元素;使用 parallelStream () 并行时找到的是其中一个元素)

@Test
public void test14() {
    Optional<Student> student = list.stream().findAny();
    System.out.println(student.get());
}
2.15、findFirst

找到第一个元素

@Test
public void test15() {
    Optional<Student> student = list.stream().findFirst();
    System.out.println(student.get());
}
2.17、count 计数
@Test
public void test17() {
    long count = list.stream().count();
    System.out.println(count);
}
2.18、of

生成一个字符串流

@Test
public void test18() {
    Stream<String> stringStream = Stream.of("i","love","you");
}
2.19、empty

生成一个空流

@Test
public void test19() {
    Stream<String> stringStream = Stream.empty();
}
2.20、iterate
@Test
public void test20() {
    List<String> list = Arrays.asList("a", "b", "c", "c", "d", "f", "a");
    Stream.iterate(0, i -> i + 1).limit(list.size()).forEach(i -> {
        System.out.println(String.valueOf(i) + list.get(i));
    });
}
2.21、collect:averagingLong

求平均值

@Test 
public void test1(){
    // 求年龄平均值
 Double average = _list_.stream().collect(Collectors._averagingLong_(Student::getAge));
  }
2.22、collect:collectingAndThen

两步结束,先如何,在如何

@Test 
public void test1(){
    // 求年龄平均值
 String average = _list_.stream().collect(Collectors._collectingAndThen_(Collectors._averagingInt_(Student::getAge), a->"哈哈,平均年龄"+a));
 System.out.println(average); }

结果:

哈哈,平均年龄20.5

2.23、collect:counting

求个数

@Test 
public void test1(){
    // 求数量
 Long num = _list_.stream().collect(Collectors._counting_());
 System._out_.println(num); 
 }
2.24、collect: groupingBy(Function)

接下来所有的都是用下面的新 List 数据测试使用

public static List<Student> _list_ \= Arrays._asList_(
        new Student("九天", "男", 5000, 18, "天秤座"),
 new Student("十夜", "男", 4000, 16, "双鱼座"),
 new Student("十一郎", "男", 3000, 24, "水瓶座"),
 new Student("十二郎", "男", 3000, 24, "水瓶座")
);

@Test public void test1(){
    Map<Integer,List<Student>> result = _list_.stream().collect(Collectors._groupingBy_(Student::getAge));
 for (Integer age:result.keySet()){
        System._out_.println(result.get(age));
    }
}

结果:

[Student(name=十夜, sex=男, salary=4000, age=16, star=双鱼座)]
[Student(name=九天, sex=男, salary=5000, age=18, star=天秤座)]
[Student(name=十一郎, sex=男, salary=3000, age=24, star=水瓶座), Student(name=十二郎, sex=男, salary=3000, age=24, star=水瓶座)]

2.25、collect:groupingBy(Function,Collector)
@Test 
public void test1(){
    // 先分组,在计算每组的个数
 Map<Integer,Long> num = _list_.stream().collect(Collectors._groupingBy_(Student::getAge,Collectors._counting_()));
 System._out_.println(num);
  }

结果:{16=1, 18=1, 24=2}

2.26、collect:groupingBy(Function, Supplier, Collector)
@Test 
public void test1(){
    // 先分组,在计算每组的个数,然后排序
 Map<Integer,Long> num = _list_.stream().collect(Collectors._groupingBy_(Student::getAge, TreeMap::new,Collectors._counting_()));
 System._out_.println(num);
  }
2.27、collect:groupingByConcurrent

同上,不过这个 Concurrent 是并发的,也有 3 个方法,和上面非并发一个效果

groupingByConcurrent(Function)

groupingByConcurrent(Function, Collector)

groupingByConcurrent(Function, Supplier, Collector)

2.28、collect:joining()
@Test
 public void test1(){
    // 名字拼接
 String result = _list_.stream().map(Student::getName).collect(Collectors._joining_());
 System._out_.println(result); 
 }

结果:

九天十夜十一郎十二郎

2.29、collect:joining(str)
@Test 
public void test1(){
    // 名字拼接,用逗号隔开
 String result = _list_.stream().map(Student::getName).collect(Collectors._joining_(","));
 System._out_.println(result);
  }

结果:

九天,十夜,十一郎,十二郎

2.30、collect:joining(str, prefix, suffix)
@Test 
public void test1(){
    // 名字拼接,包含前缀、后缀
 String result = _list_.stream().map(Student::getName).collect(Collectors._joining_(",","hello","world"));
 System._out_.println(result);
  }

结果:

hello九天,十夜,十一郎,十二郎world

2.31、collect:summarizingDouble

@Test
public void test1(){
// 求年龄的最大值、最小值、平均值、综合以及人数
DoubleSummaryStatistics result = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println(result);
}

结果:

DoubleSummaryStatistics{count=4, sum=82.000000, min=16.000000, average=20.500000, max=24.000000}

2.32、collect:toCollection

有很多如

3、Date

3.1、JDK7 Date 缺点
1、所有的日期类都是可变的,因此他们都不是线程安全的,这是Java日期类最大的问题之一。
2Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外
用于格式化和解析的类在java.text包中定义。
3java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql
包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。对于时间、时间戳、
格式化以及解析,并没有一些明确定义的类。对于格式化和解析的需求,我们有java.text.DateFormat
抽象类,但通常情况下,SimpleDateFormat类被用于此类需求
4、日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendarjava.util.TimeZone类,但他们同样存在上述所有的问题
3.2、JDK8 Date 优势
1、不变性:新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处。
2、关注点分离:新的API将人可读的日期时间和机器时间(unix timestamp)明确分离,
它为日期(Date)、 时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
3、清晰:在所有的类中,方法都被明确定义用以完成相同的行为。
举个例子,要拿到当前实例我们可以使用now()方法,在所有的类中都定义了format()parse()方法,
而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,
一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
4、实用操作:所有新的日期/时间API类都实现了一系列方法用以完成通用的任务,
如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
5、可扩展性:新的日期/时间API是工作在ISO-8601日历系统上的,但我们也可以将其应用在非IOS的日历上。
3.3、JDK8 Date 新增字段

Java.time 包中的是类是不可变且线程安全的。新的时间及日期 API 位于 java.time 中,java8 time 包下关键字段解读。

属性含义
Instant 代表的是时间戳
LocalDate 代表日期,比如 2020-01-14
LocalTime 代表时刻,比如 12:59:59
LocalDateTime 代表具体时间 2020-01-12 12:22:26
ZonedDateTime 代表一个包含时区的完整的日期时间,偏移量是以 UTC/  格林威治时间为基准的
Period 代表时间段
ZoneOffset 代表时区偏移量,比如:+8:00
Clock 代表时钟,比如获取目前美国纽约的时间
3.4、获取当前时间
Instant instant = Instant.now(); //获取当前时间戳

LocalDate localDate = LocalDate.now();  //获取当前日期

LocalTime localTime = LocalTime.now();  //获取当前时刻

LocalDateTime localDateTime = LocalDateTime.now();  //获取当前具体时间

ZonedDateTime zonedDateTime = ZonedDateTime.now();   //获取带有时区的时间
3.5、字符串转换

jdk8:
String str = “2019-01-11”;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“yyyy-MM-dd”);
LocalDate localDate = LocalDate.parse(str, formatter);

jdk7:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd”);
try {
Date date = simpleDateFormat.parse(str);
} catch (ParseException e){
e.printStackTrace();
}

3.6、Date 转换 LocalDate
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;

public class Test {

    public static void main(String\[\] args) {
        Date date = new Date();
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();

        // atZone()方法返回在指定时区从此Instant生成的ZonedDateTime。
        LocalDate localDate = instant.atZone(zoneId).toLocalDate();
        System.out.println("Date = " + date);
        System.out.println("LocalDate = " + localDate);
    }
}
3.7、LocalDate 转 Date
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;

public class Test {

    public static void main(String\[\] args) {
        ZoneId zoneId = ZoneId.systemDefault();
        LocalDate localDate = LocalDate.now();
        ZonedDateTime zdt = localDate.atStartOfDay(zoneId);

        Date date = Date.from(zdt.toInstant());

        System.out.println("LocalDate = " + localDate);
        System.out.println("Date = " + date);

    }
}
3.8、时间戳转 LocalDateTime
long timestamp = System.currentTimeMillis();

Instant instant = Instant.ofEpochMilli(timestamp);

LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
3.9、LocalDateTime 转时间戳
LocalDateTime dateTime = LocalDateTime.now();

dateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();

dateTime.toInstant(ZoneOffset.of("+08:00")).toEpochMilli();

dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
3.10、LocalDate 方法总结
getYear()                         int        获取当前日期的年份
getMonth()                        Month      获取当前日期的月份对象
getMonthValue()                   int        获取当前日期是第几月
getDayOfWeek()                    DayOfWeek  表示该对象表示的日期是星期几
getDayOfMonth()                   int        表示该对象表示的日期是这个月第几天
getDayOfYear()                    int        表示该对象表示的日期是今年第几天
withYear(int year)                LocalDate  修改当前对象的年份
withMonth(int month)              LocalDate  修改当前对象的月份
withDayOfMonth(intdayOfMonth)     LocalDate  修改当前对象在当月的日期
isLeapYear()                      boolean    是否是闰年
lengthOfMonth()                   int        这个月有多少天
lengthOfYear()                    int        该对象表示的年份有多少天(365或者366)
plusYears(longyearsToAdd)         LocalDate  当前对象增加指定的年份数
plusMonths(longmonthsToAdd)       LocalDate  当前对象增加指定的月份数
plusWeeks(longweeksToAdd)         LocalDate  当前对象增加指定的周数
plusDays(longdaysToAdd)           LocalDate  当前对象增加指定的天数
minusYears(longyearsToSubtract)   LocalDate  当前对象减去指定的年数
minusMonths(longmonthsToSubtract) LocalDate  当前对象减去注定的月数
minusWeeks(longweeksToSubtract)   LocalDate  当前对象减去指定的周数
minusDays(longdaysToSubtract)     LocalDate  当前对象减去指定的天数
compareTo(ChronoLocalDateother)   int        比较当前对象和other对象在时间上的大小,返回值如果
    为正,则当前对象时间较晚,
isBefore(ChronoLocalDateother)    boolean    比较当前对象日期是否在other对象日期之前
isAfter(ChronoLocalDateother)     boolean    比较当前对象日期是否在other对象日期之后
isEqual(ChronoLocalDateother)     boolean    比较两个日期对象是否相等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值