简体   繁体   English

通过使用 Java Stream API 组合来自 int 数组的唯一值来生成最小 int 值

[英]Make minimum int value by combining unique values from int array using Java Stream API

I need to implement a method int minValue(int[] values) using the Stream API.我需要使用 Stream API 实现一个方法int minValue(int[] values) The method takes an array of ints from 0 to 9, takes unique values, and returns a minimum possible number combined of these unique digits.该方法采用从 0 到 9 的整数数组,采用唯一值,并返回这些唯一数字组合的最小可能数字。 The array must not be converted to a string.该数组不得转换为字符串。

Example #1: input {1,2,3,3,2,3}, returned value - 123;示例 #1:输入 {1,2,3,3,2,3},返回值 - 123;

Example #2: input {9,8}, returned value – 89.示例 2:输入 {9,8},返回值 – 89。

I don't know how to implement the multiplication of every stream member to the sequence of values using Stream API: if my sorted stream has values (5, 3, 1) I need to perform 5 * 1 + 3 * 10 + 1 * 100. How can I do this using Stream API?我不知道如何使用 Stream API 实现每个流成员与值序列的乘法:如果我的排序流具有值 (5, 3, 1) 我需要执行 5 * 1 + 3 * 10 + 1 * 100. 我如何使用 Stream API 做到这一点?

My solution with a simple for loop is below:我的简单 for 循环解决方案如下:

    private static int minValue(int[] values) {
        int result = 0;
        int capacity = 1;

        int[] ints = Arrays.stream(values)
            .boxed()
            .distinct()
            .sorted(Collections.reverseOrder())
            .mapToInt(Integer::intValue).toArray();

        for (int i = 0; i < ints.length; i++) {
            result = result + ints[i] * capacity;
            capacity = capacity * 10;
        }

        return result;
    }

Calling distinct , sort , and reduce respectively is a way to go:分别调用distinctsortreduce是一种方法:

int result = Arrays.stream(values)
            .distinct()                   // unique digits
            .sorted()                     // sorted digits (descending)
            .reduce((l, r) -> l * 10 + r) // shifts a digit and adds another one
            .orElseThrow();               // this is up to you

The reduce part might look tricky, let's inspect it step-by-step on the 1234 number sequence (already distinct and sorted). reduce 部分可能看起来很棘手,让我们在1234数字序列(已经不同且已排序)上逐步检查它。

  1. l=1 and r=2 -> l * 10 + r gives 10 + 2 = 12 (new l ). l=1r=2 -> l * 10 + r给出10 + 2 = 12 (新的l )。
  2. l=12 and r=3 -> l * 10 + r gives 120 + 3 = 123 (new l ) l=12r=3 -> l * 10 + r给出120 + 3 = 123 (新的l
  3. l=123 and r=4 -> l * 10 + r gives 1230 + 4 = 1234 (finish) l=123r=4 -> l * 10 + r给出1230 + 4 = 1234 (完成)

Edit: As Holger pointed out, this would work only in the sequential processing.编辑:正如霍尔格指出的那样,这仅适用于顺序处理。 The following solution would work in parallel:以下解决方案将并行工作:

String result = Arrays.stream(values)
            .distinct()
            .sorted()
            .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
            .toString();
int intResult = Integer.parseInt(result);

This kind of repeated calculation can be converted into a formally correct stream operation, as has been demonstrated in How to compute the hash code for a stream in the same way as List.hashCode()这种重复计算可以转换为形式上正确的流操作,如如何以与 List.hashCode() 相同的方式计算流的哈希码中所述

The difference between these tasks only lies in the factor, you need factor ten, the other needed 31.这些任务的区别只在于因子,你需要因子10,其他需要31。

But the solution is not simple.但解决方案并不简单。 You could write a simple but formally wrong reduction operation with a non-associative function which still works in sequential execution with the current implementation, but that would be an abuse of the Stream API and begs the question what you hope to gain from using the Stream API.您可以使用非关联函数编写一个简单但形式上错误的归约操作,该函数仍然可以与当前实现顺序执行,但这将是对 Stream API 的滥用,并回避了您希望从使用 Stream 中获得什么的问题应用程序接口。

The Stream API does not always lead to a simple or efficient solution. Stream API 并不总是导致简单或有效的解决方案。 That especially applies to your initial task of identifying all distinct digits of a very small set and getting them in an ordered intermediate result.这尤其适用于识别非常小的集合的所有不同数字并将它们放入有序中间结果的初始任务。

You can implement the whole operation as您可以将整个操作实现为

private static int minValue(int[] values) {
    int allDigits = 0;
    for(int i: values) allDigits |= 1 << i;

    int result = 0;
    for(int digit = 1; digit < 10; digit++)
        if((allDigits & (1 << digit)) != 0) result = result * 10 + digit;

    return result;
}

Since the task description states that all values are in the 0..9 range, they easily fit into a single int value which has 32 bits.由于任务描述指出所有值都在 0..9 范围内,因此它们很容易适合具有 32 位的单个int值。 When we set the 0th to 9th bit when encountering that digit, we have the information in an intrinsically sorted way.当我们在遇到该数字时将第 0 位设置为第 9 位时,我们以一种固有的排序方式获得了信息。 The second loop just probes each digit's bit and when the digit is present, it performs a result = result * 10 + digit;第二个循环只是探测每个数字的位,当数字出现时,它执行result = result * 10 + digit; calculation similar to the linked Q&A which works when iterating from smallest to largest digit, but iterating from largest to smallest similar to your approach would work too:类似于从最小到最大数字迭代时有效的链接问答的计算,但从最大到最小迭代类似于您的方法也可以:

private static int minValue(int[] values) {
    int allDigits = 0;
    for(int i: values) allDigits |= 1 << i;

    int result = 0;
    for(int digit = 9, factor = 1; digit > 0; digit--)
        if((allDigits & (1 << digit)) != 0) {
            result = result + digit * factor;
            factor *= 10;
        }

    return result;
}

Here is a way to do it:这是一种方法:

    final int[] result = {0};
    Arrays.stream(values).boxed().distinct().sorted()
            .mapToInt(Integer::intValue).forEach(i -> result[0] = i + result[0] * 10);
    return result[0];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM