Java的8个流,如何获得前N个计数?

anthony44:

我需要你的意见,以简化下面这段代码。我有赢得比赛的ID的球员名单。我想提取从该列表中的2名最好的球员(2名球员谁拥有更好的电量匹配Id的)提取一次,我要回到初始列表进行其他操作。我认为这是可能改善优化或阅读方面的代码。如果你能帮助我。

public class PlayerStatistics {
    int id
    String name;
    int idMatchWon; // key from Match

    // getter , setter
}

public static void main(String[] args) throws Exception {

    List<PlayerStatistics> _players = new ArrayList<PlayerStatistics>();

    _players.add(initialize(1,'John',4));
    _players.add(initialize(2,'Teddy',2));
    _players.add(initialize(3,'Kevin',3));

    // How to get Top 2
    List<PlayerStatistics> _top2Players = extractTop2Players(_players);
}

private List<PlayerStatistics> extractTop2Players (List<PlayerStatistics> _list) {

    List<PlayerStatistics> _topPlayers = new ArrayList<PlayerStatistics>();

    // 1. Group and count 
    Map<String, Long> _players = _list
            .stream()
            .filter(x -> (!"".equals(x.getName()) && x.getName()!= null) )
            .collect(
                    Collectors.groupingBy(
                            PlayerStatistics::getName, Collectors.counting()
                    )
            );
    ;

    // 2 Best Palyers
    Set<String> _sortedPlayers = _players.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Collections.reverseOrder()))
            .limit(2)
            .map(Entry::getKey)
            .collect(Collectors.toSet())
    ;

    // 3. Rebuild list 
    _topPlayers = _list
            .stream()
            .filter(x -> _sortedPlayers.contains(x.getName()))
            .collect(Collectors.toList())
    ;

    return _topPlayers;
}


private PlayerStatistics initialize (int id, String name, int year, int month, int won, int lost) {
    return 
        new PlayerStatistics()
            .withId(id)
            .withName(name)
            .withIdMatchWon(won)
        );
}
费德里科·佩拉尔塔夏弗纳:

首先,让我们的状态,你的代码是完全正确的。它做什么需要做,它甚至可以通过使用组进行了优化。这可以从两个方面进一步提高,但:

  1. 时间复杂度:要排序整个数据集,其中有一个时间复杂度O(mlogm),以m作为您的播放器初始列表的大小。随即,你正在服用的顶部N列表的元素,N << m

    下面我展示的方式,以提高时间算法的复杂性O(mlogN),这意味着在特定情况下,它会成为O(m)(这是因为N=2,如此logN=log2=1)。

  2. 您遍历数据集3次:第一个你迭代的球员名单,以创建计数的地图,那么你迭代这个地图获得与顶部N的球员,最后你又迭代的球员名单检查每个玩家是否属于集合的顶级N球员。

    这可以提高到超过该数据集只进行2次:第一个创建地图的计数(类似于你已经完成),另一个创建将只保留顶部的结构N元素,通过分类数下降,结果准备好一旦遍历结束要返回。

重要提示:下面的解决方案要求您的PlayerStatistics类实现hashCodeequals方法一致。

首先,我们有一个通用的方法topN即(不奇怪)提取顶端N从任何给定的地图元素。这是通过按值进行比较其项,降(在这个版本中,值V必须Comparable<V>的,但这种算法可以很容易地扩展到没有实现支撑性值Comparable<V>提供了一个自定义的Comparator<V>):

public static 
<K, V extends Comparable<? super V>, T extends Comparable<? super T>>
Collection<K> 
topN(
        Map<K, V> map, 
        int N,
        Function<? super K, ? extends T> tieBreaker) {

    TreeMap<Map.Entry<K, V>, K> topN = new TreeMap<>(
        Map.Entry.<K, V>comparingByValue()      // by value descending, then by key
            .reversed()                         // to allow entries with duplicate values
            .thenComparing(e -> tieBreaker.apply(e.getKey())));

    map.entrySet().forEach(e -> {
      topN.put(e, e.getKey());
      if (topN.size() > N) topN.pollLastEntry();
    });

    return topN.values();
}

在这里,topN TreeMap表现为优先级队列大小的N(虽然我们加起来N+1元素)。首先,我们把进入topN地图,然后,如果地图比多N条目,我们立即调用的pollLastEntry方法就可以了,它去除了具有最低优先级的条目(按的按键的顺序TreeMap)。这保证了在遍历,该topN地图将只包含上面N的条目,已经排序。

请注意,我用一个比较,首先各种各样的TreeMap<Map.Entry<K, V>, K>由值V降序排列,然后通过按键K这是通过的帮助下实现的Function<? super K, ? extends T> tieBreaker功能,这将每个键K的值T必须是Comparable<T>这一切都使地图包含有重复值的条目V,而无需按键K也可以Comparable<K>

最后,你会按如下方式使用上述方法:

Map<PlayerStatistics, Long> counts = yourInitialListOfPlayers.stream()
    .filter(x -> !"".equals(x.getName()) && x.getName() != null)
    .collect(Collectors.groupingBy(x -> x, Collectors.counting()));

Collection<PlayerStatistics> top2 = topN(counts, 2, PlayerStatistics::getName);

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章