Java8编程实践

一、关于Lambda表达式

public static void main(String[] args) {
        Runnable noArguments = ()-> System.out.println("没有参数的lambda");
        noArguments.run();

        ActionListener oneArguments = event -> System.out.println("有一个参数");

        Runnable multiStatement = () -> {
            System.out.println("有多条执行语句");
        };
        multiStatement.run();

        // 包含了多个参数
        BinaryOperator<Long> add = (x,y) -> x + y;
        Long result = add.apply(1L,2L);
        System.out.println(result);

        BinaryOperator<Long> addExcept = (Long x,Long y) -> x * y;
        Long value = addExcept.apply(1L,3L);
        System.out.println(value);


    }

1.2 关于函数接口

函数接口只有一个抽象方法的接口,用作Lambda表达式的类型。

常用的函数接口

class Student {

    String firstName;

    String lastName;

    Double grade;

    Double feeDiscount = 0.0;

    Double baseFee = 20000.0;

    public Student(String firstName, String lastName, Double grade) {

        this.firstName = firstName;

        this.lastName = lastName;

        this.grade = grade;
    }

    public void printFee() {

        Double newFee = baseFee - ((baseFee * feeDiscount) / 100);

        System.out.println("The fee after discount: " + newFee);

    }
}

class PreidcateConsumerDemo {
    public static Student updateStudentFee(Student student, Predicate<Student> predicate, Consumer<Student> consumer) {
        if (predicate.test(student)) {
            consumer.accept(student);
        }
        return student;
    }
}

public static void main(String[] args) {
        Student student1 = new Student("Ashok","Kumar", 9.5);
        student1 = PreidcateConsumerDemo.updateStudentFee(student1,student -> student.grade > 8.5,student -> student.feeDiscount=30.0);
        student1.printFee();

    }

  • Predicate<T> 判断输入的对象是否符合某个条件。
  • Consumer<T> 接受一个输入参数并没有返回值。
  • Function<T,R> 获得Artist对象的方法.作为一个参数传递进去
  • Supplier<T> 工厂方法

public class FunctionDemo {
    static void modifyTheValue(int valueToBeOperated,Function<Integer,Integer> function) {
        int newValue = function.apply(valueToBeOperated);
        System.out.println(newValue);
    }

    public static void main(String[] args) {
        int incr = 20;
        int myNumber = 10;
        modifyTheValue(myNumber,val -> val + incr);
    }
}


二、关于流

对核心类库的改进主要包括集合类的API和新引入的流。流可以让程序员站在更高的抽象层次对集合进行操作。

2.1 从外部迭代到内部迭代

我们在使用集合类时,一个通用的处理模式就是在集合上进行迭代,然后处理返回的每一个元素。比如:

public static void main(String[] args) {
        List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);

        int count = 0;
        for (Integer item:allNum
             ) {
            if (item > 10) {
                count ++;
            }
        }
        System.out.println(count);
    }

循环遍历这个集合,然后逐个处理里面的元素。

这段代码的背后原理其实是:首先调用了Iterator方法,产生了一个新的iterator对象,进而控制整个迭代过程。这就是外部迭代。代码示例如下:

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);
        int count = 0;
        Iterator<Integer> iterator = allNum.iterator();
        while (iterator.hasNext()) {
            Integer item = iterator.next();
            if (item > 10){
                count ++;
            }
        }
        System.out.println(count);


外部迭代本质上来讲是一种串行化操作。使用for循环会将行为和方法混为一谈。

另外一种方法就是使用内部迭代。

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);
        long count = allNum.stream().filter(item -> item > 10).count();
        System.out.println(count);

Stream是用函数式编程方式在集合类上进行复杂操作的工具。

.filter  表示需要对stream对象进行过滤;过滤是指“只保留通过某项测试的对象”。测试由一个函数完成。返回true或false. 用stream编程并没有改变集合里面的内容,而是描述出stream里面的内容。count() 计算给定stream里面包含了多少个对象。

2.2 实现机制

allNum.stream().filter(item -> {
            System.out.println("this num" + item);
            return item > 10;
        });

这里并不会输出东西。因为是使用了惰性求值,所以并不会输出东西。

但这样就会输出:

allNum.stream().filter(item -> {
            System.out.println("this num" + item);
            return item > 10;
        }).count();
因为.count() 会拥有一个终止操作的流。这样名字就会被输出出来。

判断一个操作是惰性求值或及早求值很简单:只看它的返回值。如果返回值是Stream,那就是惰性求值,如果返回值是另一个值或为空。那就是及早求值。

使用这些操作的理想方式就是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果。

整个过程和建造者模式有共通之处,建造者模式使用一系列操作设置属性和配置,最后调用一个build方法,这时真正的对象才会创建起来。

2.3 常见的流操作

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);
        List<Integer> collected = allNum.stream().collect(Collectors.toList());
这个里面的collect(toList)方法由Stream里的值生成一个列表,是一个及早求值操作。因为它会返回一个List出来所以是一个及早求值操作。

通用的玩法是:首先由列表生成一个Stream,然后进行一些Stream上的操作,继而是collect操作,由Stream生成列表,

2.4 map

如果有一个函数可以将一种类型的值转换成另外一种类型,map操作就可以使用该函数,将一个流中的值转换成一个新的流。

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);

        List<Integer> mapResult = allNum.stream().map(item -> item + 100).collect(Collectors.toList());

通过使用map可以把里面涉及到的元素转换一下。类型变化一下。

map的操作跟python里面的类似。就是针对一个列表中各个元素要做的操作。

2.5 filter

遍历数据并检查其中的元素,可以用filter方法。过滤出来指定条件的元素。

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);
        List<Integer> filterResult = allNum.stream().filter(item -> item > 10).collect(Collectors.toList());//过滤出来大于10的元素拼成新集合
2.6 max和min

Stream上常用的操作之一就是求最大值和最小值。可以使用max与min。

List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);
        Integer minResult= allNum.stream().min(Comparator.comparing(item -> item)).get();
2.7 reduce操作
这个操作可以实现从一组值中生成一个值,像count,min,max方法都是reduce操作。
int count1 = allNum.stream().reduce(0,(acc,element) -> acc+element);


2.8 整合操作
List<Integer> allNum = new ArrayList<>();
        allNum.add(1);
        allNum.add(11);
        allNum.add(12);


        Set<Integer> zh = allNum.stream().filter(item -> item > 10).map(item -> item + 10).collect(Collectors.toSet());
是一串组合操作:过滤出来大于10的元素,然后逐个应用规则,再做集合收集变成一个新的集合。
这是一个链式操作。
String str = "aba,edf,hello,end,nihao";
List<String> value = Arrays.stream(str.split(",")).filter(item->item.startsWith("n")).map(item->item.toUpperCase()).collect(Collectors.toList());

熟悉python的语法的看到这个不会陌生的.

针对map对象的流操作:

final long currentTime = System.currentTimeMillis();
        List<String> cleanIdList = new ArrayList<>();
        Map<String/*batchId*/,Long/*timestamp*/> batch2timeMap = new HashMap<>();
        batch2timeMap.put("b-1",System.currentTimeMillis());
        batch2timeMap.put("b-2",System.currentTimeMillis());
        batch2timeMap.put("b-3",System.currentTimeMillis());
        batch2timeMap.put("b-4",System.currentTimeMillis());
        batch2timeMap.entrySet().stream().filter(e -> currentTime - e.getValue() > 3).forEach(e -> cleanIdList.add(e.getKey()));
        cleanIdList.forEach(id -> {
            System.out.println(id);
        });

2.9 正确使用Lambda表达式
回调函数是一个合法的Lambda表达式。

第四章节:类库

Java8的一个变化是引入了默认方法和接口的静态方法,它改变了人们认识类库的方法,接口中的方法也可以包含代码体。

关于基本类型

像int是基本类型,而像Integer是装箱类型。Java的泛型是基于对泛型参数的擦除,假设它是Object对象的实例---只有装箱类型才能作用泛型参数

装箱类型是对象,因此在内存里面中存在额外的开销。一个int占4个字节,而一个Integer要占16个字节。如果是数组就更加严重了。同样的大小一个Integer[]要比int[]大6倍。

装箱与拆箱都需要额外的计算开销。所以我们在写代码的时候尽量避免这种装箱与拆箱操作,减少不必要的内存占用与计算消耗。

为了避免出现这种自动装箱与拆箱操作。Stream提供了一些方法如:mapToLong

ToLongFunction(T -> long)

long -> T (LongFunction)

4.3 重载解析

private static void A(Object a){
        System.out.println("this is object");
    }

    private static void A(String a){
        System.out.println("this is string");
    }

    public static void main(String[] args) {
        A("a");
        A(new Integer(1));
    }

BinaryOperator是一种特殊的BiFunction类型,参数的类型和返回值的类型相同。比如两个整数相加就是一个BinaryOperator.










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值