Based upon a BlackJack Question, I got to wondering about how to indicate all of the winning hands. The original question simply asked, in effect, about the maximum of two numbers not greater than 21. So a method like
public int blackjack(int a, int b);
However, what if one wished to return all of the winning hands (assuming the location in the input array was a seat at a table), so a signature such as:
/**
* returns an array indicate the index in the specified hands that
* correspond to the winning locations. Will return an empty array if
* there are no winners. The length of the returned array is how many
* winning hands there were
* @param hands The total for each hand, where the index is the seat
* @return the index/"seat" where a winning hand was found; may return
* an empty array
*/
public int[] blackjack(int[] hands) { ... }
So based upon input data such as (just using 3 "players" at "seats" 0, 1, 2):
{ 17, 15, 23 }
{ 23, 25, 22 }
{18, 16, 18 }
{16, 21, 20}
I would expect output along the lines of:
Hands: [17, 15, 23] has winners at [0]
Hands: [23, 25, 22] has no winners
Hands: [18, 16, 18] has winners at [0, 2]
Hands: [16, 21, 20] has winners at [1]
In times past, I would have iterated the hands[]
array, found the maximum that was <= 21, and then iterated again finding each index that was equal to the maximum. So something like this:
public static int[] blackjackByIteration(int[] hands)
{
int max = 0;
int numAtMax = 0;
for (int i = 0; i < hands.length; ++i) {
if (hands[i] <= 21 && hands[i] > max) {
max = hands[i];
numAtMax = 1;
}
else if (hands[i] == max) {
++numAtMax;
}
}
int[] winningSeats = new int[numAtMax];
int loc = 0;
for (int i = 0; i < hands.length; ++i) {
if (hands[i] == max) {
winningSeats[loc++] = i;
}
}
return winningSeats;
}
However, I was wondering if there was a more efficient way to implement it via streams. I am cognizant that using Lambdas is not the solution to all problems. I believe, if I have read correctly, that is not possible to find the index of an int[]
array directly, so the approach must rely upon using a List<Integer>
, as suggested in the question referenced above.
I made an initial solution using Streams, but was wondering if there was a more efficient approach. I fully admit my understand of streams is limited.
public static int[] blackjackByStreams(int[] hands)
{
// set to an empty array; no winners in this hand
int[] winningSeats = new int[0];
// get the maximum that is <= 21
OptionalInt oi = Arrays.stream(hands).filter(tot -> tot <= 21).max();
// if there are any hands that are <= 21
if (oi.isPresent()) {
// have to make a list (?)
List<Integer> list = Arrays.stream(hands)
.boxed()
.collect(Collectors.toList());
// find the location(s) in the list
winningSeats = IntStream.range(0, list.size())
.filter(i -> list.get(i) == oi.getAsInt())
.toArray();
}
return winningSeats;
}
The two approaches return the same data, so it is not a question of functionality per se. Rather, is there any way to make the blackjackByStreams
better? Especially, is there a way to eliminate the creation of the List<Integer> list
?
Edit: I did read this question here, in which one answer suggested creating a custom collector. Not sure if that would be the only alternative approach.
Thank you for providing any insight.
You're missing the simple solution when you have found the maximum element. Just create a Stream over the indexes of the array directly instead of having an intermediate list:
public static int[] blackjackByIteration(int[] hands) {
OptionalInt oi = Arrays.stream(hands).filter(i -> i <= 21).max();
if (oi.isPresent()) {
int value = oi.getAsInt();
return IntStream.range(0, hands.length).filter(i -> hands[i] == value).toArray();
}
return new int[0];
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With