【问题标题】:Java - Custom sort arrays as if they were tuplesJava - 自定义排序数组,就好像它们是元组一样
【发布时间】:2020-07-17 20:07:28
【问题描述】:

假设我有三个这样的数组:

a = [1, 2, 1, 4, 1]
b = [6, 1, 2, 4, 3]
c = [9, 6, 4, 7, 8]

我想对它们进行排序,就好像它们是一个元组 (a, b, c) 所以结果应该是这样的:

a    |    b    |    c
----------------------
1         2         4
1         3         8
1         6         9
2         1         6
4         4         7

我想编写以最有效的方式执行此操作的 Java 代码。谢谢!

【问题讨论】:

  • 您好,到目前为止您有没有尝试过的代码?
  • 你的排序标准是什么?
  • 如何制作一个元组数组,然后对其进行排序?您知道,使用 Java 的面向对象语言的特性,并创建对象。并行数组是一种反模式,你不应该拥有它们。 Anti-pattern: parallel collections by Jon Skeet。
  • 我只是用来测试的,所以暂时不需要样式

标签: java arrays sorting tuples


【解决方案1】:

您应该创建一个实现Comparable 接口的包装器对象,将数组中的值映射到此包装器类的实例,排序并使用这些值。

例子:

    static class X implements Comparable<X> {
        final int a;
        final int b;
        final int c;
        public X(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
        
        public int getA() {return a;}
        public int getB() {return b;}
        public int getC() {return c;}
        
        @Override
        public int compareTo(X that) {
            int cmp = Integer.compare(this.a, that.a);
            if (cmp == 0) {
                cmp = Integer.compare(this.b, that.b);
                if (cmp == 0) {
                    cmp = Integer.compare(this.c, that.c);
                }
            }
            return cmp;
        }
    }

    public static void main(String[] args){
        int[] a = {1, 2, 1, 4, 1};
        int[] b = {6, 1, 2, 4, 3};
        int[] c = {9, 6, 4, 7, 8};
        
        IntStream.range(0, a.length)
                 .mapToObj(i -> new X(a[i], b[i], c[i]))
                 .sorted()
                 .forEach(x -> {
                     System.out.printf("%d\t%d\t%d\t\n", x.getA(), x.getB(), x.getC()); 
                 });
    }

此代码打印预期输出:

1   2   4   
1   3   8   
1   6   9   
2   1   6   
4   4   7

如果需要,您可以将排序后的值设置回初始数组。

更新 您也可以使用 solution offered by Gerold Boser 的改进版本,尽管它对输入整数的范围和/或数组的计数有一定的限制:

static int m = 0;

public static void main(String[] args) {
    // range of integer values for 3 arrays is -2^20 ... 2^20 -1
    // or 0..2^21 -1
    final int[] a = { Integer.MAX_VALUE>>11, 1, 1, -4, Integer.MIN_VALUE/(1<<11) };
    final int[] b = { 6, 1, 1, 4, 3 };
    final int[] c = { 9, -6, -24, 7, 8 };

    IntSummaryStatistics stats = 
        Stream.of(Arrays.stream(a), Arrays.stream(b), Arrays.stream(c))
              .flatMapToInt(s -> s)
              .summaryStatistics();
    long min = stats.getMin();
    long max = stats.getMax() - min + 1;

    IntStream.range( 0, a.length )
             // use long to fit multiplication results into 63 bits
             .mapToLong( i -> (a[i] - min) * max * max + (b[i]-min) * max + (c[i]-min) )
             .sorted()
             .forEach( n -> {
                 // re-calculate long back to ints
                 a[m] = (int) (n / (max * max) % max + min);
                 b[m] = (int) (n / max % max + min);
                 c[m] = (int) (n % max + min);
                 System.out.printf("% 9d\t% d\t% d\t\n", a[m], b[m], c[m]);
                 m++;
             });
}

输出:

 -1048576    3   8  
       -4    4   7  
        1    1  -24 
        1    1  -6  
  1048575    6   9

注意可以通过使用BigDecimal 而不是long 来克服上述限制,以避免在执行乘法时溢出

【讨论】:

    【解决方案2】:

    Try it online!

    import static java.lang.System.out;
    
    import java.util.Arrays;
    import java.util.stream.IntStream;
    
    public class SO62961100 {
    
        static int i;
    
        public static void main( final String[] args ) {
    
            final int[] a = { 1, 2, 1, 4, 1 };
            final int[] b = { 6, 1, 2, 4, 3 };
            final int[] c = { 9, 6, 4, 7, 8 };
    
            IntStream.range( 0, a.length )
                    .map( i -> a[i] * 100 + b[i] * 10 + c[i] )
                    .sorted()
                    .forEach( n -> {
                        a[i] = n / 100 % 10;
                        b[i] = n / 10 % 10;
                        c[i++] = n % 10;
                    } );
            
            out.printf( "a=%s%nb=%s%nc=%s",
                    Arrays.toString( a ),
                    Arrays.toString( b ),
                    Arrays.toString( c ) );
        }
    }
    

    输出

    a=[1, 1, 1, 2, 4]
    b=[2, 3, 6, 1, 4]
    c=[4, 8, 9, 6, 7]
    

    【讨论】:

    • 这是一个有趣的实现,但它似乎对输入数据非常敏感。如果任何输入数组包含负值,则计算将被破坏。此外,如果事先检测到最大值(而不是硬编码的 10),则乘法可能会导致整数溢出。
    猜你喜欢
    • 2017-06-21
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多