Java核心技术· 卷二(11版)笔记(第1-4章)

第一章 Java8 的流库

Java8 中的流库是一个新的API,它提供了一种简便的方式来对数据进行操作和处理。它包括了一些基础的接口和类,可以用来处理集合,数组等数据结构。下面是 Java8 中的流库的一些主要接口和类:

  1. Stream:流的主要接口,提供了一些常用的方法,如 filter、map、reduce 等,用于对数据进行操作。

  2. Optional:可选值类型,用于处理缺失的值,防止出现 NullPointerException。

  3. Collector:收集器,用于将流中的元素收集到指定的集合中,也可以用于分组、分区等操作。

  4. Spliterator:分割器,用于将数据分割成多个部分,以便于并行处理。

  5. IntStream、LongStream、DoubleStream:基本类型流,用于操作基本类型的数据,可以提升性能。

总的来说,Java8 中的流库提供了一种更加简单、方便且高效的方式来处理数据,它的出现是 Java 语言在函数式编程方面的一次巨大进步。

1.1 从迭代到流的操作

Java中的迭代操作常常通过foreach循环来实现,然而在Java8中,引入了流(Stream)的概念,可以大大地简化迭代过程。下面我们来比较一下迭代和流的操作:

  1. 迭代操作:
List<String> list = Arrays.asList("apple", "banana", "orange", "pear", "grape");
for (String fruit : list) {
    if (fruit.length() > 5) {
        System.out.println(fruit);
    }
}
  1. 流的操作:
List<String> list = Arrays.asList("apple", "banana", "orange", "pear", "grape");
list.stream().filter(fruit -> fruit.length() > 5).forEach(System.out::println);

可以看出,使用流的操作可以比使用迭代操作更加简洁、清晰。具体来说,流的操作主要有以下几个步骤:

  1. 创建流对象:使用Collection下的stream()和parallelStream()方法创建。
List<String> list = Arrays.asList("apple", "banana", "orange", "pear", "grape");
Stream<String> stream = list.stream();
  1. 中间操作:Stream提供了很多中间操作,如filter、distinct、map、sorted、limit和skip等。
Stream<String> filteredStream = stream.filter(fruit -> fruit.length() > 5);
  1. 终止操作:终止操作是产生最终结果的操作,可以是forEach、toArray、reduce、collect等。
filteredStream.forEach(System.out::println);
//或者使用一行代码
list.stream().filter(fruit -> fruit.length() > 5).forEach(System.out::println);

使用流的操作可以大大地简化代码,同时也能够提高代码的可读性和可维护性。

下面是一个完整的实例代码,演示了从迭代到流的操作过程:

import java.util.Arrays;
import java.util.List;

public class IterationVsStreamExample {

    public static void main(String[] args) {

        // 迭代操作
        List<String> list = Arrays.asList("apple", "banana", "orange", "pear", "grape");
        for (String fruit : list) {
            if (fruit.length() > 5) {
                System.out.println(fruit);
            }
        }

        // 流的操作
        List<String> list2 = Arrays.asList("apple", "banana", "orange", "pear", "grape");
        list2.stream().filter(fruit -> fruit.length() > 5).forEach(System.out::println);
    }
}

输出结果:

banana
orange
grape
banana
orange
grape

可以看出,使用流的操作可以比使用迭代操作更加简洁、清晰。使用流的操作可以大大地简化代码,同时也能够提高代码的可读性和可维护性。

1.2 流的创建

在 Java 8 中,可以使用 Stream 类来创建流。Stream 类提供了多种创建流的方法,包括:

  1. 通过 Collection 接口的 stream() 方法创建流:
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();
  1. 通过 Arrays 类的 stream() 方法创建流:
String[] arr = {"apple", "banana", "orange"};
Stream<String> stream = Arrays.stream(arr);
  1. 通过 Stream 类的 of() 方法创建流:
Stream<String> stream = Stream.of("apple", "banana", "orange");
  1. 通过 Stream 类的 generate() 方法创建一个无限流,需要提供一个 Supplier(供应商)来生成元素:
Stream<String> stream = Stream.generate(() -> "hello");
  1. 通过 Stream 类的 iterate() 方法创建一个无限流,需要提供一个初始值和一个 UnaryOperator(一元操作符)来生成后续的元素:
Stream<Integer> stream = Stream.iterate(1, n -> n + 1);

注意,这种方式创建的流是无限的,要使用 limit() 方法来限制元素个数。

  1. 通过 Files 类的 lines() 方法创建流,该方法将文件的每一行作为流的一个元素:
Path path = Paths.get("file.txt");
Stream<String> stream = Files.lines(path);

以上是 Java 中常用的创建流的方式,使用这些方式可以创建出各种形式的流,以便对数据进行处理和操作。

下面是使用不同方式创建流的完整实例代码:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        // 通过 Collection 接口的 stream() 方法创建流
        List<String> list = Arrays.asList("apple", "banana", "orange");
        Stream<String> stream1 = list.stream();

        // 通过 Arrays 类的 stream() 方法创建流
        String[] arr = {"apple", "banana", "orange"};
        Stream<String> stream2 = Arrays.stream(arr);

        // 通过 Stream 类的 of() 方法创建流
        Stream<String> stream3 = Stream.of("apple", "banana", "orange");

        // 通过 Stream 类的 generate() 方法创建流
        Supplier<String> supplier = () -> "hello";
        Stream<String> stream4 = Stream.generate(supplier);

        // 通过 Stream 类的 iterate() 方法创建流
        Stream<Integer> stream5 = Stream.iterate(1, n -> n + 1).limit(5);

        // 通过 Files 类的 lines() 方法创建流
        Path path = Paths.get("file.txt");
        try (Stream<String> stream6 = Files.lines(path)) {
            // Do something with the stream
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.3 filter、map和flatMap

filter、map和flatMap是Java Stream API中常用的操作方法,下面是简单的介绍和实例代码:

  1. filter

filter方法用于过滤出符合条件的元素,接收一个Predicate参数,返回一个包含符合条件元素的新流。

例如,过滤出长度大于等于5的字符串:

List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon", "peach");
Stream<String> stream = list.stream();
Stream<String> newStream = stream.filter(s -> s.length() >= 5);
newStream.forEach(System.out::println); // watermelon, peach
  1. map

map方法用于将每个元素映射到另一个元素,接收一个Function参数,返回一个包含映射结果的新流。

例如,将字符串转换为大写:

List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();
Stream<String> newStream = stream.map(s -> s.toUpperCase());
newStream.forEach(System.out::println); // APPLE, BANANA, ORANGE
  1. flatMap

flatMap方法用于将一个流中的每个元素转换为另一个流,并将这些流合并成一个流,接收一个Function参数,返回一个包含合并流结果的新流。

例如,将字符串拆分为字符并返回包含所有字符的流:

List<String> list = Arrays.asList("hello", "world");
Stream<String> stream = list.stream();
Stream<Character> newStream = stream.flatMap(s -> s.chars().mapToObj(c -> (char) c));
newStream.forEach(System.out::println); // h, e, l, l, o, w, o, r, l, d

下面是filter、map和flatMap三个方法的完整实例代码:

  1. filter
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class FilterDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon", "peach");
        Stream<String> stream = list.stream();
        Stream<String> newStream = stream.filter(s -> s.length() >= 5);
        newStream.forEach(System.out::println);
    }
}

输出结果:

watermelon
peach
  1. map
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class MapDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("apple", "banana", "orange");
        Stream<String> stream = list.stream();
        Stream<String> newStream = stream.map(s -> s.toUpperCase());
        newStream.forEach(System.out::println);
    }
}

输出结果:

APPLE
BANANA
ORANGE
  1. flatMap
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class FlatMapDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world");
        Stream<String> stream = list.stream();
        Stream<Character> newStream = stream.flatMap(s -> s.chars().mapToObj(c -> (char) c));
        newStream.forEach(System.out::println);
    }
}

输出结果:

h
e
l
l
o
w
o
r
l
d

1.4 抽取子流和组合流

Java中,可以使用filter、map、reduce等方法来抽取子流和组合流。

抽取子流:

  1. filter方法:可以根据指定的条件筛选出一个流中符合条件的子流。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> subList = list.stream().filter(i -> i > 3).collect(Collectors.toList());
// 结果为[4, 5, 6]
  1. limit方法:可以限制一个流中元素的数量。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> subList = list.stream().limit(3).collect(Collectors.toList());
// 结果为[1, 2, 3]
  1. skip方法:可以跳过指定数量的元素,返回剩下的元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> subList = list.stream().skip(3).collect(Collectors.toList());
// 结果为[4, 5, 6]

组合流:

  1. flatMap方法:可以将多个流合并成一个流。
List<List<Integer>> lists = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
List<Integer> combinedList = lists.stream().flatMap(List::stream).collect(Collectors.toList());
// 结果为[1, 2, 3, 4, 5, 6]
  1. reduce方法:可以将一个流中的所有元素合并成一个元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = list.stream().reduce(0, (a, b) -> a + b);
// 结果为21

以下是Java抽取子流和组合流的完整实例代码:

抽取子流:

  1. filter方法:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> subList = list.stream().filter(i -> i > 3).collect(Collectors.toList());
        System.out.println(subList); // [4, 5, 6]
    }
}
  1. limit方法:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LimitExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> subList = list.stream().limit(3).collect(Collectors.toList());
        System.out.println(subList); // [1, 2, 3]
    }
}
  1. skip方法:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SkipExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> subList = list.stream().skip(3).collect(Collectors.toList());
        System.out.println(subList); // [4, 5, 6]
    }
}

组合流:

  1. flatMap方法:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FlatMapExample {
   
    public static void main(String[] args) {
   
        List<List<Integer>> lists = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
        List<Integer> combinedList = lists.stream().flatMap(List::stream).collect(Collectors.toList());
        System.out.println(combinedList); // [1, 2, 3, 4, 5, 6]
    }
}
  1. reduce方法:
import java.util.Arrays;
import java.util.List;

public class ReduceExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        int sum = list.stream().reduce(0, (a, b) -> a + b);
        System.out.println(sum); // 21
    }
}

以上代码均为简单的示例,可以根据具体需求进行修改。

1.5 其他的流转换

除了抽取子流和组合流外,Java流还提供了许多其他的流转换。以下是其中的一些:

  1. map方法:将流中的元素通过函数转换为新的元素。示例代码:
import java.util.Arrays;
import java.util.List;

public class MapExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> newList = list.stream().map(i -> i * i).collect(Collectors.toList());
        System.out.println(newList); // [1, 4, 9, 16, 25, 36]
    }
}
  1. distinct方法:去除流中的重复元素。示例代码:
import java.util.Arrays;
import java.util.List;

public class DistinctExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 1, 2, 2, 3, 4, 5, 5, 6);
        List<Integer> newList = list.stream().distinct().collect(Collectors.toList());
        System.out.println(newList); // [1, 2, 3, 4, 5, 6]
    }
}
  1. sorted方法:对流中的元素进行排序。示例代码:
import java.util.Arrays;
import java.util.List;

public class SortedExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(6, 5, 4, 3, 2, 1);
        List<Integer> sortedList = list.stream().sorted().collect(Collectors.toList());
        System.out.println(sortedList); // [1, 2, 3, 4, 5, 6]
    }
}
  1. peek方法:对流中的每个元素执行操作,但是不改变流中元素的值(类似于调试)。示例代码:
import java.util.Arrays;
import java.util.List;

public class PeekExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<Integer> newList = list.stream().peek(i -> System.out.println("Original value: " + i)).map(i -> i * i).peek(i -> System.out.println("New value: " + i)).collect(Collectors.toList());
        System.out.println(newList); // [1, 4, 9, 16, 25, 36]
    }
}
  1. toArray方法:将流转换为数组。示例代码:
import java.util.Arrays;
import java.util.List;

public class ToArrayExample {
   
    public static void main(String[] args) {
   
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        Integer[] array = list.stream().toArray(Integer[]::new);
        System.out.println(Arrays.toString(array)); // [1, 2, 3, 4, 5, 6]
    }
}

以上仅是其中的一些常用流转换方法,Java流还有很多其他的转换方法,可以根据实际需要进行选择使用。

1.6 简单约简

在Java中,约简是指将流中的元素合并为单个值的操作。Java中提供了三种类型的约简操作:归约、收集和汇总。

  1. 归约操作:将流中的元素合并为单个值。
// 示例:计算流中所有整数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
int sum = list.stream().reduce(0, (a, b) -> a + b);
System.out.println(sum); // 21
  1. 收集操作:将流中的元素收集到一个集合中。
// 示例:将流中的字符串收集到List中
String[] array = {
   "hello", "world", "java", "stream"};
List<String> list = Arrays.stream(array).collect(Collectors.toList());
System.out.println(list); // [hello, world, java, stream]
  1. 汇总操作:对流中的元素进行汇总操作(如计算平均值、最大值、最小值等)。
// 示例:计算流中所有整数的平均值
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
double average = list.stream().mapToInt(Integer::intValue).average().getAsDouble();
System.out.println(average); // 3.5

Java的约简操作提供了非常灵活的处理方式,可以根据实际需要进行选择使用。

下面是 Java 简单约简的完整代码实例:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamReductionExample {
   

    public static void main(String[] args) {
   

        // 归约操作
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        int sum = list.stream().reduce(0, (a, b) -> a + b);
        System.out.println("Sum: " + sum); // Sum: 21

        // 收集操作
        String[] array = {
   "hello", "world", "java", "stream"};
        List<String> stringList = Arrays.stream(array).collect(Collectors.toList());
        System.out.println("String list: " + stringList); // String list: [hello, world, java, stream]

        // 汇总操作
        List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6);
        double average = intList.stream().mapToInt(Integer::intValue).average().getAsDouble();
        System.out.println("Average: " + average); // Average: 3.5
    }
}

1.7 Optional 类型

Java 8 引入了一个新的类 Optional,它是一个容器对象,可以包含也可以不包含非空值。它的主要目的是避免在代码中出现 null 引用,从而避免空指针异常。

Optional 类型提供了以下几个主要方法:

  • of(T value):创建一个包含非空值的 Optional 对象。
  • empty():创建一个空的 Optional 对象。
  • ofNullable(T value):如果值非空,则创建包含该值的 Optional 对象,否则创建空的 Optional 对象。
  • get():返回包含的非空值,如果值为空则抛出 NoSuchElementException 异常。
  • isPresent():判断是否包含非空值。
  • orElse(T other):如果包含非空值则返回该值,否则返回指定的默认值。
  • orElseGet(Supplier<? extends T> other):如果包含非空值则返回该值,否则返回一个由指定的供应函数生成的值。
  • orElseThrow(Supplier<? extends X> exceptionSupplier):如果包含非空值则返回该值,否则抛出一个由指定的异常供应函数生成的异常。

下面是一个使用 Optional 的简单例子:

import java.util.Optional;

public class OptionalExample {
   

    public static void main(String[] args) {
   

        String str = "hello";

        Optional<String> optional1 = Optional.ofNullable(str);
        System.out.println(optional1.isPresent()); // true
        System.out.println(optional1.get()); // hello

        Optional<String> optional2 = Optional.empty();
        System.out.println(optional2.isPresent()); // false

        String s1 = optional1.orElse("world");
        String s2 = optional2.orElse("world");
        System.out.println(s1); // hello
        System.out.println(s2); // world
    }
}

在上面的例子中,我们使用 Optional.ofNullable() 方法创建了一个包含非空值的 Optional 对象,然后使用 isPresent() 方法判断是否包含非空值,并使用 get() 方法获取值。另外,我们还使用了 orElse()orElseGet() 方法指定默认值。

1.7.1 获取Optional 值

在 Java 中,可以使用 Optional 类来避免 null 值的问题。Optional 是一个容器对象,它可以包含非空值或者空值。在获取 Optional 值时,可以使用以下方法:

  1. get() 方法

get() 方法用于获取 Optional 对象中的值。如果 Optional 对象中的值为 null,则会抛出 NoSuchElementException 异常。因此,在使用 get() 方法之前,需要先检查 Optional 对象中的值是否为空。

示例:

Optional<String> optional = Optional.of("Hello");
String value = optional.get();
System.out.println(value); // 输出:Hello
  1. orElse() 方法

orElse() 方法用于在 Optional 对象中的值为 null 时提供一个默认值。如果 Optional 对象中的值不为 null,则返回该值。如果 Optional 对象中的值为 null,则返回传入的默认值。

示例:

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElse("World");
System.out.println(value); // 输出:World
  1. orElseGet() 方法

orElseGet() 方法与 orElse() 方法类似,也是在 Optional 对象中的值为 null 时提供一个默认值。但是,与 orElse() 方法不同的是,orElseGet() 方法接受一个 Supplier 参数,用于提供默认值。在使用 orElseGet() 方法时,只有在 Optional 对象中的值为 null 时,才会执行 Supplier 提供的代码,生成默认值。

示例:

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElseGet(() -> "World");
System.out.println(value); // 输出:World
  1. orElseThrow() 方法

orElseThrow() 方法与 get() 方法类似,用于获取 Optional 对象中的值。但是,与 get() 方法不同的是,orElseThrow() 方法在 Optional 对象中的值为 null 时抛出一个指定的异常。

示例:

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElseThrow(() -> new RuntimeException("Value is null"));

上述方法可以满足不同的需求,根据具体情况选择使用。

以下是使用不同方法获取 Optional 值的完整 Java 代码实例:

import java.util.Optional;

public class OptionalDemo {
   
    public static void main(String[] args) {
   
        // 创建 Optional 对象
        Optional<String> optional = Optional.of("Hello");

        // 使用 get() 方法获取 Optional 对象中的值
        String value = optional.get();
        System.out.println(value); // 输出:Hello

        // 创建一个值为 null 的 Optional 对象
        Optional<String> optionalNull = Optional.ofNullable(null);

        // 使用 orElse() 方法获取 Optional 对象中的值或默认值
        String valueOrNull = optionalNull.orElse("World");
        System.out.println(valueOrNull); // 输出:World

        // 使用 orElseGet() 方法获取 Optional 对象中的值或默认值
        String valueOrNull2 = optionalNull.orElseGet(() -> "World");
        System.out.println(valueOrNull2); // 输出:World

        // 使用 orElseThrow() 方法获取 Optional 对象中的值或抛出异常
        try {
   
            String valueOrNull3 = optionalNull.orElseThrow(() -> new RuntimeException("Value is null"));
        } catch (RuntimeException e) {
   
            System.out.println(e.getMessage()); // 输出:Value is null
        }
    }
}

1…7.2 消费 Otional 值

在Java 8中引入了 Optional 类来处理一些可能为空的对象,可以使用 ifPresent() 方法来消费 Optional 值。

以下是 Java 消费 Optional 值的完整示例代码:

import java.util.Optional;

public class OptionalDemo {
   

    public static void main(String[] args) {
   
        Optional<String> optional = Optional.of("Hello");

        // 使用 ifPresent() 方法消费 Optional 值
        optional.ifPresent(value -> System.out.println(value)); // 输出:Hello

        Optional<String> optionalNull = Optional.ofNullable(null);

        // 无法消费 Optional 值,因为值为 null
        optionalNull.ifPresent(value -> System.out.println(value)); // 没有输出
    }
}

通过 ifPresent() 方法,可以在值存在时执行一些操作。如果值为 null ,则不会执行任何操作。可以使用 lambda 表达式或方法引用来处理值。

1.7.3 管道化 Optional 值

在Java 9中,引入了 ifPresentOrElse() 方法来管道化 Optional 值。它允许在值存在和值不存在时都执行不同的操作,而不是只有值存在时才执行操作。

以下是 Java 管道化 Optional 值的完整示例代码:

import java.util.Optional;

public class OptionalDemo {
   

    public static void main(String[] args) {
   
        Optional<String> optional = Optional.of("Hello");

        // 使用 ifPresentOrElse() 方法管道化 Optional 值
        optional.ifPresentOrElse(
                value -> System.out.println("Value is present: " + value),       // 值存在时执行的操作
                () -> System.out.println("Value is not present"));              // 值不存在时执行的操作

        Optional<String> optionalNull = Optional.ofNullable(null);

        // 使用 ifPresentOrElse() 方法管道化 Optional 值
        optionalNull.ifPresentOrElse(
                value -> System.out.println("Value is present: " + value),       // 值存在时执行的操作
                () -> System.out.println("Value is not present"));              // 值不存在时执行的操作
    }
}

通过 ifPresentOrElse() 方法,可以在值存在时执行一个操作,否则执行另一个操作。它可以接受两个参数,第一个参数是在值存在时执行的操作,第二个参数是在值不存在时执行的操作。

1.7.4不合适使用的 Optional 值的方式

以下是不合适使用 Optional 值的方式:

  1. 强制展开 Optional 值:这意味着使用感叹号(!)来强制获取 Optional 值的实际值,即使它可能为空。如果 Optional 值为空,则将会引发运行时错误。应该避免使用此方法,而是使用安全的 Optional 绑定方法或 nil 合并操作符(??)。

  2. 忽略 Optional 值:这意味着不检查 Optional 值是否为空,直接将其传递给需要非空值的函数或属性。这可能会导致意外的运行时错误,因此应该始终检查 Optional 值是否为空。

  3. 误用弱 Optional 值:弱 Optional 值是一种特殊类型的 Optional 值,用于防止循环引用。如果不正确使用弱 Optional 值,它可能会导致崩溃或内存泄漏。应该仔细阅读文档,并确保正确使用弱 Optional 值。

1.7.5 创建 Optional 值

以下是三种创建 Optional 值的实例代码:

  1. 使用 of 方法创建一个非空的 Optional 对象:
Optional<String> name = Optional.of("John");
  1. 使用 ofNullable 方法创建一个可能为空的 Optional 对象:
Optional<String> name = Optional.ofNullable(null);
  1. 使用 empty 方法创建一个空的 Optional 对象:
Optional<String> name = Optional.empty();

需要注意的是,如果创建的 Optional 对象是空的,那么调用 get 方法会抛出 NoSuchElementException 异常。因此,在使用 Optional 对象时,需要先判断其是否为空,再进行操作。例如:

Optional<String> name = Optional.ofNullable(null);
if (name.isPresent()) {
    String upperCaseName = name.get().toUpperCase();
    System.out.println(upperCaseName);
} else {
    System.out.println("Name is empty.");
}

上述代码中,首先判断 name 是否为空(即是否为 null),如果不为空,则将其转换为大写字母并输出;如果为空,则输出提示信息。

下面是一个创建 Optional 值的完整示例代码:

import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        // 创建非空 Optional 对象
        Optional<String> name1 = Optional.of("John");
        System.out.println(name1.get()); // 输出 John

        // 创建可能为空的 Optional 对象
        Optional<String> name2 = Optional.ofNullable(null);
        if (name2.isPresent()) {
            System.out.println(name2.get()); // 不会执行
        } else {
            System.out.println("Name is empty.");
        }

        // 创建空的 Optional 对象
        Optional<String> name3 = Optional.empty();
        if (name3.isPresent()) {
            System.out.println(name3.get()); // 不会执行
        } else {
            System.out.println("Name is empty.");
        }
    }
}

在上述代码中,首先使用 of 方法创建了一个非空的 Optional 对象 name1,然后调用 get 方法获取其值并输出。

接着使用 ofNullable 方法创建了一个可能为空的 Optional 对象 name2,然后使用 isPresent 方法判断其是否为空,如果不为空则调用 get 方法获取其值并输出,否则输出提示信息。

最后使用 empty 方法创建了一个空的 Optional 对象 name3,并使用与 name2 相同的方式判断其是否为空。由于 name3 是空的,因此不会执行 get 方法,而是输出提示信息。

1.7.6 用flatMap 构建 Optional值的函数

flatMap 方法可以将一个 Optional 对象映射为另一个 Optional 对象,并将其扁平化为一个 Optional 对象。下面是一个使用 flatMap 方法构建 Optional 值的示例代码:

import java.util.Optional;

public class FlatMapExample {
    public static void main(String[] args) {
        Optional<String> str = Optional.of("hello");
        Optional<String> result = str.flatMap(s -> Optional.of(s.toUpperCase()));
        System.out.println(result.get()); // 输出 HELLO
    }
}

在上述代码中,首先使用 of 方法创建了一个非空的 Optional 对象 str,其值为 “hello”。然后使用 flatMap 方法将 str 映射为另一个 Optional 对象,该对象的值为 str 转换为大写字母后的结果。因为该方法返回的是一个扁平化后的 Optional 对象,所以可以直接调用 get 方法获取其值并输出。结果为 “HELLO”。

1.7.7 将 Optional 转化为流

要将 Java 中的 Optional 对象转换为流,可以使用 Optional 类的 stream() 方法。该方法会返回一个 Stream 对象,其中包含了 Optional 对象的值(如果存在值的话)。

例如,假设有一个 Optional 对象,可以使用如下代码将其转换为流:

Optional<String> optional = Optional.of("Hello World");
Stream<String> stream = optional.stream();

如果 Optional 对象中存在值,那么 stream() 方法将返回一个只包含该值的 Stream 对象。否则,它将返回一个空的 Stream 对象。可以使用流的相关方法进行进一步的操作,例如:

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

这些方法将分别打印出 Optional 对象中的值或者什么都不做(如果 Optional 对象为空)。

下面是一个将 Optional 对象转换为流的 Java 示例代码:

import java.util.Optional;
import java.util.stream.Stream;

public class OptionalToStreamExample {
   
    public static void main(String[] args) {
   
        Optional<String> optional = Optional.of("Hello World");
        Stream<String> stream = optional.stream();
        optional.ifPresent(System.out::println);
        stream.forEach(System.out::println);
    }
}

该程序首先创建了一个 Optional 对象,并使用 stream() 方法将其转换为流。接下来,它使用 ifPresent() 方法打印 Optional 对象中的值(如果存在),并使用 forEach() 方法打印流中的所有元素。

输出结果:

Hello World
Hello World

可以看到,由于 Optional 对象中存在值,因此 ifPresent() 方法的打印输出和 forEach() 方法的打印输出都包含了相同的字符串。

1.8 收集结果

Java中,可以使用 collect() 方法将流转换为集合或其他数据结构,并返回一个结果对象。收集器(Collector)是一个实现了 Collector 接口的函数对象,它定义了如何将流元素累加到集合或其他数据结构中。

以下是一些使用 collect() 方法并使用收集器的示例代码:

  1. 将流转换为 List:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());
System.out.println(evenNumbers); // Output: [2, 4]
  1. 将流转换为 Set:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Set<Integer> evenNumbersSet = numbers.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toSet());
System.out.println(evenNumbersSet); // Output: [2, 4]
  1. 将流转换为 Map:
List<String> names = Arrays.asList("John", "Jane", "Adam", "Tom");
Map<String, Integer> nameLengthMap = names.stream()
        .collect(Collectors.toMap(
                name -> name,
                name -> name.length()
        ));
System.out.println(nameLengthMap); // Output: {John=4, Jane=4, Adam=4, Tom=3}

在第三个示例中,我们使用 toMap() 收集器将流元素转换为一个 Map 集合。在 toMap() 收集器中,我们使用一个函数将键映射到值,其中 name -> name 将每个元素映射为相同的键和值,name -> name.length() 将每个元素映射为名称长度的值。

以上是一些示例,但还有许多其他可用的收集器,例如 joining() 收集器可以将流元素连接为一个字符串,averagingInt() 收集器可以计算流元素的平均值等等。

下面是一个完整的实例代码,展示了如何使用 collect() 方法和各种收集器来收集 Java 流的结果:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamCollectorExample {
   
    public static void main(String[] args) {
   
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Collecting to List
        List<Integer> evenNumbers = numbers.stream()
                .filter(n -> n % 2 == 0)
                .collect(Collectors.toList());
        System.out.println("Even numbers: " + evenNumbers);

        // Collecting to Set
        Set<Integer> oddNumbersSet = numbers.stream()
                .filter(n -> n % 2 != 0)
                .collect(Collectors.toSet());
        System.out
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值