Java8 常用方法

LZC發表於2020-04-29

compute

V compute(K key, BiFunction < ? super K, ? super V, ? extends V> remappingFunction)

指定的key在map中的值進行操作,不管存不存在,操作完成後儲存到map中

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    map.put("1",1);
    map.put("2",2);
    map.compute("1", (k,v)-> {
        System.out.println("key = " + k + ", value = " + v);
        if (v != null) {
            return v+1;
        } else {
            return 0;
        }
    });
    map.compute("3", (k,v)->{
        System.out.println("key = " + k + ", value = " + v);
        if (v != null) {
            return v+1;
        } else {
            return 0;
        }
    });
    System.out.println(map.toString());
}

控制檯列印

key = 1, value = 1
key = 3, value = null
{1=2, 2=2, 3=0}

computeIfAbsent

V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

computeIfAbsent的方法有兩個引數 第一個是所選map的key,第二個是需要做的操作。這個方法當key值不存在時才起作用。

當key存在返回當前value值,不存在執行函式並儲存到map中。

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<>();
    map.put("1",1);
    map.put("2",2);
    // key存在返回value
    Integer integer1 = map.computeIfAbsent("2", key -> new Integer(10));
    // key不存在執行函式存入
    Integer integer2 = map.computeIfAbsent("3", key -> new Integer(20));
    System.out.println("integer1 = " + integer1);
    System.out.println("integer2 = " + integer2);
    System.out.println(map.toString());
}

控制檯列印

integer1 = 2
integer2 = 20
{1=1, 2=2, 3=20}

computeIfPresent

V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

對指定的 在map中已經存在的key的value進行操作。只對已經存在key的進行操作,其他不操作

public static void main(String[] args) {
    HashMap<String,Integer> map = new HashMap<>();
    map.put("1",1);
    map.put("2",2);
    map.put("3",3);
    // 只對 map 中存在的key對應的value進行操作
    Integer integer1 = map.computeIfPresent("3", (k,v) -> {
        System.out.println("key = " + k + ", value = " + v);
        return v+1;
    });
    Integer integer2 = map.computeIfPresent("4", (k,v) -> {
        System.out.println("key = " + k + ", value = " + v);
        return v+1;
    } );
    System.out.println("integer1 = " + integer1);
    System.out.println("integer2 = " + integer2);
    System.out.println(map.toString());
}

控制檯列印

key = 3, value = 3
integer1 = 4
integer2 = null
{1=1, 2=2, 3=4}

of(T value)

Student student = new Student();
// 如果 student 為 null,則會丟擲NullPointerException
// 適用於 student 不為空的情況
Optional<Student> opt = Optional.of(student);

ofNullable(T value)

Student student = new Student();
// student 為 null 時不會報錯
Optional<User> opt = Optional.ofNullable(user);
// 若 student 不為空,則返回true,否則返回false
System.out.println(opt.isPresent());

獲取物件值

Student student = new Student();
// student 為 null 時不會報錯
Optional<User> opt = Optional.ofNullable(user);
// 若 student 不為空,則返回true,否則返回false
System.out.println(opt.isPresent());
// 若 student 為 null, opt.get() 會報錯
// 若 student 不為null,則返回物件值
System.out.println(opt.get());

檢查是否有值的另一個選擇是 ifPresent() 方法。該方法除了執行檢查,還接受一個Consumer(消費者) 引數,如果物件不是空的,就對執行傳入的 Lambda 表示式。

Student student = new Student();
Optional<Student> opt = Optional.ofNullable(student);
// 只有當 student 不為空時才會執行 ifPresent 方法裡面的表示式
opt.ifPresent(u -> {
    System.out.println("username = " + u.getUsername());
});

返回預設值

orElse(T other)

Student student1 = null;
Student student2 = new Student();
student2.setUsername("lizhencheng");
Student optStudent = Optional.ofNullable(student1).orElse(student2);
System.out.println(optStudent.getUsername());

這裡 student1物件是空的,所以返回了作為預設值的 student2。

如果物件的初始值不是 null,那麼預設值會被忽略:

Student student1 = new Student();
student1.setUsername("lzc");
Student student2 = new Student();
student2.setUsername("lizhencheng");
Student optStudent = Optional.ofNullable(student1).orElse(student2);
System.out.println(optStudent.getUsername());

orElseGet(Supplier<? extends T> other)

這個方法會在有值的時候返回值,如果沒有值,它會執行作為引數傳入的 Supplier(供應者) 函式式介面,並將返回其執行結果:

Student student1 = new Student();
student1.setUsername("lzc");
Student optStudent = Optional.ofNullable(student1).orElseGet(()->{
    Student student2 = new Student();
    student2.setUsername("lizhencheng");
    return student2;
});
// 如果 student1 為 null,控制檯則列印 "lzc" ,否則列印 "lizhencheng"
System.out.println(optStudent.getUsername());

orElse() 和 orElseGet() 的不同之處

public class Main {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setUsername("lzc");

        System.out.println("orElse......");
        Student optStudent1 = Optional.ofNullable(student1).orElse(createNewStudent());
        System.out.println("orElseGet......");
        Student optStudent2 = Optional.ofNullable(student1).orElseGet(()-> createNewStudent());

        System.out.println(optStudent1.getUsername());
        System.out.println(optStudent2.getUsername());
    }

    public static Student createNewStudent() {
        System.out.println("createNewStudent......");
        Student student = new Student();
        student.setUsername("lizhencheng");
        return student;
    }
}

控制檯列印結果

orElse......
createNewStudent......
orElseGet......
lzc
lzc

這個示例中,兩個 Optional 物件都包含非空值,兩個方法都會返回對應的非空值。不過,orElse() 方法仍然建立了 User 物件。與之相反,orElseGet() 方法不建立 User 物件。在執行較密集的呼叫時,比如呼叫 Web 服務或資料查詢,這個差異會對效能產生重大影響。

返回異常

Student student = null;
Student optStudent = Optional.ofNullable(student).orElseThrow(() -> new IllegalArgumentException("引數不能為空"));

如果 student值為 null,會丟擲 IllegalArgumentException。

這個方法讓我們有更豐富的語義,可以決定丟擲什麼樣的異常,而不總是丟擲 NullPointerException。

轉換值

Student student = new Student("lzc","1班");
String str = Optional
                .ofNullable(student)
                .map(s -> s.getUsername())
                .orElse("lizhencheng");
System.out.println(str);

過濾值

Student student = new Student("lzc","1班");
Optional<Student> temp = Optional
    .ofNullable(student)
    .filter(s -> s.getUsername() != null && s.getUsername().length() >= 3);
System.out.println(temp.isPresent());

forEach

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

遍歷列表元素

List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.forEach(s -> System.out.println(s));

filter

Stream<T> filter(Predicate<? super T> predicate)
List<String> list = new ArrayList<>();
list.add("123");
list.add("456789");
List<String> resultList = list
    .stream()
    .filter(s -> s.length() > 3)
    .collect(Collectors.toList());
resultList.forEach(s -> System.out.println(s));

Collectors.toList()可以把流轉換為 List 型別或者是Set型別

distinct

Stream<T> distinct()

去除重複元素,這個方法是透過類的 equals 方法來判斷兩個元素是否相等的

List<String> list = new ArrayList<>();
list.add("123");
list.add("123");
list.add("456789");
List<String> resultList = list
    .stream()
    .distinct()
    .collect(Collectors.toList());
resultList.forEach(s -> System.out.println(s));

sorted

Stream<T> sorted()

Stream<T> sorted(Comparator<? super T> comparator)

如果流中的元素的類實現了 Comparable 介面,即有自己的排序規則,那麼可以直接呼叫 sorted() 方法對元素進行排序。

方法一

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));

List<Student>  resultList = list
    .stream()
    .sorted((s1,s2) -> s1.getAge() - s2.getAge())
    .collect(Collectors.toList());

resultList.forEach(s -> System.out.println(s.toString()));

方法二

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));

List<Student>  resultList = list
                .stream()
                //.sorted(Comparator.comparing(Student::getAge)) // 升序
                .sorted(Comparator.comparing(Student::getAge).reversed()) // 降序
                .collect(Collectors.toList());

resultList.forEach(s -> System.out.println(s.toString()));

limit

Stream<T> limit(long maxSize)

返回前 n 個元素

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));

List<Student>  resultList = list
                .stream()
                .limit(2)
                .collect(Collectors.toList());

resultList.forEach(s -> System.out.println(s.toString()));

skip

Stream<T> skip(long n)

去除前 n 個元素

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));

List<Student>  resultList = list
                .stream()
                .skip(2)
                .collect(Collectors.toList());

resultList.forEach(s -> System.out.println(s.toString()));

map

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

將流中的每一個元素 T 對映為 R(類似型別轉換)

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));

List<String>  resultList = list
                .stream()
                .map(s -> {
                    String username = s.getUsername();
                    return username;
                }).collect(Collectors.toList());

resultList.forEach(s -> System.out.println(s.toString()));

flatMap

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

將流中的每一個元素 T 對映為一個流,再把每一個流連線成為一個流

List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");

List<String> resultList = list
                .stream()
                .map(s -> s.split(" "))
                .flatMap(Arrays::stream).collect(Collectors.toList());

resultList.stream().forEach(s -> System.out.println(s));

上面例子中,我們的目的是把 List 中每個字串元素以” “分割開,變成一個新的 List。
首先 map 方法分割每個字串元素,但此時流的型別為 Stream<String[ ]>,因為 split 方法返回的是 String[ ] 型別;所以我們需要使用 flatMap 方法,先使用Arrays::stream將每個 String[ ] 元素變成一個 Stream 流,然後 flatMap 會將每一個流連線成為一個流,最終返回我們需要的 Stream。

anyMatch

boolean anyMatch(Predicate<? super T> predicate)

流中是否有一個元素匹配給定的 T -> boolean 條件

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",20));
list.add(new Student("wangwu",18));
// 是否存在一個 Student 物件的 age >= 20
boolean b = list.stream().anyMatch(s -> s.getAge() >= 20);
System.out.println(b);

allMatch

boolean allMatch(Predicate<? super T> predicate)

流中是否所有元素都匹配給定的 T -> boolean 條件

collect

<R, A> R collect(Collector<? super T, A, R> collector)

joining

joining 連線字串

對流裡面的字串元素進行連線

List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");
String collect = list.stream().collect(Collectors.joining(","));
System.out.println(collect);

toList/toSet

toList或者toSet,將資料流轉換成List型別或者是Set型別

List<String> list = new ArrayList<>();
list.add("123");
list.add("456789");
List<String> resultList = list
    .stream()
    .filter(s -> s.length() > 3)
    .collect(Collectors.toList());
resultList.forEach(s -> System.out.println(s));

groupingBy

groupingBy 用於將資料分組,最終返回一個 Map 型別

List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan",16));
list.add(new Student("lisi",2));
list.add(new Student("lzc",18));
list.add(new Student("xiaohong",1));
list.add(new Student("xiaoming",18));
list.add(new Student("lili",2));

Map<Integer, List<Student>> collect =
    list.stream()
    // .collect(Collectors.groupingBy(Student::getAge)); // 這樣是無序的
    // .collect(Collectors.groupingBy(Student::getAge, TreeMap::new,Collectors.toList())); // 升序
    .collect(Collectors.groupingBy(Student::getAge, (() -> new TreeMap().descendingMap()),Collectors.toList())); // 降序

for (Integer key : collect.keySet()) {
    List<Student> students = collect.get(key);
    System.out.println("age = " + key);
    students.forEach(s -> System.out.println(s.toString()));
}

groupingBy 可以接受一個第二引數實現多級分組:

Map<Integer, Map<String, List<Student>>> collect =
    list.stream()
    .collect(
        Collectors.groupingBy(
            Student::getAge, 
            Collectors.groupingBy(Student::getUsername)
        )
);

收集資料

list.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.summingInt(Student::getAge)));

partitioningBy

Map<Boolean, List<Student>> collect = list.stream().collect(Collectors.partitioningBy(s -> s.getAge() > 1));

partitioningBy 也可以新增一個收集器作為第二引數,進行類似 groupBy 的多重分割槽等等操作。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章