【问题标题】:How to extract only one allowed element from a stream?如何从流中仅提取一个允许的元素?
【发布时间】:2020-09-12 01:49:48
【问题描述】:

我有一个元素列表,并且想要提取字段属性的值。 问题:所有元素都应该具有相同的属性值。

我能比下面的做得更好或更优雅吗?

Set<String> matches = fields.stream().map(f -> f.getField()).collect(Collectors.toSet());
if (matches.size() != 1) throw new IllegalArgumentException("could not match one exact element");
String distrinctVal = matches.iterator().next(); //continue to use the value

这是否可以直接使用流方法,例如使用reduce

【问题讨论】:

  • 如果您的元素应该通过equals 进行比较,那么您的解决方案是正确的并且比大多数其他解决方案更快。与通常的流一样,迭代代码可以更快地结束,因为它可以在发现不匹配时更快地中止。
  • 如果“fields”为空,它甚至会抛出异常?
  • @TreffnonX 还可以在流管道中添加一点盐 (limit(2)) 以尽可能实现短路。
  • 您的代码有效吗? if (matches.size() != 1)所有元素都应该具有相同的属性值。 看起来并不足以执行。出于一些猜测,看起来groupBy 对于用例可能更实用。

标签: java java-stream


【解决方案1】:

您当前的解决方案很好。您也可以尝试这种方式来避免收集。

使用distinct(),然后使用count()

if (fields.stream().map(f -> f.getField()).distinct().count() != 1) 
      throw new IllegalArgumentException("could not match one exact element");

获取价值

String distrinctVal = fields.get(0).getField();

【讨论】:

  • 很好,但我还需要提取不同的值。
  • 嘿,如果我的回答看起来和你的几乎一模一样,我很抱歉 :) 这不是我的本意。您的帖子已被删除,因此我将我自己的答案发布给您的一些改进(即在管道中启用短路机制),我会建议您这样做而不是如果您的帖子最初没有被删除,请发布我自己的答案。
【解决方案2】:

您当然可以通过多种方式做到这一点,但哪种方式更优雅可能因人而异。

无论如何,如果您要通过流尝试此操作,我会这样做:

my answer here 稍作修改即可:

boolean result = fields.stream()
                       .map(f -> f.getField())
                       .distinct()
                       .limit(2) // ENABLE SHORT CIRCUITING
                       .count() != 1;

if (result) throw new IllegalArgumentException("could not match one exact element");

String distinctVal = fields.get(0).getField();

这种方法的好处基本上是利用limit(2) 尽可能优化。

结论:您当前的方法实际上非常好,所以如果您坚持这样做我不会感到惊讶,但您也可以选择这种方法,您可以short-循环管道。

【讨论】:

    【解决方案3】:

    这将是 reduce 解决方案。

    Optional<String> distinctVal = fields.stream()
        .map(f -> f.getField())
        .reduce((a, b) -> {
            if(a != b) throw new IllegalArgumentException("could not match one exact element");
            return a;
        });
    

    【讨论】:

    • 虽然这看起来很有希望,但它不能在 throw new 阶段编译?
    【解决方案4】:

    根据调用频率和集合的大小,迭代代码可以显着加快。

    public boolean allEqual(Collection<Fields> fields) {
        if (fields.size() > 1) {
            String last;
            boolean first = true;
            for (Field field : fields) {
                String thisString = field.getField();
                if (first) {
                    last = thisString;
                } else {
                    if (!StringUtils.equals(last, thisString)) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    

    虽然这不是一个流式解决方案,但它会在发现第一个不匹配时中止,这取决于输入 - 可以明显更快。

    【讨论】:

      【解决方案5】:

      类似于this:

      String distrinctVal = fields.stream()
          .map(f -> f.getField())
          .reduce((a, b) 
              -> { throw new IllegalArgumentException("could not match one exact element");}
          ).get();
      

      【讨论】:

        【解决方案6】:

        正如其他人所说,这主要是品味问题。这是我的。

            String distinctVal = fields.iterator().next().getField();
            if (fields.stream().map(Field::getField).anyMatch(e -> ! e.equals(distinctVal)) {
                throw new IllegalArgumentException("could not match one exact element");
            }
            //continue to use the value
        

        (代码未经测试;请原谅拼写错误。)

        我没有特别考虑性能效率的代码,但是代码只会搜索到第一个不匹配的字符串,所以应该是高效的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-25
          • 2017-05-26
          • 2023-03-17
          相关资源
          最近更新 更多