Java函数式编程整理

Java函数式编程的第一个作用是可以将匿名类改写成函数式表达式,由系统自动判断类型

我们先定义一个接口

public interface Goods {
    boolean test(int i);
}

传统的匿名类写法

public class Anonymous {
    public static void main(String[] args) {
        List<Goods> goodsList = new ArrayList<>();
        goodsList.add(new Goods(){
            @Override
            public boolean test(int i) {
                return 5 - i > 0;
            }
        });
        System.out.println(goodsList.get(0).test(3));
    }
}

运行结果:

true

使用lamdba表示式的写法

public class AnonymousLambda {
    public static void main(String[] args) {
        List<Goods> goodsList = new ArrayList<>();
        goodsList.add(i -> 5 - i > 0);
        System.out.println(goodsList.get(0).test(3));
    }
}

运行结果

true

像这种一个接口里面只有一个方法的接口,我们可以称之为函数接口,JDK中有很多这样的接口,当然我们可以在该接口上打上@FunctionalInterface以表明该接口是一个函数接口

@FunctionalInterface
public interface Goods {
    boolean test(int i);
}

我们先来看一下JDK中的Predicate接口,这是一个可以进行条件判断的接口

我们先用传统方式来看一下这么几个条件

  • 判断传入的字符串的长度是否大于5
  • 判断传入的参数是否是偶数
  • 判断数字是否大于10
public class PredicateOne {
    /**
     * 判断长度大于5
     * @param judgeString
     * @return
     */
    public boolean judgeStringLength(String judgeString) {
        return judgeString.length() > 5;
    }

    /**
     * 判断是否是偶数
     * @param judgeInteger
     * @return
     */
    public boolean judgeIntegerOdds(int judgeInteger) {
        return (judgeInteger & 1) == 0;
    }

    /**
     * 判断数字是否大于10
     * @param num
     * @return
     */
    public boolean judgeSecialNumber(int num) {
        return num > 10;
    }

    public static void main(String[] args) {
        PredicateOne predicateOne = new PredicateOne();
        System.out.println(predicateOne.judgeStringLength("12345"));
        System.out.println(predicateOne.judgeIntegerOdds(12345));
        System.out.println(predicateOne.judgeSecialNumber(12345));
    }
}

运行结果

false
false
true

现在改成函数式编程如下

public class PredicateTwo<T> {
    public boolean judgeConditionByFunction(T t, Predicate<T> predicate) {
        return predicate.test(t);
    }

    public static void main(String[] args) {
        PredicateTwo<Integer> integerPredicateTwo = new PredicateTwo<>();
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> String.valueOf(t).length() > 5));
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> (t & 1) == 0));
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> t > 10));
    }

运行结果

false
false
true

我们知道逻辑判断中有与、或、非的比较,Predicate接口同样具有这样的功能,我们来看一下它的源码

@FunctionalInterface
public interface Predicate<T> {

    /**
     * 需要实现的比较方法
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * 逻辑与,类似于&&
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 逻辑非,类似于!
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 逻辑或,类似于||
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 逻辑等,类似于equals()方法
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

现在我们加上条件的交集判断

public class PredicateThree<T> {
    /**
     * 两个条件的与操作
     * @param t
     * @param predicate
     * @param predicate1
     * @return
     */
    public boolean judgeConditionByFunctionAnd(T t, Predicate<T> predicate,Predicate<T> predicate1) {
        return predicate.and(predicate1).test(t);
    }

    /**
     * 两个条件的或操作
     * @param t
     * @param predicate
     * @param predicate1
     * @return
     */
    public boolean judgeConditionByFunctionOr(T t, Predicate<T> predicate,Predicate<T> predicate1) {
        return predicate.or(predicate1).test(t);
    }

    /**
     * 对一个条件进行取反
     * @param t
     * @param predicate
     * @return
     */
    public boolean judgeConditionByFunctionNegate(T t,Predicate<T> predicate) {
        return predicate.negate().test(t);
    }

    /**
     * 判断两个对象的值是否相等
     * @param t1
     * @param t2
     * @return
     */
    public boolean judgeConditionIsEquals(T t1,T t2) {
        return Predicate.isEqual(t1).test(t2);
    }

    public static void main(String[] args) {
        PredicateThree<Integer> integerPredicateThree = new PredicateThree<>();
        System.out.println(integerPredicateThree.judgeConditionByFunctionAnd(12345,t -> t > 10,t ->(t & 1) == 0));
        System.out.println(integerPredicateThree.judgeConditionByFunctionOr(12345,t -> t > 10,t ->(t & 1) == 0));
        System.out.println(integerPredicateThree.judgeConditionByFunctionNegate(12345,t -> t > 10));
        System.out.println(integerPredicateThree.judgeConditionIsEquals(123,123));
    }
}

运行结果

false
true
false
true

我们再来看一下JDK中的Consumer接口,Consumer的作用是 给定义一个参数,对其进行(消费)处理,处理的方式可以是任意操作.

我们来获取一群人中名字为lisa的所有人,传统方式如下

@Data
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
}
public class ConsumerOne {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        Consumer<Person> consumer = new Consumer<Person>() {
            @Override
            public void accept(Person person) {
                if (person.getName().equals("lisa")) {
                    personList.add(person);
                }
            }
        };
        List<Person> list = Arrays.asList(
                new Person("lisa", 23),
                new Person("Mike", 33),
                new Person("lisa", 27),
                new Person("Jack", 25)
        );
        for (Person person : list) {
            consumer.accept(person);
        }
        System.out.println(JSON.toJSONString(personList));
    }

}

运行结果

[{"age":23,"name":"lisa"},{"age":27,"name":"lisa"}]

改写成函数式编程如下

public class ConsumerTwo {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        Consumer<Person> consumer = t -> {
            if (t.getName().equals("lisa")) {
                personList.add(t);
            }
        };
        Arrays.asList(
                new Person("lisa", 23),
                new Person("Mike", 33),
                new Person("lisa", 27),
                new Person("Jack", 25)
        ).stream().forEach(consumer);
        System.out.println(JSON.toJSONString(personList));
    }
}

运行结果

[{"age":23,"name":"lisa"},{"age":27,"name":"lisa"}]

stream后面会全面讲解,我们来看一下Consumer接口的源码

@FunctionalInterface
public interface Consumer<T> {

    /**
     * 给指定的参数t执行定义的操作
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     *  对给定的参数t执行定义的操作执行再继续执行after定义的操作 
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

现在我们只需要大于25岁的lisa

public class ConsumerThree {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        Consumer<Person> consumer = t -> {
            if (t.getName().equals("lisa")) {
                personList.add(t);
            }
        };
        consumer = consumer.andThen(t -> personList.removeIf(x -> x.getAge() < 25));
        Arrays.asList(
                new Person("lisa", 23),
                new Person("Mike", 33),
                new Person("lisa", 27),
                new Person("Jack", 25)
        ).stream().forEach(consumer);
        System.out.println(JSON.toJSONString(personList));
    }
}

运行结果

[{"age":27,"name":"lisa"}]

这里removeIf()的参数是一个Predicate接口,它的源码如下,它是在Collection接口源码中

default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}

我们再来看一下JDK中的Function接口,Function的作用是将一个给定的对象进行加工,然后返回加工后的对象,这个加工可以是任何操作.

传统方式如下

@Data
@AllArgsConstructor
@ToString
public class Person {
    private String name;
    private int age;
}
@AllArgsConstructor
@Data
@ToString
public class Result {
    private String msg;
    private String code;
    private Person person;
}
public class FunctionOne {
    public static void main(String[] args) {
        Function<Person,Result> function = new Function<Person, Result>() {
            @Override
            public Result apply(Person person) {
                return new Result("成功","200",person);
            }
        };
        System.out.println(function.apply(new Person("lisa",24)));
    }
}

运行结果

Result(msg=成功, code=200, person=Person(name=lisa, age=24))

改写成函数式编程如下

public class FunctionTwo {
    public static void main(String[] args) {
        Function<Person,Result> function = x -> new Result("成功","200",x);
        System.out.println(function.apply(new Person("lisa",24)));
    }
}

运行结果

Result(msg=成功, code=200, person=Person(name=lisa, age=24))

Function接口的源码如下

@FunctionalInterface
public interface Function<T, R> {

    /**
     *  将一个给定的对象进行加工,然后返回加工后的对象,可以将该方法理解为一个一维函数,参数R是自变量,参数T是因变量. 
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     *  组合函数,在调用当前function之前执行 
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    /**
     *  组合函数,在调用当前function之后执行 
     *
     * @param <V> the type of output of the {@code after} function, and of the
     *           composed function
     * @param after the function to apply after this function is applied
     * @return a composed function that first applies this function and then
     * applies the {@code after} function
     * @throws NullPointerException if after is null
     *
     * @see #compose(Function)
     */
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    /**
     *  原函数,返回与参数一致的函数,即可以理解为 y = x 
     *
     * @param <T> the type of the input and output objects to the function
     * @return a function that always returns its input argument
     */
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

实例代码如下

public class FunctionThree {
    public static void main(String[] args) {
        Function<Integer,Integer> x2 = x -> x * 2;
        Function<Integer,Integer> square = x -> x * x;
        //先计算3*3=9,再计算9*2=18
        Integer result = x2.compose(square).apply(3);
        System.out.println(result);
        //先计算3*2=6,再计算6*6=36
        Integer other = x2.andThen(square).apply(3);
        System.out.println(other);
        //返回3
        Object apply = Function.identity().apply(3);
        System.out.println(apply);
    }
}

运行结果:

18
36
3

我们再来看一下JDK中的 Supplier 接口, Supplier 接口是相对于Conumer接口的, 这个接口是一个提供者的意思,只有一个get的方法,没有默认的方法以及静态的方法,传入一个泛型T的get方法,返回一个泛型T,一般可以理解为创建对象的工厂。

传统写法

public class SupplierOne {
    public static void main(String[] args) {
        Supplier<Person> supplier = new Supplier<Person>() {
            @Override
            public Person get() {
                return new Person("张三",23);
            }
        };
        System.out.println(supplier.get());
    }
}

运行结果

Person(name=张三, age=23)

函数式编程写法

public class SupplierTwo {
    public static void main(String[] args) {
        Supplier<Person> supplier = () -> new Person("张三",23);
        System.out.println(supplier.get());
    }
}

运行结果

Person(name=张三, age=23)

Supplier接口的源码非常简单

@FunctionalInterface
public interface Supplier<T> {

    /**
     * 获取一个结果
     *
     * @return a result
     */
    T get();
}

我们再来看一下JDK的 UnaryOperator 接口,该接口是继承于Function接口,其主要作用是返回一个跟参数对象加工后相同类的对象。

我们给Person类添加克隆方法,显然这是一个浅克隆。

@Data
@AllArgsConstructor
@ToString
public class Person implements Cloneable {
    private String name;
    private int age;

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}

现在我们要通过一个Persion类的对象返回这个对象的克隆

传统方法

public class UnaryOperatorOne {
    public static void main(String[] args) {
        UnaryOperator<Person> operator = new UnaryOperator<Person>() {
            @Override
            public Person apply(Person person) {
                try {
                    return person.clone();
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };
        System.out.println(operator.apply(new Person("张三",23)));
    }
}

运行结果

Person(name=张三, age=23)

函数式编程写法

public class UnaryOperatorTwo {
    public static void main(String[] args) {
        UnaryOperator<Person> operator = x -> {
            try {
                return x.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        };
        System.out.println(operator.apply(new Person("张三",23)));
    }
}

运行结果

Person(name=张三, age=23)

UnaryOperator接口的源码如下,因为是继承了Function接口,所以它同样有compose和andThen默认方法

@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {

    /**
     * 返回一个跟参数对象一模一样的对象
     *
     * @param <T> the type of the input and output of the operator
     * @return a unary operator that always returns its input argument
     */
    static <T> UnaryOperator<T> identity() {
        return t -> t;
    }
}

实例代码如下

public class UnaryOperatorThree {
    public static void main(String[] args) {
        UnaryOperator<Integer> operator = x -> x * 2;
        UnaryOperator<Integer> square = x -> x * x;
        Integer apply = operator.compose(square).apply(3);
        System.out.println(apply);
        Integer result = operator.andThen(square).apply(3);
        System.out.println(result);
        System.out.println(UnaryOperator.identity().apply(new Person("张三",23)));
    }
}

运行结果

18
36
Person(name=张三, age=23)

我们再来看一下JDK的 BinaryOperator 接口,它跟UnaryOperator类似,是两个相同类型的参数对象,返回一个该类型的结果对象

传统方式如下

public class BinaryOperatorOne {
    public static void main(String[] args) {
        BinaryOperator<String> operator = new BinaryOperator<String>() {
            @Override
            public String apply(String s, String s2) {
                return new StringBuilder(s).append(s2).toString();
            }
        };
        System.out.println(operator.apply("张三","李四"));
    }
}

运行结果

张三李四

函数式编程如下

public class BinaryOperatorTwo {
    public static void main(String[] args) {
        BinaryOperator<String> operator = (x,y) -> new StringBuilder(x).append(y).toString();
        System.out.println(operator.apply("张三","李四"));
    }
}

BinaryOperator接口的源码如下,继承于BiFunction接口,有一个andThen方法,该方法的参数是一个Function接口。而它本身的静态方法minBy以及maxBy的参数则使用了Comparator接口

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    /**
     * 获取更小的值
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the lesser of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /**
     * 获取更大的值
     *
     * @param <T> the type of the input arguments of the comparator
     * @param comparator a {@code Comparator} for comparing the two values
     * @return a {@code BinaryOperator} which returns the greater of its operands,
     *         according to the supplied {@code Comparator}
     * @throws NullPointerException if the argument is null
     */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

代码示例

public class BinaryOperatorThree {
    public static void main(String[] args) {
        BinaryOperator<Integer> add = (x,y) -> x + y;
        Function<Integer,Integer> multiplication = x -> x * 30;
        //先计算3+6=9,再计算9*30=270
        Integer result = add.andThen(multiplication).apply(3,6);
        System.out.println(result);
        BinaryOperator<Integer> minBy = BinaryOperator.minBy((x,y) -> x - y);
        System.out.println(minBy.apply(3,6));
        BinaryOperator<Integer> maxBy = BinaryOperator.maxBy((x,y) -> x - y);
        System.out.println(maxBy.apply(3,6));
    }
}

运行结果

270
3
6

----------------------------------------------------------------------------------

而另一个最主要的作用就是对集合的操作,从传统编程到函数式编程(lambda),我们先来看一个最简单的例子

@Data
@AllArgsConstructor
public class Artist {
    private String name;
    private String homeTown;

    public boolean isFrom(String homeTown) {
        if (this.homeTown.equals(homeTown)) {
            return true;
        }
        return false;
    }
}

这是一个艺术家的简单类,包含名字和家乡。

现在我们来创建一个艺术家的集合,然后判定来自伦敦的艺术家有多少人

public class ComeFrom {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        int count = 0;
        for (Artist artist : artists) {
            if (artist.isFrom("London")) {
                count++;
            }
        }
        System.out.println(count);
    }
}

运行结果:

2

现在我们来改造成函数式的编程

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        long count = artists.stream().filter(artist -> artist.isFrom("London"))
                .count();
        System.out.println(count);
    }
}

运行结果

2

这里第一种方式,我们称为外部迭代;而第二种方式,我们称为内部迭代,而stream是这种用函数式编程方式在集合类上进行复杂操作的工具。

如果我们对代码稍作修改

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        artists.stream().filter(artist -> {
            System.out.println(artist.getName());
            return artist.isFrom("London");
        });
    }
}

执行该端代码,没有任何输出。像这种只过滤不计数的,filter只刻画出了Stream,但并没有产生新的集合的方法,我们称之为惰性求值方法;而像count这样最终会从Stream产生值的方法叫做及早求值方法。

我们再把上述代码改回求值。

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        long count = artists.stream().filter(artist -> {
            System.out.println(artist.getName());
            return artist.isFrom("London");
        }).count();
        System.out.println(count);
    }
}

运行结果

Martin
Shirly
Dilon
Dave
2

判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是Stream,那么是惰性求值;如果返回值是另一个值或为空,那么就是及早求值。整个过程和建造者模式有共通之处。建造者模式使用一系列操作设置属性和配置,最后调用build方法,这时,对象才被真正创建。

Java的流式编程里面很多都是使用很多的惰性求值,最后来一个及早求值,得到我们所需要的结果,而流式方法的参数基本上都是函数式接口。

我们先来说明一些常用的流操作。

collect(toList())方法是由stream里的值生成一个列表,是一个及早求值操作,of()方法是使用一组初始值生成新的Stream.

public class CollectTest {
    public static void main(String[] args) {
        //通用方法,首先由列表生成一个Stream,然后再进行Stream惰性操作,最后求值操作collect()
        List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList());
        System.out.println(assertEquals(Arrays.asList("a","b","c"),collected));
        System.out.println(collected);
    }
    public static boolean assertEquals(Collection a,Collection b) {
        return a.equals(b);
    }
}

运行结果

true
[a, b, c]

map操作可以将一种类型的值转换成另一种类型,将一个流中的值转换成一个新的流。

比方说我们要将一个字符列表里的字符串全部转换成大写,传统写法如下

public class MapOne {
    public static void main(String[] args) {
        List<String> collected = new ArrayList<>();
        for (String string : Arrays.asList("a","b","hello")) {
            String upperCase = string.toUpperCase();
            collected.add(upperCase);
        }
        System.out.println(collected);
    }
}

运行结果

[A, B, HELLO]

函数式编程如下

public class MapTwo {
    public static void main(String[] args) {
        List<String> collect = Stream.of("a", "b", "hello").map(x -> x.toUpperCase()).collect(Collectors.toList());
        System.out.println(collect);
    }
}

这里需要注意的是map方法的参数是一个function的函数接口,之前我们说了function接口加工一个类型的对象转变成另一个类型的对象,当然这两种类型也可以相同,这里就都是String类型的对象。它将("a", "b", "hello")转换成新的流("A", "B", "HELLO"),当然它是一个惰性求值,我们必须使用及早求值的collect()方法来获取我们要的真正的列表。

运行结果

[A, B, HELLO]

filter操作可以在遍历数据并检查其中的元素时,找出符合规则的元素,它的参数也是一个函数式接口 Predicate接口 ,该接口为一个进行条件判断的接口。filter也是一个惰性求值,流式编程中惰性求值非常多。这里比如说我们要获取一个字符串列表中首字符为数字的所有字符串。

传统方式如下

public class FilterOne {
    public static void main(String[] args) {
        List<String> beginWithNumbers = new ArrayList<>();
        for (String string : Arrays.asList("a","1abc","abc1","2bmw")) {
            if (Character.isDigit(string.charAt(0))) {
                beginWithNumbers.add(string);
            }
        }
        System.out.println(beginWithNumbers);
    }
}

运行结果

[1abc, 2bmw]

函数式编程如下

public class FilterTwo {
    public static void main(String[] args) {
        List<String> collect = Stream.of("a", "1bac", "abc1", "2bmw").filter(x -> Character.isDigit(x.charAt(0)))
                .collect(Collectors.toList());
        System.out.println(collect);
    }
}

运行结果

[1bac, 2bmw]

转载于:https://my.oschina.net/u/3768341/blog/3055774

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值