繁体   English   中英

使用Java Streams获取嵌套在HashMap中的大多数元素的集合

[英]Getting the Set with the most elements nested in a HashMap using Java Streams

所以情况就是这样:我需要在某些日期登记人们的投票。 简而言之,提出了一个日期,人们投票支持他们想要的日期。

数据结构如下:

private HashMap<LocalDateTime, Set<Vote>> votes;

投票是:

public class Vote {
    private String name;
    private VoteType vote;

    public Vote(String name, VoteType vote) {
        super();
        this.name = name;
        this.vote = vote;
    }
}

其中VoteType只是一个枚举:

public enum VoteType {YES, NO, MAYBE}

现在我已经创建了一个返回可用性投票数量的流(VoteType):

public Map<LocalDateTime, Integer> voteCount(VoteType targetVote) {
    return this.votes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new Integer(
            e.getValue().stream().filter(v -> v.getVote() == targetVote).collect(Collectors.toList()).size())));
}

所以我的问题是:如何使用Java Streams获得最多“YES”的日期。

/* Returns the date that got the most 'YES' votes */
public LocalDateTime winningDate() {
    // TODO
}

感谢您的帮助!

所以我的问题是:如何使用Java Streams获得最多“YES”的日期。

这将是一个漫长的...

  1. 我们需要到达一个我们有一个Stream<LocalDateTime>的位置,这样我们以后可以按日期分组,应用一个counting下游收集器来获得该特定日期的投票数,我们可以通过flatMap完成这个结构。
  2. 我们只需要保留投票类型为YES的对象
  3. 我们按日期对结果进行分组,并将值作为该特定日期的YES投票数。
  4. 我们流过entrySet并通过投票找到max日期

码:

/* Returns the date that got the most 'YES' votes */
public Optional<LocalDateTime> getWinningDate() {
    return votes.entrySet() // Set<Entry<LocaleDateTime, Set<Vote>>
            .stream() // Stream<Entry<LocaleDateTime, Set<Vote>>
            .flatMap(e -> e.getValue().stream().filter(a -> a.getVote() == VoteType.YES)
                         .map(x -> e.getKey())) // Stream<LocalDateTime>
           .collect(groupingBy(Function.identity(), counting())) // Map<LocaleDateTime, Long>
           .entrySet() // Set<Entry<LocaleDateTime, Long>>
           .stream() // Stream<Entry<LocaleDateTime, Long>>
           .max(Comparator.comparingLong(Map.Entry::getValue)) // Optional<Entry<LocaleDateTime, Long>>
           .map(Map.Entry::getKey); // Optional<LocalDateTime>
}
  • 请注意我已将方法返回类型更改为Optional<LocaleDateTime> ,我本可以返回.map(Map.Entry::getKey).orElse(null)因此您可以维护当前方法返回类型LocalDateTime但这只是感觉不好,所以我决定推迟决定在客户的“无价值案例”中做些什么。
  • 我已将方法名称更改为getWinningDate以增强可读性。

至于处理Optional<T> ,在你的情况下,如果你想在getWinningDate返回一个空的Optional的情况下有一个null值,你可以安全地解包它:

LocalDateTime winningDate = getWinningDate().orElse(null);

或者如果您想提供默认日期:

LocalDateTime winningDate = getWinningDate().orElse(defaultDate);

或者如果您确定总会有结果,那么只需调用get()

LocalDateTime winningDate = getWinningDate().get();

等等..

你可以这样做:

private LocalDateTime winningDate(Map<LocalDateTime, Integer> mapGroup) {
    Integer max = mapGroup
                    .values().stream()
                    .max(Comparator.naturalOrder())
                    .get();

    return mapGroup
                    .entrySet()
                    .stream()
                    .filter(e -> e.getValue().equals(max))
                    .map(Map.Entry::getKey)
                    .findFirst().orElse(null);
}

这个答案显示了一种方法,可以在没有您的voteCount方法的情况下执行操作,但以防万一您可能希望在winningDate方法中编写一些与您已经制作的voteCount方法集成的逻辑。

在这种情况下,我们可以做:

/* Returns the date that got the most 'YES' votes */
public Optional<LocalDateTime> getWinningDate() {
    return voteCount(VoteType.YES).entrySet() // call voteCount and stream over the entries
            .stream()
            .max(Comparator.comparingLong(Map.Entry::getValue))
            .map(Map.Entry::getKey);
}
  • 首先,我们调用voteCount(VoteType.YES)方法来获取日期的映射和日期的YES投票数。
  • 第二,我们通过投票计数找到最大的LocalDateTime
  • 请注意我已将方法返回类型更改为Optional<LocaleDateTime> ,我本可以返回.map(Map.Entry::getKey).orElse(null)因此您将能够维护当前方法返回类型LocalDateTime但这只是感觉不好,所以我决定推迟决定在客户的“无价值案例”中做些什么。
  • 我已将方法名称更改为getWinningDate以增强可读性。

此外, voteCount方法可以改进为:

public Map<LocalDateTime, Long> voteCount(VoteType targetVote) {
        return this.votes.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, 
                e -> e.getValue().stream().filter(v -> v.getVote() == targetVote).count()));
}

这样可以避免构建通过过滤器的所有元素列表的开销,只需通过size()来检索计数,而只是filter和调用count

使用您的第一种方法来计算YES投票,返回一个肯定计数的地图,该地图被传递到获胜日期方法:

/* Returns the date that got the most 'YES' votes */
public LocalDateTime winningDate(Map<LocalDateTime, Integer> yesVotes) {
    return yesVotes.entrySet().stream().max(Map.Entry.comparingByValue()).get().getKey();
}

我不禁想到这是这里的意图,但我知道什么。

你问过如何用流做,这是另一种方式:

class Max { long value = Long.MIN_VALUE; LocalDateTime date; }
Max max = new Max();
votes.forEach((d, vs) -> {
    long count = vs.stream().filter(v -> VoteType.YES == v.getVote()).count();
    if (count > max.value) {
        max.value = count;
        max.date = d;
    }
});

LocalDateTime maxDate = max.date;

并得到一组投票:

Set<Vote> maxVotesForYes = votes.get(maxDate);

此解决方案迭代映射条目并为每个日期计数YES投票。 如果此计数大于当前最大计数,则更改最大计数(及其对应日期)。

为了能够修改最大计数及其相应的日期,我们需要一个本地类Max来跟踪这些值(否则,我们将无法从lambda中更改变量)。

问题是如何解决这个问题“使用Java Streams” 以下使用流。 还有一个for -loop。

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;


public class VoteCountTest
{
    public static void main(String[] args)
    {
        Map<LocalDateTime, Set<Vote>> votes = 
            new LinkedHashMap<LocalDateTime, Set<Vote>>();

        Set<Vote> yes0 = votesWith(VoteType.NO, VoteType.NO);
        Set<Vote> yes1 = votesWith(VoteType.YES, VoteType.NO);
        Set<Vote> yes2 = votesWith(VoteType.YES, VoteType.YES);

        votes.put(LocalDateTime.of(2000, 1, 1, 1, 1), yes1);
        votes.put(LocalDateTime.of(2000, 1, 2, 1, 1), yes0);
        votes.put(LocalDateTime.of(2000, 1, 3, 1, 1), yes2);
        votes.put(LocalDateTime.of(2000, 1, 4, 1, 1), yes1);

        System.out.println(getWinningDateA(votes));
        System.out.println(getWinningDateB(votes));
    }

    public static Optional<LocalDateTime> getWinningDateA(
        Map<LocalDateTime, Set<Vote>> votes)
    {
        LocalDateTime bestDate = null;
        long maxCount = -1;
        Predicate<Vote> votedYes = v -> v.getVote() == VoteType.YES;
        for (Entry<LocalDateTime, Set<Vote>> entry : votes.entrySet())
        {
            long count = entry.getValue().stream().filter(votedYes).count(); 
            if (count > maxCount)
            {
                maxCount = count;
                bestDate = entry.getKey();
            }
        }
        return Optional.ofNullable(bestDate);
    }

    // As of https://stackoverflow.com/a/53771478/3182664
    public static Optional<LocalDateTime> getWinningDateB(Map<LocalDateTime, Set<Vote>> votes) 
    {
        return votes.entrySet() // Set<Entry<LocaleDateTime, Set<Vote>>
                .stream() // Stream<Entry<LocaleDateTime, Set<Vote>>
                .flatMap(e -> e.getValue().stream().filter(a -> a.getVote() == VoteType.YES)
                             .map(x -> e.getKey())) // Stream<LocalDateTime>
               .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) // Map<LocaleDateTime, Long>
               .entrySet() // Set<Entry<LocaleDateTime, Long>>
               .stream() // Stream<Entry<LocaleDateTime, Long>>
               .max(Comparator.comparingLong(Map.Entry::getValue)) // Optional<Entry<LocaleDateTime, Long>>
               .map(Map.Entry::getKey); // Optional<LocalDateTime>
    }    


    //=========================================================================
    enum VoteType {YES, NO, MAYBE}

    static class Vote {
        private String name;
        private VoteType vote;

        public Vote(String name, VoteType vote) {
            super();
            this.name = name;
            this.vote = vote;
        }
        public VoteType getVote()
        {
            return vote;
        }
    }

    private static Set<Vote> votesWith(VoteType... voteTypes)
    {
        Set<Vote> votes = new LinkedHashSet<Vote>();
        for (int i = 0; i < voteTypes.length; i++)
        {
            votes.add(new Vote("v" + i, voteTypes[i]));
        }
        return votes;
    }

}

将其与“纯流”解决方案进行比较,并考虑您希望将来阅读,理解和维护哪些代码。 然后明智地选择。

(我知道严格来说,这可能不是这个问题的理想答案。但有些人似乎故意过度使用流,并从中获得某种令人讨厌的自豪感。我偶尔也喜欢这种挑战 。但想象一下可能是未来必须保持这些功能性编程可憎的人让我不寒而栗...)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM