2015-04-22 3 views
9

Я создаю решатель рейтинга в покере, и мне приходится подсчитывать карты одного и того же ранга в наборе карт. Здесь я создаю HashMap и увеличиваю значение, если несколько рангов находятся в наборе.Подсчет объектов с одинаковым значением стоимости

private boolean isFourOfAKind() { 
     Map<RANK, Integer> rankDuplicates = new HashMap<>(); 
     for(Card card : cards) { 
      rankDuplicates.put(card.getRank(), rankDuplicates.getOrDefault(card.getRank(), 0) + 1); 
     } 
     return rankDuplicates.containsValue(4); 
    } 

Мне было интересно, можно ли использовать потоки, делать то же самое с потоками java. Это будет выглядеть примерно так:

private boolean isFourOfAKind() { 
     Map<RANK, Integer> rankDuplicates = cards.stream() 
      .map(Card::getRank) 
      .collect(Collectors.toMap(/*...something goes here..*/)); // of course this is very wrong, but you can see what I'm aiming at. 
     return rankDuplicates.containsValue(4); 
    } 

ответ

8

Похоже, что вы ищете что-то вроде

return cards.stream() 
     .collect(Collectors.toMap(Card::getRank, e -> 1, Integer::sum)) 
     //      keyMapper, valueMapper, mergeFunction 
     .containsValue(4); 
+0

Это действительно хорошо! –

+2

мне, '.collect (Collectors.groupingBy (Card :: getRank, Collectors.counting())' выглядит чище ... – Holger

+1

@ Хольгер На самом деле мне тоже. Не знал об этом при публикации этого ответа и создавал код на основе текущие и ограниченные знания.В любом случае вы, кажется, много знаете о Java 8. Могу ли я спросить, откуда взялись ваши знания? Можете ли вы порекомендовать хороший источник информации о нем помимо официального учебника (книг или сайтов/статей)? – Pshemo

5

Другой вариант с groupingBy:

import static java.util.stream.Collectors.counting; 
import static java.util.stream.Collectors.groupingBy; 

... 

private boolean isFourOfAKind() { 
    return cards.stream() 
       .collect(groupingBy(Card::getRank, counting())) 
       .containsValue(4L); 

} 

В основном вы группировать карты ранги так что у вас есть Map<Rank, List<Card>>

Затем вы используете dow nstream collector counting(), чтобы сопоставить каждый список с его количеством элементов, так что конечный результат равен Map<Rank, Long> (вы также можете использовать summingInt(i -> 1) вместо counting(), если вам действительно нужно иметь Map<Rank, Integer>).

+4

В качестве дополнения, если 'Rank' является' enum', используя '.collect (groupingBy (Card :: getRank,() -> новый EnumMap (Rank.class)), counting())) 'может быть немного более эффективным. – Holger

Смежные вопросы