【问题标题】:What is the best way to implement the python count function in java?在java中实现python计数功能的最佳方法是什么?
【发布时间】:2018-06-04 21:48:23
【问题描述】:

我正在学习如何在 java 中使用流,我想知道将 python 计数功能复制到 java 中的最有效方法。

对于不熟悉 python 计数的人,请参阅here

我已经完成了一个简单的实现,但我怀疑这是否会被添加到生产级环境中:

private List<String> countMessages(List<String> messages) {
        Map<String, Integer> messageOccurrences = new HashMap<>();
        List<String> stackedMessages = new LinkedList<String>();
        this.messages.stream().filter((message) -> (messageOccurrences.containsKey(message))).forEachOrdered((message) -> {
                    int new_occ = messageOccurrences.get(message) + 1;
                    messageOccurrences.put(message, new_occ);
        });
        messageOccurrences.keySet().forEach((key) -> {
            stackedMessages.add(key + "(" + messageOccurrences.get(key) + "times)" );
        });
        return stackedMessages;
    }

任何改进或指针将不胜感激。

【问题讨论】:

  • 这个问题更适合codereview.stackexchange.com
  • 这看起来是一种复杂的方式来执行 pythons list count() 方法所做的事情。你不只是想要long count = this.messages.stream().filter((message) -&gt; message.equals(messages)).count(); 吗?如果没有,请详细说明...
  • @Aominè 我第一次提交问题时犯了一个错误。该函数应该获取消息列表并返回一个哈希图,其中消息作为键,它在列表中出现的次数作为值。我必须使用流来做到这一点。
  • 作为一种风格说明,如果您不使用这么多大括号污染代码,您的代码将变得更具可读性。而不是(name) -&gt; (expression),只写name -&gt; expression。此外,不要写(name) -&gt; { singleMethodInvocation(); },而是写name -&gt; singleMethodInvocation()。然后,不要遍历 keySet() 以在地图上为每个键执行 get(),因为这不仅是视觉噪音,而且效率低下。您可以遍历 entrySet() 或在这里,只需使用 messageOccurrences.forEach((key, value) -&gt; stackedMessages.add(key + "(" + value + "times)" )...
  • 并考虑When to use LinkedList over ArrayList?(剧透:几乎从不)。

标签: java functional-programming java-stream


【解决方案1】:

回答“在java中实现python计数功能的最佳方法是什么?”这个问题。

Java 已经有 Collections.frequency 可以做到这一点。

但是,如果您想使用流 API 来执行此操作,那么我相信通用解决方案是:

public static <T> long count(Collection<T> source, T element) {
       return source.stream().filter(e -> Objects.equals(e, element)).count();
}

那么用例将是:

long countHellp = count(myStringList, "hello");
long countJohn = count(peopleList, new Person("John"));
long count101 = count(integerList, 101); 
...
...

或者如果你愿意,你甚至可以传递一个谓词:

public static <T> long count(Collection<T> source, Predicate<? super T> predicate) {
       return source.stream().filter(predicate).count();
}

那么用例将是例如:

long stringsGreaterThanTen = count(myStringList, s -> s.length() > 10);
long malesCount = count(peopleList, Person::isMale);
long evens = count(integerList, i -> i % 2 == 0); 
...
...

鉴于您对帖子的评论,您似乎想要“分组”然后获取每个组的计数。

public Map<String, Long> countMessages(List<String> messages) {
        return messages.stream()
                       .collect(groupingBy(Function.identity(), counting()));
}

这会从messages 列表中创建一个流,然后对它们进行分组,传递一个counting() 作为下游收集器,这意味着我们将检索一个Map&lt;String, Long&gt;,其中键是元素,值是该特定元素的出现字符串。

确保您有导入:

import static java.util.stream.Collectors.*;

对于后一种解决方案。

【讨论】:

  • 这可能是一个愚蠢的问题,但有没有办法将Long 返回值转换为函数内的Integer?也将此标记为答案,因为它让我得到了我想要的结果
  • @Q.H.这根本不是一个愚蠢的问题。这是个好问题。只需将 counting() 更改为 summingInt(e -&gt; 1)
  • 不要使用e -&gt; Objects.equals(e, element),使用Predicate.isEqual(element)。除了噪音更少之外,它的实现等效于element == null? Object::isNull: element::equals,它不会重复null 检查每个流元素。
  • @Holger 对,谢谢。从现在开始会牢记这一点。
猜你喜欢
  • 2011-09-10
  • 2018-02-10
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 2012-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多