zoukankan      html  css  js  c++  java
  • Java学习:常用函数接口

    常用函数接口

    什么是函数式接口?


      函数式接口,@FunctionalInterface,简称FI,简单的说,FI就是指仅含有一个抽象方法的接口,以@Functionalnterface标注

    注意:这里的抽象方法指的是该接口自己特有的抽象方法,而不包含它从其上级继承过来的抽象方法,例如:

    @FunctionalInterface
    Interface FI{
       abstract judge(int a);
       abstract equals();      
    }

    上面这个接口尽管含有两个抽象方法,但是它仍然是一个FI,因为equals抽象方法是其从超类Object中继承的(当然这里的“接口继承超类Object”的说法很有争议,但是不妨碍咱们这里拿来理解FI这个概念)

    Java8中常用的全新的接口

    Predicate接口

    Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

    代码如下:

    Predicate<String> predicate = (s) -> s.length() > 0;
     
    predicate.test("foo");              // true
    predicate.negate().test("foo");     // false
    
    Predicate<Boolean> nonNull = Objects::nonNull;
    Predicate<Boolean> isNull = Objects::isNull;
    
    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();

    Function 接口

    Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

    代码如下:

    Function<String, Integer> toInteger = Integer::valueOf;
    Function<String, String> backToString = toInteger.andThen(String::valueOf);
    backToString.apply("123"); // "123"

    Supplier 接口

    Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

    代码如下:

    Supplier<Person> personSupplier = Person::new;
    personSupplier.get(); // new Person

    Consumer 接口

    Consumer 接口表示执行在单个参数上的操作。

    代码如下:

    1 Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
    2 greeter.accept(new Person("Luke", "Skywalker"));

    Comparator 接口

    Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:

    代码如下:

    1 Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
    2 Person p1 = new Person("John", "Doe");
    3 Person p2 = new Person("Alice", "Wonderland");
    4 
    5 comparator.compare(p1, p2); // > 0
    6 comparator.reversed().compare(p1, p2); // < 0

    Optional 接口

    Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:

    Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

    代码如下:

    1 Optional<String> optional = Optional.of("bam");
    2 optional.isPresent(); // true 3 optional.get(); // "bam" 4 optional.orElse("fallback"); // "bam"
    5 optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"


    Stream 接口

    java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

    首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

    代码如下:

    1 List<String> stringCollection = new ArrayList<>();
    2 stringCollection.add("ddd2");
    3 stringCollection.add("aaa2");
    4 stringCollection.add("bbb1");
    5 stringCollection.add("aaa1");
    6 stringCollection.add("bbb3");
    7 stringCollection.add("ccc");
    8 stringCollection.add("bbb2");
    9 stringCollection.add("ddd1");

    Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。

    Filter 过滤

    过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

    代码如下:

    stringCollection
        .stream()
        .filter((s) -> s.startsWith("a"))
        .forEach(System.out::println);
    // "aaa2", "aaa1"

    Sort 排序

    排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。

    代码如下:

    1 stringCollection
    2     .stream()
    3     .sorted()
    4     .filter((s) -> s.startsWith("a"))
    5     .forEach(System.out::println);
    6  
    7 
    8 // "aaa1", "aaa2" 

    需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:

    代码如下:

    1 System.out.println(stringCollection);
    2 // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

    注意: 其实这也是函数式编程的一个好处:不会改变对象状态,每次都会创建一个新对象。

    Map 映射

    中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

    代码如下:

    1 stringCollection
    2     .stream()
    3     .map(String::toUpperCase)
    4     .sorted((a, b) -> b.compareTo(a))
    5     .forEach(System.out::println);
    6  
    7 
    8 // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" 

    Match 匹配

    Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

    代码如下:

     1 boolean anyStartsWithA = 
     2     stringCollection
     3         .stream()
     4         .anyMatch((s) -> s.startsWith("a"));
     5  
     6 
     7 System.out.println(anyStartsWithA);      // true
     8 
     9 boolean allStartsWithA = 
    10     stringCollection
    11         .stream()
    12         .allMatch((s) -> s.startsWith("a"));
    13 
    14 System.out.println(allStartsWithA);      // false
    15 
    16 boolean noneStartsWithZ = 
    17     stringCollection
    18         .stream()
    19         .noneMatch((s) -> s.startsWith("z"));
    20 
    21 System.out.println(noneStartsWithZ);      // true 

    Count 计数

    计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

    代码如下:

    1 long startsWithB = 
    2     stringCollection
    3         .stream()
    4         .filter((s) -> s.startsWith("b"))
    5         .count();
    6  
    7 
    8 System.out.println(startsWithB);    // 3 

    Reduce 规约

    这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

    代码如下:

    Optional<String> reduced =
        stringCollection
            .stream()
            .sorted()
            .reduce((s1, s2) -> s1 + "#" + s2);
     
    
    reduced.ifPresent(System.out::println);
    // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" 

    并行Streams

    前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

    下面的例子展示了是如何通过并行Stream来提升性能:

    首先我们创建一个没有重复元素的大表:

    代码如下:

    int max = 1000000;
    List<String> values = new ArrayList<>(max);
    for (int i = 0; i < max; i++) {
        UUID uuid = UUID.randomUUID();
        values.add(uuid.toString());
    }

    然后我们计算一下排序这个Stream要耗时多久,

    串行排序:

    代码如下:

    1 long t0 = System.nanoTime();
    2  
    3 long count = values.stream().sorted().count();
    4 System.out.println(count);
    5 
    6 long t1 = System.nanoTime();
    7 
    8 long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    9 System.out.println(String.format("sequential sort took: %d ms", millis));

    // 串行耗时: 899 ms

    并行排序:

    代码如下:

    long t0 = System.nanoTime();
     
    
    long count = values.parallelStream().sorted().count();
    System.out.println(count);
    
    long t1 = System.nanoTime();
    
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("parallel sort took: %d ms", millis)); 

    // 并行排序耗时: 472 ms
    上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。

    Map

    前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

    代码如下:

    1 Map<Integer, String> map = new HashMap<>();
    2  
    3 
    4 for (int i = 0; i < 10; i++) {
    5     map.putIfAbsent(i, "val" + i);
    6 }
    7 
    8 map.forEach((id, val) -> System.out.println(val)); 

    以上代码很容易理解, putIfAbsent 不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。

    下面的例子展示了map上的其他有用的函数:

    代码如下:

    map.computeIfPresent(3, (num, val) -> val + num);
    map.get(3);             // val33
     
    map.computeIfPresent(9, (num, val) -> null);
    map.containsKey(9);     // false
    
    map.computeIfAbsent(23, num -> "val" + num);
    map.containsKey(23);    // true
    
    map.computeIfAbsent(3, num -> "bam");
    map.get(3);             // val33 

    接下来展示如何在Map里删除一个键值全都匹配的项:

    代码如下:

    map.remove(3, "val3");
    map.get(3); // val33
    
    map.remove(3, "val33");
    map.get(3); // null

    另外一个有用的方法:

    代码如下:

    map.getOrDefault(42, "not found"); // not found

    对Map的元素做合并也变得很容易了:

    代码如下:

    1 map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
    2 map.get(9); // val9
    3 
    4 map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
    5 map.get(9); // val9concat

    Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。

    函数式接口的出现,是Java8对于Lambda表达式的一种支持。

  • 相关阅读:
    DRUPAL-PSA-CORE-2014-005 && CVE-2014-3704 Drupal 7.31 SQL Injection Vulnerability /includes/database/database.inc Analysis
    WDCP(WDlinux Control Panel) mysql/add_user.php、mysql/add_db.php Authentication Loss
    Penetration Testing、Security Testing、Automation Testing
    Tomcat Server Configuration Automation Reinforcement
    Xcon2014 && Geekpwn2014
    phpMyadmin /scripts/setup.php Remote Code Injection && Execution CVE-2009-1151
    Linux System Log Collection、Log Integration、Log Analysis System Building Learning
    The Linux Process Principle,NameSpace, PID、TID、PGID、PPID、SID、TID、TTY
    Windows Management Instrumentation WMI Security Technology Learning
    IIS FTP Server Anonymous Writeable Reinforcement, WEBDAV Anonymous Writeable Reinforcement(undone)
  • 原文地址:https://www.cnblogs.com/cainiao-chuanqi/p/11331905.html
Copyright © 2011-2022 走看看