首页 > 其他分享 >JDK1.8新特性

JDK1.8新特性

时间:2024-05-27 09:57:57浏览次数:23  
标签:JDK1.8 String Stream list 接口 特性 println public

Lambda表达式

又称为函数式编程

面向对象思想: 想要做什么事情,就需要创建这个类的对象,通过对象来完成某些事情

函数式的思想:忽略了面向对象复杂的语法,强调做什么,而不是以什么形式做

面向对象的思想实现多线程

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程在运行");
    }
}


public class DemoRunnable {
    public static void main(String[] args) {
        //创建Runnable实现类对象
        Runnable run = new RunnableImpl();
        //新建Thread类对象,传入Runnable实现类对象
        Thread t = new Thread(run);
        //调用start方法
        t.start();


        System.out.println(Thread.currentThread().getName() + "线程在运行");


        //简化写法,使用内部类的写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "线程在运行");
            }
        }).start();

    }
}

代码分析:

1,Thread类需要Runnable接口作为参数,其中抽象的run()方法是用来指定任务内容的核心

2,为了指定run()方法的内容,不得不去实现Runnable接口,写实现类

3,为了省去定义一个实现类的麻烦,使用了匿名内部类去实现

4,在匿名内部类中,必须要重写run()方法,方法名、返回值等内容都需要重写一遍

5,在整个代码中,核心的内容是方法体,以前的写法,不得不把一些其他的信息都写一遍

6,我们想要的其实是run()方法的核心代码的运行,不得不去做了其他的事情

使用lambda表达式,可以解决以上的问题

//lambda表达式的写法

new Thread(

()->{System.out.println(Thread.currentThread().getName() + "线程在运行");}

).start();

对比几种实现方式:

1,实现Runnable接口,在main中,new Thread传入接口实现类,调用start方法,实现多线程

语法比较繁琐,还需要创建实现类,比较麻烦

2,匿名内部类的写法,在new Thread的时候,传入Runnable的实现

可以省去一个实现类的定义,但是语法比较复杂,看不太懂

3,lambda表达式的写法

比较简洁,没学习过也看不太懂

Lambda表达式的格式:

由三个部分组成

1,一些参数

2,一个箭头

3,一段代码

格式: (参数)->{方法的具体实现内容}

lambda表达式一般只能使用在,实现一些函数式接口的时候使用,

() :表示对应了这个接口中的抽象方法,方法如果有参数,那么就需要传入参数,没有参数就空着,多个参数,逗号隔开

->:传递的意思,把参数传递给方法体

{}:重写接口中方法的方法体

函数式接口【重点】

经过前面的学习,相信大家对于Lambda表达式已经有了初步的了解。

总结一下:

Lambda表达式是接口的匿名内部类的简写形式

接口必须满足:有且仅有一个抽象方法

其实这样的接口,我们称为函数式接口,我们学过的Runnable、Comparator都是函数式接口的典型代表。

概念

函数式接口,即适用于函数式编程场景的接口。

而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。

只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

格式

只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称 {

public abstract 返回值类型 方法名称(可选参数信息);

// 其他非抽象方法内容

}

由于接口当中抽象方法的public abstract 是可以省略的,所以定义一个函数式接口很简单:

public interface MyFunctionalInterface {

Integer getValue(Integer num);

}

@FunctionalInterface接口

但是在实践中,函数接口是非常脆弱的,只要有人在接口里多添加一个方法,那么这个接口就不是函数接口了,就会导致编译失败。

与 @Override 注解的作用类似,Java 8提供了一个特殊的注解@FunctionalInterface来克服上面提到的脆弱性并且显示地表明函数接口。而且jdk8版本中,对很多已经存在的接口都添加了@FunctionalInterface注解,例如Runnable接口

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。

需要注 意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

自定义函数式接口

需求:对一个数进行运算,比如乘方、加减另一个数等

自定义一个函数式接口

@FunctionalInterface
public interface MyFunctionalInterface {
Integer getValue(Integer num);
}

                                        /*
对一个数进行运算,比如乘方、加减另一个数等
 */
public class NumberTest {

    //定义一个方法,将函数式的接口作为参数使用
    public static int operation(int num, MyFunction function) {
        return function.getValue(num);
    }

    public static void main(String[] args) {
        //计算一个数的平方
        int num = operation(100, i -> i * i);
        System.out.println(num);
    }
}

4大内置核心函数式接口

Supplier接口

接口说明

JDK源码

@FunctionalInterface

public interface Supplier<T> {

// 无需参数,返回一个T类型结果

T get();

}

Supplier,英文翻译就是“供应者”,顾名思义:只产出,不收取。所以不接受任何参数,返回T类型结果。

用来获取一个泛型参数指定类型的对象数据。

由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据。

示例

示例:定义一个方法产生指定个数的整数,并放入到集合中,在测试方法中遍历该集合

/*
示例:定义一个方法产生指定个数的整数,并放入到集合中,在测试方法中遍历该集合
 */
public class SupplierDemo {
    public static void main(String[] args) {
        List<Integer> list = getNumList(3, () -> (int) (Math.random() * 100));

        //for (Integer integer : list) {
        //    System.out.println(integer);
        //}
        list.forEach(System.out::println);
    }
    //定义一个方法产生指定个数的整数,并放入到集合中
    public static List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList();
        //这里的num表示指定个数
        for (int i = 0; i < num; i++) {
            list.add(sup.get());
        }
        return list;
    }
}

练习:求数组最大值

使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。提示:接口的泛型请使用 java.lang.Integer 类。

/*
示例:使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
 */
public class SupplierDemo01 {
    public static void main(String[] args) {
        int[] arr = {1,3,22,34,56,23};
        int maxNum = getMax(() -> {
            int max = arr[0];
            for (int i : arr) {
                if (i > max) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println(maxNum);
    }
    //定义一个方法产生指定个数的整数,并放入到集合中
    public static int getMax(Supplier<Integer> sup) {
        return sup.get();
    }
}

Consumer接口

接口说明

java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。

JDK源码:

@FunctionalInterface

public interface Consumer<T> {

void accept(T t);

default Consumer<T> andThen(Consumer<? super T> after) {

Objects.requireNonNull(after);

return (T t) -> { accept(t); after.accept(t); };

}

}

抽象方法:accept

消费一个指定泛型的数据。基本使用如:

public class ConsumerDemo {

public static void main(String[] args) {

consumerMoney(100,money -> System.out.println("消费了"+money+"元"));

}

//定义一个花钱 方法

public static void consumerMoney(double money,Consumer<Double> consumer){

consumer.accept(money);

}

}

默认方法:andThen

如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。

要想实现组合,需要两个或多个Lambda表达式即可,而andThen的语义正是“一步接一步”操作。

例如两个步骤组 合的情况:

public class ConsumerDemo01 {

public static void main(String[] args) {

consumerStr(

s -> System.out.println(s.toUpperCase()),

s -> System.out.println(s.concat("world"))

);

}

public static void consumerStr(Consumer<String> con1,Consumer<String> con2){

con1.andThen(con2).accept("hello");

}

}

Predicate接口

接口说明

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口。

JDK源码

package java.util.function;

import java.util.Objects;

@FunctionalInterface

public interface Predicate<T> {

boolean test(T t);

default Predicate<T> and(Predicate<? super T> other) {

Objects.requireNonNull(other);

return (t) -> test(t) && other.test(t);

}

default Predicate<T> negate() {

return (t) -> !test(t);

}

default Predicate<T> or(Predicate<? super T> other) {

Objects.requireNonNull(other);

return (t) -> test(t) || other.test(t);

}

static <T> Predicate<T> isEqual(Object targetRef) {

return (null == targetRef)

? Objects::isNull

: object -> targetRef.equals(object);

}

}

抽象方法:test

用于条件判断的场景,Predicate系列参数不固定,但是返回的一定是boolean类型。

需求:将满足条件的字符串放入到集合中【过滤字符串】

/*

需求:将满足条件的字符串放入到集合中【过滤字符串】

过滤出长度大于4的字符,放入集合中

*/

public class PredicateDemo {

public static void main(String[] args) {

//先创建一个字符串集合

List<String> list = Arrays.asList("hello", "world", "ok", "oracle", "java");

//找出集合中长度大于4的字符串

List<String> strList = filterStr(list, (str) -> str.length() > 4);

for (String s : strList) {

System.out.println(s);

}

}

//过滤字符串方法,将满足条件的字符串加入到集合中

public static List<String> filterStr(List<String> list, Predicate<String> pre) {

ArrayList<String> strList = new ArrayList<>(); //符合条件的字符串放到这个list中

for (String s : list) {

if (pre.test(s)) {

strList.add(s);

}

}

return strList;

}

}

默认方法:and

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法and 。

public class PredicateDemo {

public static void main(String[] args) {

//先创建一个字符串集合

List<String> list = Arrays.asList("hello", "world", "ok", "oracle", "java");

//找出集合中长度大于4的字符串

List<String> strList = filterStr(list,

str -> str.length() > 4,

str -> str.contains("h"));

for (String s : strList) {

System.out.println(s);

}

}

//过滤字符串方法,将满足条件的字符串加入到集合中

public static List<String> filterStr(List<String> list, Predicate<String> pre1,Predicate<String> pre2) {

ArrayList<String> strList = new ArrayList<>(); //符合条件的字符串放到这个list中

for (String s : list) {

if (pre1.and(pre2).test(s)) {

strList.add(s);

}

}

return strList;

}

}

默认方法:or

与and的“与”类似,默认方法or实现逻辑关系中的“或”

上述and的条件改为或:即字符串长度>4或包含h,只需要修改上面的filterStr方法即可

public class PredicateDemo {

public static void main(String[] args) {

//先创建一个字符串集合

List<String> list = Arrays.asList("hei", "world", "ok", "oracle", "java");

//找出集合中长度大于4的字符串

List<String> strList = filterStr(list,

str -> str.length() > 4,

str -> str.contains("h"));

for (String s : strList) {

System.out.println(s);

}

}

//过滤字符串方法,将满足条件的字符串加入到集合中

public static List<String> filterStr(List<String> list, Predicate<String> pre1,Predicate<String> pre2) {

ArrayList<String> strList = new ArrayList<>(); //符合条件的字符串放到这个list中

for (String s : list) {

if (pre1.or(pre2).test(s)) {

strList.add(s);

}

}

return strList;

}

}

默认方法:negate

它是执行了test方法之后,对结果boolean值进行“!”取反而已。一定要在 test 方法调用之前调用 negate 方法,正如 and 和 or 方法一样:

修改条件:取长度<=3的字符串

public class PredicateDemo {

public static void main(String[] args) {

//先创建一个字符串集合

List<String> list = Arrays.asList("hei", "world", "ok", "oracle", "java");

//找出集合中长度大于4的字符串

List<String> strList = filterStr(list,

str -> str.length() > 4

);

for (String s : strList) {

System.out.println(s);

}

}

//过滤字符串方法,将满足条件的字符串加入到集合中

public static List<String> filterStr(List<String> list, Predicate<String> pre1) {

ArrayList<String> strList = new ArrayList<>(); //符合条件的字符串放到这个list中

for (String s : list) {

if (pre1.negate().test(s)) {

strList.add(s);

}

}

return strList;

}

}

另一种写法

Predicate<String> predicate1 = (s)-> s.length() > 1;

Predicate<String> predicate2 = (s)-> s.contains("a");

System.out.println(predicate1.and(predicate2).test("hello"));

Function接口

接口说明

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件, 后者称为后置条件。

Function代表的是有参数,有返回值的函数。还有很多类似的Function接口:

接口名 描述

BiFunction<T,U,R> 接收两个T和U类型的参数,并且返回R类型结果的函数

DoubleFunction<R> 接收double类型参数,并且返回R类型结果的函数

IntFunction<R> 接收int类型参数,并且返回R类型结果的函数

LongFunction<R> 接收long类型参数,并且返回R类型结果的函数

ToDoubleFunction<T> 接收T类型参数,并且返回double类型结果

ToIntFunction<T> 接收T类型参数,并且返回int类型结果

ToLongFunction<T> 接收T类型参数,并且返回long类型结果

DoubleToIntFunction 接收double类型参数,返回int类型结果

DoubleToLongFunction 接收double类型参数,返回long类型结果

看出规律了吗?这些都是一类函数接口,在Function基础上衍生出的,要么明确了参数不确定返回结果,要么明确结果不知道参数类型,要么两者都知道。

抽象方法:apply

Function接口中最主要的抽象方法为:R apply(T t),根据类型T的参数获取类型R的结果。 使用的场景

例如:将String类型经过处理之后转换为String类型。

public class FunctionDemo {

public static void main(String[] args) {

String str = strOperation("helloworld", s -> s.substring(2, 5));

System.out.println(str);

}

//定义一个方法实现字符串的处理

public static String strOperation(String str, Function<String,String> function){

return function.apply(str);

}

}

默认方法:andThen

Function接口中有一个默认的 andThen方法,用来进行组合操作。JDK源代码如下:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {

Objects.requireNonNull(after);

return (T t) -> after.apply(apply(t));

}

该方法同样用于“先做什么,再做什么”的场景,和Consumer中的 andThen差不多:

apply方法中的例子:先去除字符串两端空格,再截取

/*

先去除字符串两端空格,再截取

*/

public class FunctionDemo {

public static void main(String[] args) {

String str = strOperation(" helloworld ",

s -> s.trim(),

s -> s.substring(2, 6));

System.out.println(str);

}

//定义一个方法实现字符串的处理

public static String strOperation(String str, Function<String, String> f1, Function<String, String> f2) {

return f1.andThen(f2).apply(str);

}

}

Stream API【重点】

了解Stream API

Java8中有两大最重要的改变: Lambda 表达式 + Stream API。

Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。

这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效、干净、简洁的代码。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。

简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

为什么使用Stream API

传统集合的多步遍历

几乎所有的集合(如Collection接口或 Map接口等)都支持直接或间接的遍历操作。

当我们需要对集合中的元 素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。

示例

public class DemoForEach {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

list.add("oracle");

list.add("mysql");

for (String s : list) {

System.out.println(s);

}

}

}

循环遍历的弊端

Java8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行 了对比说明。

现在,我们仔细体会上例代码,可以发现:

for循环的语法就是“怎么做”

for循环的循环体才是“做什么”

为什么使用循环?因为要进行遍历。

但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从 第一个到最后一个顺次处理的循环。

前者是目的,后者是方式。

试想一下,如果希望对集合中的元素进行筛选过滤:

将集合A根据条件一过滤为子集B;

然后再根据条件二过滤为子集C。

那怎么办?

示例:

public class DemoForEach {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

list.add("oracle");

list.add("mysql");

//for (String s : list) {

// System.out.println(s);

//}

//过滤集合包含a的字符串

List<String> newList = new ArrayList();

for (String s : list) {

if (s.contains("a")){

newList.add(s);

}

}

//过滤字符串长度为4的字符串

List<String> newList1 = new ArrayList();

for (String s : newList) {

if (s.length() == 4){

newList1.add(s);

}

}

//遍历过滤完的数据

for (String s : newList1) {

System.out.println(s);

}

}

}

Stream的更优写法

public class DemoForEach01 {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

list.add("oracle");

list.add("mysql");

list.stream()

.filter(s -> s.contains("a"))

.filter(s -> s.length()==4)

.forEach(System.out::println);

}

}

Stream操作的3步骤

创建 Stream

一个数据源(如:集合、数组),获取一个流

中间操作

一个中间操作链,对数据源的数据进行处理

终止操作(终端操作)

一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用

创建Stream的4种方式

java.util.stream.Stream<T> 是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

获取一个流非常简单,有以下几种常用的方式:

所有的Collection集合都可以通过stream默认方法获取流

Stream接口的静态方法of可以获取数组对应的流。

根据Collection获取流

java.util.Collection接口中加入了default方法stream用来获取流,所以其所有实现类均可获取流

//返回一个顺序流

default Stream<E> stream() {

return StreamSupport.stream(spliterator(), false);

}

//返回一个并行流

default Stream<E> parallelStream() {

return StreamSupport.stream(spliterator(), true);

}

public class CollectionStream {

public static void main(String[] args) {

List<String> list = new ArrayList();

Stream<String> stream = list.stream();

Set<String> set = new HashSet<>();

Stream<String> stream1 = set.stream();

Vector vector = new Vector();

Stream stream2 = vector.stream();

}

}

根据Map获取流

Map<String, String> map = new HashMap();

Stream<String> stream = map.keySet().stream();

Stream<String> stream1 = map.values().stream();

Stream<Map.Entry<String, String>> stream2 = map.entrySet().stream();

根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of

/*Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

static <T> Stream<T> stream(T[] array): 返回一个流

重载形式,能够处理对应基本类型的数组:

public static IntStream stream(int[] array)

public static LongStream stream(long[] array)

public static DoubleStream stream(double[] array)

*/

Stream<String> stream2 = Arrays.stream(names);

}

}

注意:of方法的参数其实是一个可变参数,所以支持数组

创建无限流

可以使用静态方法Stream.iterate()和Stream.generate(), 创建无限流。

迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

生成 public static<T> Stream<T> generate(Supplier<T> s)

Stream的常用方法

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。

终结操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。

流进行了终止操作后,不能再次使用

逐一处理:foreach

虽然方法名字叫forEach ,但是与for循环中的“for-each”昵称不同。

源码

default void forEach(Consumer<? super T> action) {

Objects.requireNonNull(action);

for (T t : this) {

action.accept(t);

}

}

该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。

复习Consumer接口

java.util.function.Consumer<T>接口是一个消费型接口。

Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。

示例

public class DemoForEach01 {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

list.add("oracle");

list.add("mysql");

list.stream().forEach(System.out::println);

}

}

过滤:filter

可以通过filter方法将一个流转换成另一个子集流。

源码

Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

复习Predicate接口

此前我们已经学习过java.util.stream.Predicate函数式接口,其中唯一的抽象方法为:

boolean test(T t);

该方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法 将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素。

示例:

public class DemoForEach01 {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("hello");

list.add("world");

list.add("java");

list.add("oracle");

list.add("mysql");

list.stream()

.filter(s -> s.contains("a"))

.filter(s -> s.length()==4)

.forEach(System.out::println);

}

}

映射:map

如果需要将流中的元素映射到另一个流中,可以使用map方法。

方法签名 :

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

复习Function接口

此前我们已经学习过java.util.stream.Function函数式接口,其中唯一的抽象方法为:

R apply(T t);

这可以将一种T类型转换成为R类型,而这种转换的动作,就称为“映射”。

示例:

/*

把字符串类型转为int类型

*/

public class DemoStream01 {

public static void main(String[] args) {

Stream<String> stringStream = Stream.of("10", "20", "30");

Stream<Integer> rs = stringStream.map(s -> Integer.parseInt(s));

rs.forEach(System.out::println);

}

}

统计个数:count

正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:

long count();

该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。

示例:

/*

将字符串过滤后,返回数量

*/

public class DemoStream01 {

public static void main(String[] args) {

Stream<String> stringStream = Stream.of("张无忌", "张三丰", "赵敏");

long num = stringStream.filter(s -> s.contains("张")).count();

System.out.println(num);

}

}

取前n个:limit

limit方法可以对流进行截取,只取用前n个。方法签名:

Stream<T> limit(long maxSize);

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。

示例:

/*

将字符串过滤后,返回数量

*/

public class DemoStream01 {

public static void main(String[] args) {

Stream<String> stringStream = Stream.of("张无忌", "张三丰", "赵敏");

//取前几个符合要求的数据

Stream<String> limit = stringStream.limit(2);

System.out.println(limit.count());

}

}

跳过前n个:skip

如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:

Stream<T> skip(long n);

如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。

示例:

public class DemoStream01 {

public static void main(String[] args) {

Stream<String> stringStream = Stream.of("张无忌", "张三丰", "赵敏");

//取前几个符合要求的数据

Stream<String> skip = stringStream.skip(2);

skip.forEach(System.out::println);

}

}

组合:concat

如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat :

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

备注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。

示例:

public class DemoStream01 {

public static void main(String[] args) {

Stream<String> stream1 = Stream.of("张无忌", "张三丰", "赵敏");

Stream<String> stream2 = Stream.of("周芷若", "灭绝师太");

Stream<String> concat = Stream.concat(stream1, stream2);

concat.forEach(System.out::println);

}

}

其他常用方法

distinct() 刷选,通过流所生成元素hashCode()和equals()去除重复元素

flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

sorted() 产生一个新流,其中按自然顺序排序

sorted(Comparator com) 产生一个新流,其中按比较器顺序排序

max(Comparator c) 返回流中最大值

min(Comparator c) 返回流中最小值

reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值

练习

数字处理

需求:给定一个数字列表,如何返回一个由每个数的平方构成的列表呢? 比如,给定【1,2,3,4,5】,应该返回【1,4,9,16,25】

实践应用示例

使用 Stream 处理集合数据

筛选出长度大于等于5的字符串,并打印输出:

List<String> list = Arrays.asList("apple", "banana", "orange", "grapefruit", "kiwi");

list.stream()

.filter(s -> s.length() >= 5)

.forEach(System.out::println);

输出结果:

banana

orange

grapefruit

将集合中的每个字符串转换为大写,并收集到新的列表中:

List<String> list = Arrays.asList("apple", "banana", "orange", "grapefruit", "kiwi");

List<String> resultList = list.stream()

.map(String::toUpperCase)

.collect(Collectors.toList());

System.out.println(resultList);

输出结果:

[APPLE, BANANA, ORANGE, GRAPEFRUIT, KIWI]

统计集合中以字母"a"开头的字符串的数量:

List<String> list = Arrays.asList("apple", "banana", "orange", "grapefruit", "kiwi");

long count = list.stream()

.filter(s -> s.startsWith("a"))

.count();

System.out.println(count);

输出结果:

1

使用并行流来提高处理速度,筛选出长度小于等于5的字符串,并打印输出:

List<String> list = Arrays.asList("apple", "banana", "orange", "grapefruit", "kiwi");

list.parallelStream()

.filter(s -> s.length() <= 5)

.forEach(System.out::println);

输出结果:

apple

kiwi

使用 Stream 对集合中的整数求和:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

int sum = numbers.stream()

.mapToInt(Integer::intValue)

.sum();

System.out.println(sum);

输出结果:

15

以上示例展示了如何使用 Stream 对集合数据进行筛选、转换、统计等操作。通过链式调用 Stream 的中间操作和终端操作。

使用 Stream 进行文件操作

import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Paths;

import java.util.stream.Stream;

public class FileStreamExample {

public static void main(String[] args) {

String fileName = "file.txt";

// 读取文件内容并创建 Stream

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {

// 打印文件的每一行内容

stream.forEach(System.out::println);

// 统计文件的行数

long count = stream.count();

System.out.println("总行数:" + count);

// 筛选包含关键词的行并打印输出

stream.filter(line -> line.contains("keyword"))

.forEach(System.out::println);

// 将文件内容转换为大写并打印输出

stream.map(String::toUpperCase)

.forEach(System.out::println);

// 将文件内容收集到 List 中

List<String> lines = stream.collect(Collectors.toList());

System.out.println(lines);

} catch (IOException e) {

e.printStackTrace();

}

}

}

在上面的代码中,首先指定了要读取的文件名 file.txt。然后使用 Files.lines() 方法读取文件的每一行内容,并创建一个 Stream 对象。接下来,我们对 Stream 进行一些操作:

使用 forEach() 方法打印文件的每一行内容。

使用 count() 方法统计文件的行数。

使用 filter() 方法筛选出包含关键词的行,并打印输出。

使用 map() 方法将文件内容转换为大写,并打印输出。

使用 collect() 方法将文件内容收集到 List 中。

请根据实际需求修改代码中的文件名、操作内容和结果处理方式。需要注意的是,在使用完 Stream 后,应及时关闭文件资源,可以使用 try-with-resources 语句块来自动关闭文件。另外,请处理可能出现的 IOException 异常。

使用 Stream 实现数据转换和筛选

public class StreamExample {

public static void main(String[] args) {

List<String> names = Arrays.asList("Amy", "Bob", "Charlie", "David", "Eva");

// 转换为大写并筛选出长度大于3的名称

List<String> result = names.stream()

.map(String::toUpperCase)

.filter(name -> name.length() > 3)

.collect(Collectors.toList());

// 打印结果

result.forEach(System.out::println);

}

}

在上述代码中,我们首先创建了一个包含一些名字的列表。然后使用 Stream 对列表进行操作:

使用 stream() 方法将列表转换为一个 Stream。

使用 map() 方法将每个名称转换为大写。

使用 filter() 方法筛选出长度大于3的名称。

使用 collect() 方法将筛选后的结果收集到一个新的列表中。

最后,我们使用 forEach() 方法打印结果列表中的每个名称。

方法引用

是什么

何时使用:若Lambda体中的内容有方法已经实现了,就可以使用方法引用。

方法引用就是Lambda表达式,就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。

要求:实现抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!

方法引用:使用操作符::将类(或对象) 与方法名分隔开来。

语法

总共有四类方法引用:

语法 描述

类名::静态方法名 类的静态方法的引用

类名::非静态方法名 类的非静态方法的引用

实例对象::非静态方法名 类的指定实例对象的非静态方法引用

类名::new 类的构造方法引用

示例

对象::实例方法名

@Test

public void test1() {

Consumer<String> con = str -> System.out.println(str); //原始写法

con.accept("hello");

PrintStream ps = System.out;

Consumer<String> con1 = ps::println; //对象::非静态方法

con1.accept("world");

Consumer<String> con2 = System.out::println; //上面con1的简化写法

con2.accept("abcd");

}

@Test

public void test2() {

Emp emp = new Emp();

Supplier<String> sup = () -> emp.getName();

System.out.println(sup.get());

Supplier<Integer> sup2 = emp::getAge; //对象::非静态方法

System.out.println(sup2.get());

}

类::静态方法名

//类::静态方法名

@Test

public void test3() {

Comparator<Integer> com = (x, y) ->Integer.compare(x, y);

Comparator<Integer> com2 = Integer::compare;

System.out.println(com2.compare(3, 5));

}

类::实例方法名

@Test

public void test4() {

BiPredicate<String, String> bp = (x,y) ->x.equals(y);

BiPredicate<String, String> bp2 = String::equals;

System.out.println(bp2.test("abc", "bcd"));

}

注意:若Lambda参数列表中第一个参数是实例方法的调用者,并且第二个参数是实例方法的参数(或无参数)时:ClassName::methodName

构造器引用 ClassName::new

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。

//构造器引用

@Test

public void test5() {

Supplier<Emp> emp = () -> new Emp();

Supplier<Emp> emp2 = Emp::new; //无参构造

System.out.println(emp2.get());

Function<String, Emp> emp3 = (x) -> new Emp(x); //Emp中需要 public Emp(String name)

Function<String, Emp> emp4 = Emp::new; //简化写法

System.out.println(emp3.apply("孙悟空") + "---"+emp4.apply("猪八戒"));

}

4.3.5 数组引用 type[] :: new

@Test

public void test6() {

Function<Integer, String[]> fun = (x) -> new String[x];

Function<Integer, String[]> fun2 = String[]::new;

String[] strs = fun2.apply(10);

System.out.println(strs.length);

}

jdk1.8时间相关类

Java 8引入了新的日期时间API (java.time包),以替代旧的java.util.Date和java.util.Calendar类,提供了更丰富和易用的日期时间处理功能。

以下是LocalDate-Time、LocalDate、LocalTime、LocalDateTime的基本用法概述:

LocalDateLocalDate类表示没有时间信息的日历日期,只包含年、月、日信息。它不包含时区信息,适合表示生日、纪念日等。

常用方法:

•获取当前日期:LocalDate now()

•指定日期:LocalDate of(int year, int month, int dayOfMonth)

•获取年、月、日:getYear(), getMonthValue(), getDayOfMonth()

•日期计算:plusDays(long daysToAdd),

minusDays(long daysToSubtract),

with(TemporalAdjuster adjuster)

LocalTimeLocalTime类代表一天中的时间,不含日期和时区信息。适用于表示一天中的某个时刻,如营业时间。

常用方法:

•获取当前时间:LocalTime now()

•指定时间:LocalTime of(int hour, int minute, int second)

•获取小时、分钟、秒:getHour(), getMinute(), getSecond()

•时间计算:

plusHours(long hoursToAdd),

minusMinutes(long minutesToSubtract),

with(TemporalAdjuster)

LocalDateTimeLocalDateTime类结合了日期和时间,不包含时区信息,适合表示具体的日期时间点,如会议时间。

常用方法:

•获取当前日期时间:LocalDateTime now()

•指定日期时间:LocalDateTime of(LocalDate date, LocalTime time)

•获取日期、时间:toLocalDate(), toLocalTime()

•日期时间计算:

plusDays(long daysToAdd),

minusHours(long hoursToSubtract),

with(TemporalAdjuster)

示例

public class DateTimeDemo {

public static void main(String[] args) {

// 获取当前日期

LocalDate today = LocalDate.now();

System.out.println("Today: " + today);

// 创建指定日期时间

LocalDateTime dateTime = LocalDateTime.of(2023, Month.JANUARY, 1, 1, 10, 30);

System.out.println("DateTime: " + dateTime);

// 计算明天的日期

LocalDate tomorrow = today.plusDays(1);

System.out.println("Tomorrow: " + tomorrow);

// 获取当前时间

LocalTime currentTime = LocalTime.now();

System.out.println("Current Time: " + currentTime);

// 将LocalDateTime转换为LocalDate

LocalDate localDate = dateTime.toLocalDate();

System.out.println("Date part of LocalDateTime: " + localDate);

// 更改时间

LocalTime newTime = currentTime.withHour(15).withMinute(0);

System.out.println("New Time: " + newTime);

}

}

}

标签:JDK1.8,String,Stream,list,接口,特性,println,public
From: https://blog.csdn.net/weixin_50975714/article/details/139229916

相关文章

  • ES2020新特性概览
    以下是ES2020版本中的一些新特性:Promise.allSettled:Promise.allSettled()方法返回一个在所有给定的promise已被决议或被拒绝后决议的promise,返回的promise根据每个Promise的结果状态决定其决议方式(注意:与Promise.all()不同,Promise.all()只有全部成功时才会返回成功,有一个......
  • 传感器的静态特性
    传感器的静态特性是指传感器在稳态(输入量为常量或变化极慢时)输入信号作用下,传感器输出与输入信号之间的关系。这种关系一般用曲线、数学表达式或表格来表示。传感器的静态特性是传感器的基本特性之一,其描述了传感器在不考虑迟滞、蠕变和不稳定性等因素时的输入输出关系。衡......
  • lightdb 24.1新特性
    J.1. 版本发布13.8-24.1J.1.1.Oracle兼容J.1.2.plorasql增强J.1.3.MySQL兼容J.1.4.lightdb新特性J.1.5.ltjdbc增强版本发布日期:. 2024年04月30日J.1.1. Oracle兼容支持groupby常量,即支持按常量分组聚集,其中分组字段可以有一个或多个常量字段......
  • 【Java笔记】第8章:面向对象的三大特性(封装、继承、多态)
    前言1.三大特性概述2.封装3.继承4.多态结语#include<GUIQU.h>intmain{上期回顾:【Java笔记】第7章:面向对象个人主页:C_GUIQU归属专栏:【Java学习】return一键三连;}前言各位小伙伴大家好!上期小编给大家讲解了Java中的面向对象,接下来讲讲Java中面向......
  • JavaScript 新特性:新增声明命令与解构赋值的强大功能
    个人主页:学习前端的小z个人专栏:JavaScript精粹本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论!ES5、ES6介绍文章目录......
  • Spring Boot 3.3新特性发布
    SpringBoot3.3现已正式发布!此版本包含大量更新,包括多项新功能。我们决定进行一些挑选,并查看最重要的变化,其中包括对类数据共享(CDS)的支持,以加快应用程序启动速度。1.新的服务连接SpringBoot中改进或添加了几个服务连接:增加了对ApacheActiveMQArtemis的支持;ap......
  • 管道文件的文件特性
    目录问题在/tmp目录下创建一条命名管道,命名管道的名称用户决定,然后设计两个程序要求进程A获取当前系统时间(time-->ctime)并写入到命名管道,进程B从命名管道中读取数据并存储在一个名字叫做log.txt的文本中。进程A#include<stdio.h>#include<stdlib.h>#include<unist......
  • MySQL8.0新特性CTE表达式递归实现累加运算 1+2+…+n 等于多少?
    上一篇内容,通过MySQL存储过程实现累加运算1+2+…+n等于多少的需求,使用当前主流版本MySQL5.7.x和MySQL8.0.x,以及最新的MySQL8.4LST版本。WITHAS子句在MySQL8.0.x及更高版本中得到支持,而在MySQL5.7及以下版本中则不支持。参考地址如下:https://blog.csdn.net/zxrhhm/......
  • ES 6 新特性
    ECMAScript6(简称ES6)是JavaScript语言的标准,于2015年正式发布,带来了一系列显著的语言改进和新特性。以下是一些重要的ES6新特性:let和const:let关键字用于声明块级作用域的变量,解决了JavaScript中长期以来存在的变量提升和函数作用域问题。const关键字用于声明常量,一旦初始......
  • 交直流系统潮流计算及相互关联特性分析(Matlab代码实现)
     ......