如何从列表中计算玩家的等级

时间:2015-07-05 04:09:36

标签: java java-8

说我有以下简单的数据结构。

items=${','.join(data)}

到目前为止一切顺利。现在的问题是,我如何获得球员排名? 我有另一种排名数据结构,即 -

public class Player {
    private Long id;
    private String name;
    private Integer scores;

    //getter setter
}

所以我有一个播放器列表,我想计算一个排名列表,我想使用java8 stream api。

我有一个名为public class Ranking { private Integer score; private Integer rank; //getter/setter } 的服务,如下所示

PlayerService

计算很简单,得分最高的人排名最高。

如果我有public class PlayerService { @Autowired private PlayerRepository playerRepository; public List<Ranking> findAllRanking(Long limit) { List<Player> players = playerRepository.findAll(); // calculation return null; } 分,那么排名将是

5,7,8,9,3

任何帮助将不胜感激。

5 个答案:

答案 0 :(得分:5)

试试这个:

     List<Player> players = new ArrayList<Player>() {{
        add(new Player(1L, "a", 5));
        add(new Player(2L, "b", 7));
        add(new Player(3L, "c", 8));
        add(new Player(4L, "d", 9));
        add(new Player(5L, "e", 3));
        add(new Player(6L, "f", 8));
     }};
     int[] score = {Integer.MIN_VALUE};
     int[] no = {0};
     int[] rank = {0};
     List<Ranking> ranking = players.stream()
         .sorted((a, b) -> b.getScores() - a.getScores())
         .map(p -> {
             ++no[0];
             if (score[0] != p.getScores()) rank[0] = no[0];
             return new Ranking(rank[0], score[0] = p.getScores());
         })
         // .distinct() // if you want to remove duplicate rankings.
         .collect(Collectors.toList());
     System.out.println(ranking);
    // result:
    // rank=1, score=9
    // rank=2, score=8
    // rank=2, score=8
    // rank=4, score=7
    // rank=5, score=5
    // rank=6, score=3

变量scorenorank.map()中lambda函数中的自由变量。所以不能重新分配它们。如果他们的类型是int而不是int[],则无法编译代码。

答案 1 :(得分:3)

接受答案的问题是,一旦你并行转换流(players.parallelStream()),你会得到意想不到的结果,因为你从单例数组中读取/更新值的竞争条件

也许你可以将你的任务分解成多个步骤。首先将分数列表按相反顺序排序,然后生成索引流。从那里,您将每个索引映射到其对应的排名。

如果您需要为同一分数获得相同的排名,则需要在mapToObj语句中测试多个条件。它使代码不是很漂亮,但你总是可以在辅助方法中提取它。

List<Integer> scores = players.stream().map(Player::getScores).sorted(reverseOrder()).collect(toList());

List<Ranking> rankings =
        IntStream.range(0, scores.size())
                .mapToObj(i -> i == 0 ? new Ranking(1, scores.get(i)) :
                                        scores.get(i - 1).equals(scores.get(i)) ? new Ranking(i, scores.get(i)) :
                                                                                  new Ranking(i + 1, scores.get(i)))
                .collect(toList());

那就是说,如果你不打算并行化这一步,我会选择好的旧的for循环。

如果您不需要为相同的分数获得相同的排名,则可以查看此帖子Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip),例如proton-pack

List<Ranking> rankings = StreamUtils.zip(IntStream.rangeClosed(1, players.size()).boxed(),
                                         players.stream().map(Player::getScores).sorted(reverseOrder()),
                                         Ranking::new)
                                    .collect(toList());

答案 2 :(得分:0)

我相信你希望玩家拥有一个属性,一个排名,对吗?为什么不在播放器的类中添加成员变量(或实例变量)。我相信我们在面向对象编程中称之为“聚合”或“对象组合”。 https://en.wikipedia.org/wiki/Object_composition

考虑一下......

 public class Player {
    private Long id;
    private String name;
    private Integer scores;
    private Ranking rank;
    ...
    //You can add a constructor that include the Ranking, you can also add setter and getters to set the Ranking object for this player. 

    public int getRank()
    {
        return rank.getRank(); //assuming class Ranking has getRank() method
    }
    public void setRank(int rank)
    {
         rank.setRank(rank); //assuming class Ranking has setRank()
    }
 }

答案 3 :(得分:0)

为什么不根据他们的分数对付款人进行排序? 所以..沿着这些方向的东西:

public class PlayerService { 

@Autowired 
private PlayerRepository playerRepository;


public List<Ranking> findAllRanking(Long limit) {
    List<Player> players = playerRepository.findAll();

   // --- sorting players according to their scores
   Collections.sort(players, new Comparator<Player>(){
          @Override
          public int compare(Players thiz, Player that){
                 return new Integer(thiz.scores).compareTo(that.scores);
           });

   List<Ranking> rankingList = new ArrayList<>();
   int i = 0;
   for(Player p : players){
       rankingList.add(new Ranking(p.score, ++i));
   }

    return rankingList; 
} 

我将此作为练习留给您确定是否产生您想要的订单或完全相反。

答案 4 :(得分:0)

如果您首先重新考虑先决条件,您将获得更好的解决方案。作为旁注,我不明白,为什么在整个代码中使用Integer。如果您确实null rankscores的可能值,则解决方案将变得更加复杂。由于我怀疑这是你想要的,我建议改为使用int

最大的障碍是您需要生成这个相当多余的Ranking个实例。所有你需要的,是分数一次排序,他们的排名由他们的位置隐含:

List<Integer> rankAndScore = players.stream().map(Player::getScores)
    .sorted(Comparator.reverseOrder()).collect(toList());

此结果列表包含降序排名,排名由位置隐含,第一项具有第一名,第二项具有第二名,依此类推。无需明确存储排名数。唯一要注意的是集合索引从零开始,而排名从一开始。

在问题中打印值的一种方法是:

System.out.println("rank\tscore");
IntStream.range(0, rankAndScore.size())
    .forEachOrdered(r->System.out.println(r+1+"\t"+rankAndScore.get(r)));

或者,您可以简单地使用int[]数组来表示排名和分数:

int[] rankAndScore = players.stream().mapToInt(Player::getScores).sorted().toArray();
System.out.println("rank\tscore");
IntStream.rangeClosed(1, rankAndScore.length)
    .forEachOrdered(r->System.out.println(r+"\t"+rankAndScore[rankAndScore.length-r]));

IntStream不支持按降序排序,但如图所示,可以通过按升序排序并调整数组处理来简单地修复它。