【问题标题】:Get min and max String of list获取列表的最小和最大字符串
【发布时间】:2015-12-28 20:41:07
【问题描述】:

我有一个List<String> list 并希望按字母顺序获取该列表的第一个和最后一个字符串。我想利用 Java 8 的强大功能和带有收集器的 Streams 来解决这个问题。

这不起作用:

String first = list.stream().collect(Collectors.minBy(String.CASE_INSENSITIVE_ORDER));

它给了我一个编译器错误:

类型不匹配:无法从 Optional 转换为 String

你能解释一下为什么并告诉我做我想做的最好的方法吗?

【问题讨论】:

  • 如果您的字符串列表不能包含重复项,您可以改用TreeSet
  • 顺便说一下你可能会使用Optional<String> opt = list.stream().min(String.CASE_INSENSITIVE_ORDER);

标签: java string java-8 java-stream


【解决方案1】:

Collectors.minByCollectors.maxBy返回Optional:如果Stream为空,则返回空Optional;否则,返回包含结果的Optional

如果你想在 Stream 为空时有一个默认值(或者只是null),你可以调用orElse

String first = list.stream().collect(minBy(String.CASE_INSENSITIVE_ORDER)).orElse(null);

另外,如果你确定Stream不为空,可以直接调用get()取值。


附带说明,您还可以通过调用Collections.min(分别为Collections.max)返回最小值:

String first = Collections.min(list, String.CASE_INSENSITIVE_ORDER);

无需创建 Stream 管道。请注意,如果列表为空,这将引发异常。

【讨论】:

    【解决方案2】:

    错误信息很清楚。 the javadoc 也是如此:Collector.minBy() 产生Optional<T> 类型的结果,即在您的情况下为Optional<String>

    为什么?因为如果流恰好是空的,它就不能返回任何字符串。所以你需要将get的String值从collect()返回的Optional中取出来。

    请注意,如果流为空,get() 将抛出异常,因此无法找到最小值。如果那是您想要的,因为永远不应该出现空列表,那很好。否则,你应该使用 orElse() 或 orElseThrow() 来返回一个替代的默认值,或者抛出另一个异常。

    【讨论】:

    • 除非你不想使用 get();如果列表为空,则会抛出。您想使用一种安全的方法,例如 orElse、orElseThrow、ifPresent 等。
    • @BrianGoetz 除非你想要它在空列表中失败。
    • 我当然同意。但是如果目标是抛出,因为不应该发生空列表,那么 get() 已经做了正确的事情。这里似乎就是这种情况。
    【解决方案3】:

    您提到您想要列表中的最小值和最大值。没有执行此操作的标准收集器,但您可以创建自己的:

    class ExtremesCollector<T> {
        private final Comparator<T> comparator;
        private Optional<T> min = Optional.empty();
        private Optional<T> max = Optional.empty();
    
        public static Collector<T> collector(Comparator<T> comparator) {
            return Collector.of(() -> new ExtremesCollector(comparator),
                ExtremesCollector::accept, ExtremesCollector::combine);
        }
    
        public Optional<T> getMin() {
            return min;
        }
    
        public Optional<T> getMax() {
            return max;
        }
    
        private ExtremesCollector(Comparator<T> comparator) {
            this.comparator = comparator;
        }
    
        private void accept(T value) {
            if (!min.isPresent() || comparator.compare(min.get(), value) < 0)
                min = Optional.of(value):
            if (!max.isPresent() || comparator.compare(max.get(), value) > 0)
                max = Optional.of(value):
        }
    
        private ExtremesCollector combine(ExtremesCollector other) {
            if (other.min.isPresent())
                accept(other.min.get());
            if (other.max.isPresent())
                accept(other.max.get());
        }
    }
    

    这就像你所期望的那样使用:

    ExtremesCollector extremes = myList.parallelStream()
        .collect(ExtremesCollector.collector(String.CASE_INSENSITIVE_ORDER));
    extremes.getMax().ifPresent(System.out::println);
    extremes.getMin().ifPresent(System.out::println);
    

    与使用内置的minmax 方法相比,这样做的唯一优势是效率。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2020-09-22
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      • 2020-06-27
      • 2020-07-20
      相关资源
      最近更新 更多