程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(2)

java 8 新特性使用总结

发布于2021-05-29 19:06     阅读(1098)     评论(0)     点赞(22)     收藏(1)


java 8 新特性

1、默认方法

接口提供 default 关键字实现默认方法,可以直接使用,不用强制实现默认方法。

Java 8 允许我们通过 default 关键字对接口中定义的抽象方法提供一个默认的实现。

请看下面示例代码:

// 定义一个公式接口
interface Formula {
    // 计算
    double calculate(int a);

    // 求平方根
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

在上面这个接口中,我们除了定义了一个抽象方法 calculate,还定义了一个带有默认实现的方法 sqrt。 我们在实现这个接口时,可以只需要实现 calculate 方法,默认方法 sqrt 可以直接调用即可,也就是说我们可以不必强制实现 sqrt 方法。

补充:通过 default 关键字这个新特性,可以非常方便地对之前的接口做拓展,而此接口的实现类不必做任何改动。

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

上面通过匿名对象实现了 Formula 接口。但是即使是这样,我们为了完成一个 sqrt(a * 100) 简单计算,就写了 6 行代码,很是冗余。

2、lambda 表达式

Lambda表达式由用逗号分隔的参数列表–>符号函数体三部分表示 e -> System.out.println( e ) 举几个例子

在学习 Lambda 表达式之前,我们先来看一段老版本的示例代码,其对一个含有字符串的集合进行排序:

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

Collections 工具类提供了静态方法 sort 方法,入参是一个 List 集合,和一个 Comparator 比较器,以便对给定的 List 集合进行 排序。上面的示例代码创建了一个匿名内部类作为入参,这种类似的操作在我们日常的工作中随处可见。

Java 8 中不再推荐这种写法,而是推荐使用 Lambda 表达:

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

正如你看到的,上面这段代码变得简短很多而且易于阅读。但是我们还可以再精炼一点:

Collections.sort(names, (String a, String b) -> b.compareTo(a));

对于只包含一行方法的代码块,我们可以省略大括号,直接 return 关键代码即可。追求极致,我们还可以让它再短点:

names.sort((a, b) -> b.compareTo(a));

**说明:**并不是所有接口抽象方法都可以改写为lambda 表达式,只能是函数式接口,就是一个接口只有一个抽象方法,可以default 声明默认方法。

3、构造器,方法的引用

类的引用,class::new 构造器

class:: static_method 静态方法

class::methon 方法

instance::methon 对象对方法的引用

4、Lambda 访问外部变量及接口默认方法

**说明: 访问成员变量值不能更改,更改后访问不了,可以不声明fanal **

在 Lambda 表达式中,我们可以访问外部的 final 类型变量,如下面的示例代码:

// 转换器
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

与匿名内部类不同的是,我们不必显式声明 num 变量为 final 类型,下面这段代码同样有效:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

但是 num 变量必须为隐式的 final 类型,何为隐式的 final 呢?就是说到编译期为止,num 对象是不能被改变的,如下面这段代码,就不能被编译通过:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;

在 lambda 表达式内部改变 num 值同样编译不通过,需要注意, 比如下面的示例代码:

int num = 1;
Converter<Integer, String> converter = (from) -> {
	String value = String.valueOf(from + num);
	num = 3;
	return value;
};
5、带有默认实现方法的接口不能

当时,我们在接口中定义了一个带有默认实现的 sqrt 求平方根方法,在匿名内部类中我们可以很方便的访问此方法:

Formula formula = new Formula() {
	@Override
	public double calculate(int a) {
		return sqrt(a * 100);
	}
};

但是在 lambda 表达式中可不行:

Formula formula = (a) -> sqrt(a * 100);

带有默认实现的接口方法,是不能在 lambda 表达式中访问的,上面这段代码将无法被编译通过。

6、stream 工作

List<String> myList =
    Arrays.asList("a1", "a2", "b1", "c2", "c1");

myList
    .stream() // 创建流
    .filter(s -> s.startsWith("c")) // 执行过滤,过滤出以 c 为前缀的字符串
    .map(String::toUpperCase) // 转换成大写
    .sorted() // 排序
    .forEach(System.out::println); // for 循环打印

// C1
// C2
复制代码

我们可以对流进行中间操作或者终端操作。小伙伴们可能会疑问?什么是中间操作?什么又是终端操作?

Stream中间操作,终端操作Stream中间操作,终端操作

  • :**中间操作会再次返回一个流,**所以,我们可以链接多个中间操作,注意这里是不用加分号的。上图中的filter 过滤,map 对象转换,sorted 排序,就属于中间操作。
  • 终端操作是对流操作的一个结束动作,一般返回 void 或者一个非流的结果。上图中的 forEach循环 就是一个终止操作。

看完上面的操作,感觉是不是很像一个流水线式操作呢。

实际上,大部分流操作都支持 lambda 表达式作为参数,正确理解,应该说是接受一个函数式接口的实现作为参数。

2、stream 的类型

任何数据源都可以使用stream 来表示,最为常见的为 conlection 集合,list,set 使用 list.stream()方法创建流

也可以直接使用 stream.of() 方法进行创建流

Stream.of("a1", "a2", "a3")
    .findFirst()
    .ifPresent(System.out::println);  // a1

除了常规对象流之外,Java 8还附带了一些特殊类型的流,用于处理原始数据类型intlong以及double。说道这里,你可能已经猜到了它们就是IntStreamLongStream还有DoubleStream

IntStream.range(1, 4)
    .forEach(System.out::println); // 相当于 for (int i = 1; i < 4; i++) {}  //intStream.range()可以用来代替常规的for循环

// 1
// 2
// 3
  • 原始类型流使用其独有的函数式接口,例如IntFunction代替FunctionIntPredicate代替Predicate
  • 原始类型流支持额外的终端聚合操作,sum()以及average(),如下所示:

两种对象流进行互转操作

常规对象流转换为 原始对象流:mapToInt (),mapToLong() ,mapToDouble()

原始对象流转换为常规对象流 :mapToObj()

3、执行顺序
Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    });

不会打印任何东西,原因是需要有终端操作才会执行中间操作

Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    })
    .forEach(s -> System.out.println("forEach: " + s));
复制代码

再次执行,我们会看到输出如下:

filter:  d2
forEach: d2
filter:  a2
forEach: a2
filter:  b1
forEach: b1
filter:  b3
forEach: b3
filter:  c
forEach: c

疑问为什么不是先执行完 filter 再执行 foreach操作的,处于性能的考虑,是垂直执行的。

4、流的复用

当一个流使用终端操作时,流会关闭,可以使用 Supplier 来包装流得到一个新的流

为了克服这个限制,我们必须为我们想要执行的每个终端操作创建一个新的流链,例如,我们可以通过 Supplier 来包装一下流,通过 get() 方法来构建一个新的 Stream 流,如下所示:

Supplier<Stream<String>> streamSupplier =
    () -> Stream.of("d2", "a2", "b1", "b3", "c")
            .filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok
5、Collect
Collect 可以将流中的元素转换为不同的对象,例如 list,set,map ,collect 接受一个 Collector 搜集器

List<Person> filtered =
    persons
        .stream() // 构建流
        .filter(p -> p.name.startsWith("P")) // 过滤出名字以 P 开头的
        .collect(Collectors.toList()); // 生成一个新的 List  转换为set为Collectors.toSet()

System.out.println(filtered);    // [Peter, Pamela]


6、Flatmap

**作用:**可以将每个对象流转换为 0 个,一个,多个对象流

class Foo {
    String name;
    List<Bar> bars = new ArrayList<>();

    Foo(String name) {
        this.name = name;
    }
}

class Bar {
    String name;

    Bar(String name) {
        this.name = name;
    }
}
复制代码

接下来,通过我们上面学习到的流知识,来实例化一些对象:

List<Foo> foos = new ArrayList<>();

// 创建 foos 集合
IntStream
    .range(1, 4)
    .forEach(i -> foos.add(new Foo("Foo" + i)));

// 创建 bars 集合
foos.forEach(f ->
    IntStream
        .range(1, 4)
        .forEach(i -> f.bars.add(new Bar("Bar" + i + " <- " + f.name))));
复制代码

我们创建了包含三个foo的集合,每个foo中又包含三个 bar

flatMap 的入参接受一个返回对象流的函数。为了处理每个foo中的bar,我们需要传入相应 stream 流:

foos.stream()
    .flatMap(f -> f.bars.stream())
    .forEach(b -> System.out.println(b.name));

// Bar1 <- Foo1
// Bar2 <- Foo1
// Bar3 <- Foo1
// Bar1 <- Foo2
// Bar2 <- Foo2
// Bar3 <- Foo2
// Bar1 <- Foo3
// Bar2 <- Foo3
// Bar3 <- Foo3
复制代码

如上所示,我们已成功将三个 foo对象的流转换为九个bar对象的流。

7、stream 常用api

//中间操作符
/**
 * filter:过滤出想要的元素
 */
@Test
public void filter(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac");
    List<String> filter = list.stream().filter(s -> s.contains("a")).collect(Collectors.toList());
    System.out.println(filter);

}

/**
 * distinct:去除集合中相同的元素
 */
@Test
public void distinct(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    List<String> distincted = list.stream().distinct().collect(Collectors.toList());
    System.out.println(distincted);

    List<User> users = new ArrayList<>();
    users.add(new User(1, "zs"));
    users.add(new User(1, "zs"));
    users.add(new User(2, "yijian"));
    List<User> userList = users.stream().distinct().collect(Collectors.toList());
    System.out.println(userList);

}

/**
 * limit:获取流中的前几个元素
 */
@Test
public void limit(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    List<String> limit = list.stream().limit(3).collect(Collectors.toList());
    System.out.println(limit);
}

/**
 * skip:获取流中的后几个元素
 */
@Test
public void skip(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    List<String> sikp = list.stream().skip(3).collect(Collectors.toList());
    System.out.println(sikp);
}

/**
 * map:对流中的元素做统一的处理
 */
@Test
public void map(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    List<String> map = list.stream().map(s -> "map"+s).collect(Collectors.toList());
    System.out.println(map);

    List<Integer> integerList = Arrays.asList(1,2,3,4,5,6,1);
    List<Integer> integers = integerList.stream().map(integer -> integer + 1).collect(Collectors.toList());
    System.out.println(integers);

}

/**
 * sort:对集合中的元素进行排序
 */
@Test
public void sort(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    List<String> collect = list.stream().sorted().collect(Collectors.toList());
    System.out.println(collect);

    List<Integer> list1 = Arrays.asList(30,1,-1,3,7);
    List<Integer> collect1 = list1.stream().sorted((o1, o2) -> o2-o1).collect(Collectors.toList());
    System.out.println(collect1);

}
// 终止操作符

/**
 * anyMatch: 判断集合中是否有一个元素满足条件
 */
@Test
public void anyMatch(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    boolean match = list.stream().anyMatch(s -> s.contains("yijian"));
    System.out.println(match);
}

/**
 * allMatch: 判断集合中是否所有元素都满足条件
 */
@Test
public void allMatch(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    boolean match = list.stream().allMatch(s -> s.contains("yijian"));
    System.out.println(match);
}

/**
 * noneMatch: 判断集合中是否所有元素都不满足条件
 */
@Test
public void noneMatch(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    boolean match = list.stream().noneMatch(s -> s.contains("yijian"));
    System.out.println(match);
}

/**
 * findAny: 从集合中随机返回一个元素
 */
@Test
public void findAny(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    // 因为Stream 是串行的,默认返回第一个元素,提高效率
    // 可以改为parallelStream 让他并行执行
    Optional<String> any = list.parallelStream().findAny();
    if (any.isPresent()){
        System.out.println(any.get());
    }
}

/**
 * findFirst: 从集合中随机返回第一个元素
 */
@Test
public void findFirst(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    // 因为Stream 是串行的,默认返回第一个元素,提高效率
    // 可以改为parallelStream 让他并行执行
    Optional<String> any = list.parallelStream().findFirst();
    if (any.isPresent()){
        System.out.println(any.get());
    }
}

/**
 * collect:将流转换为其他形式:list、set、map
 */
@Test
public void collect(){
    List<String> list = Arrays.asList("qw", "yijian", "ad", "efg","ac","yijian");
    Set<String> collect = list.stream().collect(Collectors.toSet());
    System.out.println(collect);
    // Collectors.toMap(key -> key, value -> value, (oldKey, newKey) -> newKey)
    Map<String, String> collect1 = list.stream().collect(Collectors.toMap(key -> key, value -> value, (oldKey, newKey) -> newKey));
    System.out.println(collect1);
}

/**
 * reduce:将流中的元素结合起来
 */
@Test
public void reduce(){
    List<Integer>list = Arrays.asList(1,2,3,5,6,6,2,1);
    // reduce((初始值,每一项的值))
    Optional<Integer> optional = list.stream().reduce((initialValue, itemValue) -> {
        return initialValue + itemValue;
    });
    if (optional.isPresent()){
        System.out.println(optional.get());
    }
    // reduce(初始值,(初始值,每一项的值))
    Integer integer = list.stream().reduce(1, (initialValue, itemValue) -> {
        return initialValue + itemValue;
    });
        System.out.println(integer);

}

/**
 * count:获取集合中元素数量
 */
@Test
public void count(){
    List<Integer>list = Arrays.asList(1,2,3,5,6,6,2,1);
    Optional<Integer> max = list.stream().max((o1, o2) -> {
        return o1 - o2;
    });
    System.out.println(max);
    System.out.println("集合中元素的个数:"+list.stream().count());

}
}


所属网站分类: 技术文章 > 博客

作者:skdk

链接:http://www.javaheidong.com/blog/article/207116/1b00b70c7c311eeee21a/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

22 0
收藏该文
已收藏

评论内容:(最多支持255个字符)