I'm using JavaSE-17
A - I have 3 things :
(1) A list of n cards, say:
List<Card> cards; // where Card is a Record
Interger numberOfCards = cards.size(); // Usually cards.size() == 52
(2) A number of players, say:
Integer numberOfPlayers;
(3) A number of cards to deal to each player, say:
Integer numberOfCardsPerPlayer;
Assuming :
numberOfCardsPerPlayer
).numberOfCardsPerPlayer * numberOfPlayers <= numberOfCards
B - I want to get a list containing numberOfPlayers
lists of cards where each list of cards contains exactly numberOfCardsPerPlayer
cards.
For example, if I have a deck of 52 cards, 3 players and 5 cards per player, I should get a list of 3 lists of cards with each list of cards contains 5 cards.
C - Question
Is there a way of doing it without the AtomicInteger
and without calling stream
two times? (and without using loops or external librairies).
D - The code below works but feels strange (using AtomicInteger
feels like cheating because I try to avoid mutability and I'm not sure calling stream
two times is good practice)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class DeckOfCards {
public enum Suit {CLUB, DIAMOND, HEART, SPADE}
public enum Value {TWO, THREE, FOUR, FIVE, SIX, SEVEN}
public record Card(Suit suit, Value value) {}
public static void main(String[] args) {
Integer numberOfPlayers = 3;
Integer numberOfCardsPerPlayer = 2;
List<Card> cards = new ArrayList<Card>(
Arrays.asList(new Card(Suit.CLUB, Value.TWO),
new Card(Suit.DIAMOND, Value.SIX),
new Card(Suit.HEART, Value.FOUR),
new Card(Suit.SPADE, Value.SEVEN),
new Card(Suit.HEART, Value.FIVE),
new Card(Suit.CLUB, Value.FOUR),
new Card(Suit.SPADE, Value.THREE)
)
);
AtomicInteger counter = new AtomicInteger();
List<List<Card>> result =
cards.stream()
.limit(numberOfCardsPerPlayer * numberOfPlayers)
.collect(Collectors.groupingBy(x -> counter.getAndIncrement() / numberOfCardsPerPlayer))
.values()
.stream()
.collect(Collectors.toList());
System.out.println(result);
// Prints :
// [
// [Card[suit=CLUB, value=TWO], Card[suit=DIAMOND, value=SIX]]
// , [Card[suit=HEART, value=FOUR], Card[suit=SPADE, value=SEVEN]]
// , [Card[suit=HEART, value=FIVE], Card[suit=CLUB, value=FOUR]]
// ]
}
}
Here is one way. I am using a record to simulate cards as as simple integer.
IntStream
.subList
taking the proper number of cards for each player.mapped
to place each new "hand" on the stream.hand
in list of lists.record Card(int a) {
}
Integer numberOfPlayers = 3;
Integer numberOfCardsPerPlayer = 5;
List<Card> cards = IntStream.range(0, numberOfPlayer*numberOfCardsPerPlayer).mapToObj(i -> new Card(i))
.toList();
List<List<Card>> hands = IntStream.range(0, numberOfPlayers)
.<List<Card>>mapToObj(player -> new ArrayList<>(
cards.subList(player * numberOfCardsPerPlayer,
(player + 1) * numberOfCardsPerPlayer)))
.toList();
hands.forEach(System.out::println);
prints
[Card[a=0], Card[a=1], Card[a=2], Card[a=3], Card[a=4]]
[Card[a=5], Card[a=6], Card[a=7], Card[a=8], Card[a=9]]
[Card[a=10], Card[a=11], Card[a=12], Card[a=13], Card[a=14]]
The reason I add the subList
as an argument to ArrayList
is because a subList provided a view of the original list. So if you alter a subList, you change the original list. Creating a new ArrayList
makes the sublist
independent.