【问题标题】:Is There a More Efficient Java 8 Stream approach for finding the index in an int[]?是否有更有效的 Java 8 Stream 方法来查找 int[] 中的索引?
【发布时间】:2016-04-25 20:17:59
【问题描述】:

基于BlackJack Question,我想知道如何指示所有获胜的手牌。最初的问题实际上只是问了两个不大于 21 的数字的最大值。所以像

这样的方法
public int blackjack(int a, int b);

但是,如果一个人希望返回所有获胜的手牌(假设输入数组中的位置是桌子上的一个座位),那么签名如下:

/**
 * 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) { ... }

因此基于输入数据,例如(仅在“座位”0、1、2 使用 3 个“玩家”):

{ 17, 15, 23 }
{ 23, 25, 22 }
{18, 16, 18 }
{16、21、20}

我希望输出如下:

手牌:[17, 15, 23] 在 [0] 处获胜
手牌:[23, 25, 22] 没有赢家
手牌:[18, 16, 18] 在 [0, 2] 处获胜
手牌:[16, 21, 20] 在 [1] 处获胜

在过去,我会迭代 hands[] 数组,找到

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;
}

但是,我想知道是否有更有效的方法通过流来实现它。我知道using Lambdas is not the solution to all problems。我相信,如果我没看错的话,那是不可能直接找到int[] 数组的索引的,所以该方法必须依赖于使用List&lt;Integer&gt;,正如question referenced above 中所建议的那样。

我使用 Streams 做了一个初始解决方案,但想知道是否有更有效的方法。我完全承认我对流的理解是有限的。

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;
}

这两种方法返回相同的数据,所以这不是功能问题本身。相反,有什么方法可以让blackjackByStreams 变得更好?特别是,有没有办法消除List&lt;Integer&gt; list 的创建?

编辑:我确实读过this question here,其中一个答案建议创建一个自定义收集器。不确定这是否是唯一的替代方法。

感谢您提供任何见解。

【问题讨论】:

  • 流一般不会提高效率。流通常不会比正常写出的相同程序更快,而且通常更慢。它们只是一个图书馆,它们不是魔法。您的初始代码将至少一样快,甚至可能更快。

标签: java arrays java-8 java-stream


【解决方案1】:

当您找到最大元素时,您错过了简单的解决方案。只需直接在数组的索引上创建一个 Stream,而不是使用中间列表:

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];
}

【讨论】:

  • 我以为我已经尝试过了并收到了一个错误,但考虑到它可能尝试使用Arrays.stream(hands)... 而不是IntStream.range(0, hands.length)...。方法的简洁性大大提高!
  • @Holger 不一定,该数组可以包含所有大于 21 的值,因此 OptionalInt 将为空。不幸的是,OptionalInt 上没有mapToObj,否则我们可以有oi.mapToObj(...).orElseGet(() -&gt; new int[0])
  • 确实,这很复杂。我应该再喝杯咖啡......你可以做些什么来避免ifint value = oi.orElse(21); 并继续第二个流操作,当没有匹配时会产生一个空数组。我仍然认为,应该有一些更容易的东西……
猜你喜欢
  • 1970-01-01
  • 2012-04-18
  • 1970-01-01
  • 2023-03-10
  • 2017-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多