如何在集合中对数组进行排序?

时间:2016-03-24 19:38:15

标签: java sorting collections

我有一个对象列表。那些对象(以及其他)有一个私有的int数组(如果它有帮助,我可以将它转移到List中)。这个数组有一个公共Getter。所有阵列的大小都相同。

我想根据它们的数组对Object进行排序:

Unsorted:
{[0, 1, 4, 5], 
 [0, 0, 2, 3],
 [0, 1, 1, 2]}

Sorted:
{[0, 0, 2, 3],
 [0, 1, 1, 2],
 [0, 1, 4, 5]}

用词(称为词典编纂):

  • 比较每个数组的第一个int
  • 如果它们相等,则比较每个数组的下一个int(依此类推)
  • 如果它们不相等,则比较结果是最终结果。

我设法搜索它们,例如只有具有普通比较器的数组的第一个元素,但我不知道如何搜索它们。

7 个答案:

答案 0 :(得分:12)

一个不错的Java 8解决方案

static final Comparator<CustomObject> COMPARATOR = (o1, o2) -> {
    int[] arr1 = o1.getArray();
    int[] arr2 = o2.getArray();
    return IntStream.range(0, arr1.length)
                    .map(i -> Integer.compare(arr1[i], arr2[i]))
                    .filter(i -> i != 0)
                    .findFirst()
                    .orElse(0);
};

然后,给定List<CustomObject>,您可以

list.sort(COMPARATOR);

Comparator仅适用于相同长度的数组。您可能需要修改它。)

答案 1 :(得分:8)

  

我有一个对象的集合(最好是某种列表)   [...]   现在我想根据它们的数组对对象进行排序

要使它有意义,有问题的Collection必须是保留顺序并允许您重新排序元素的List。就高级集合界面而言,只有Collection具有所需的属性,因此我们假设您的List确实是List

Collections.sort()进行排序的标准方法是使用两种Comparable方法之一。一个要求列表元素实现Comparator,而另一个更通用的要求你提供一个实现Comparable的对象,用于确定对象的所需相对顺序。

数组不实现Comparator(这相当于说它们没有&#34;自然顺序&#34;),但是包含它们的对象的类可以这样做。但是,可能更好的形式是编写一个单独的HideCaret(YourAwesomeTEdit.Handle); 类来实现您想要的顺序,并使用该类的实例。

答案 2 :(得分:6)

如果我理解正确,遵循直截了当的方法应该有效:

Import-Csv .\test.csv | Where-Object {$_.Enabled -eq 'True'} --> what goes here to replace 'True' with 'A'?

它产生:

public class SortArrays {

    public static void main(String[] args) {
        List<int[]> listOfArrays = new ArrayList<>(4);
        listOfArrays.add(new int[]{0, 1, 4, 5});
        listOfArrays.add(new int[]{0, 0, 2, 3});
        listOfArrays.add(new int[]{0, 1, 1, 2});
        Collections.sort(listOfArrays, (o1, o2) -> {
            for (int i = 0; i < o1.length; i++) {
                if (o1[i] < o2[i])
                    return -1;
                if (o1[i] > o2[i])
                    return 1;
            }
            return 0;
        });
        listOfArrays.forEach(a -> System.out.println(Arrays.toString(a)));
    }
}

这就是你所期待的。

答案 3 :(得分:4)

假设您的课程看起来像这样,并且您无法对其进行修改。

final class MyClass
{
  private final int[] key;

  MyClass(int[] key)
  {
    this.key = key.clone();
  }

  public int[] getKey()
  {
    return key.clone();
  }
}

然后,您可以通过实现MyClass接口为Comparator的实例定义排序。更一般地说,我实际上要为int[]实施比较器:

final class IntArrayComparator
  implements Comparator<int[]>
{

  @Override
  public int compare(int[] a, int[] b)
  {
    int n = Math.min(a.length, b.length);
    for (int idx = 0; idx < n; ++idx) {
      if (a[idx] != b[idx])
        return (a[idx] < b[idx]) ? -1 : +1;
    }
    return a.length - b.length;
  }
}

鉴于这个新的比较器和您现有的类型,它很容易排序:

final class Test
{
  public static void main(String... arg)
  {
    /* Create your list somehow... */
    List<MyClass> list = new ArrayList<>();
    list.add(new MyClass(new int[]{0, 1, 4, 5}));
    list.add(new MyClass(new int[]{0, 0, 2, 3}));
    list.add(new MyClass(new int[]{0, 1, 1, 2}));

    /* Now sort the list. */
    list.sort(Comparator.comparing(MyClass::getKey, new IntArrayComparator()));

    /* Display the result... */
    list.stream().map(MyClass::getKey).map(Arrays::toString).forEach(System.out::println);
  }
}

你应该得到输出:

[0, 0, 2, 3]
[0, 1, 1, 2]
[0, 1, 4, 5]

我使用的comparing()工厂方法有两个参数:Function到&#34;提取&#34;列表中每个对象的键,以及可以比较提取的键的Comparator。如果提取的密钥具有自然排序,并实现Comparable(例如,它是String),那么就没有必要指定Comparator。< / p>

或者,如果您可以修改MyClass,则可以通过实施Comparable并将int[]比较移至其compareTo()方法来为其提供自然顺序,如其他答案。然后,您可以使用不带参数的sort()方法。

答案 4 :(得分:4)

为了对int数组列表进行排序,您需要一个执行int数组的词典比较的函数。这通常表示为Java中的Comparator<int[]>。实现这种功能的通常方法是从左到右比较相应的值,直到找到不匹配为止;不匹配决定了订单。如果在没有找到不匹配的情况下到达阵列的末尾,则通常认为较短的阵列小于较长的阵列。如果长度相等,并且所有值都相等,则认为数组相等。

其他答案对此函数使用了匿名内部类或lambdas,但对于这样的事情,我更喜欢具有正确&#34;形状&#34;的普通方法,即需要两个int[]参数并返回int,这是比较的结果。这使它可以用作方法参考的目标。

int arrayCompare(int[] a, int[] b) {
    int len = Math.min(a.length, b.length);
    for (int i = 0; i < len; i++) {
        int c = Integer.compare(a[i], b[i]);
        if (c != 0) {
            return c;
        }
    }
    return a.length - b.length;
}

请注意谨慎使用Integer.compare()而不是减法,以避免潜在的溢出问题。用法如下:

    List<int[]> arrays = Arrays.asList(
        new int[] { 0, 1, 4, 5 },
        new int[] { 0, 0, 2, 3 },
        new int[] { 0, 1, 1, 2 }
    );

    arrays.sort(this::arrayCompare);
    arrays.forEach(a -> System.out.println(Arrays.toString(a)));

在JDK 9中,词典数组比较函数已添加到Arrays类中。见Arrays.compare(int[], int[])。其他基本类型和引用类型也有重载。 int[]重载替换了我上面写的arrayCompare函数,因此您可以按如下方式重写排序调用:

    arrays.sort(Arrays::compare);

Arrays类中比较函数的优点是它们可以由JVM专门处理,例如,它可以使用向量指令来加速数组比较。

针对您的具体问题,您似乎没有int[]的列表,但是您有一个MyObject类型的对象列表,其中包含int[]个你想用作排序键。假设获取数组的方法是调用方法getArray()。您可以按如下方式对列表进行排序:

    myObjects.sort(Comparator.comparing(MyObject::getArray, Arrays::compare));

答案 5 :(得分:2)

考虑这个对象:

public class Foo {

    private int[] values;

    public Foo(int[] values) { 
        this.values = values;
    }
}

制作对象列表:

ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo(new int[] {0, 1, 4, 5}));
foos.add(new Foo(new int[] {0, 0, 2, 3}));
foos.add(new Foo(new int[] {0, 1, 1, 2}));

现在我们要对对象列表进行排序,所以我们要做的第一件事就是使对象实现Comparable。您应该阅读更多what the Comparable interface ishow to implement it

public class Foo implements Comparable<Foo> {
    ...
}

此时,编译器会抱怨您没有实现接口所需的所有方法。那样做。

public class Foo implements Comparable<Foo> {
    ...
    public int compareTo(Foo f) {
        // Return a negative integer if this < f
        // Return zero if this == f
        // Return a positive integer if this > f
    }
}

就实际比较逻辑而言,您的问题并不是非常具体地用于比较两个对象的确切规则。但是,从这个例子中,我可以猜测这些是你的规则:

  • 比较每个数组的第一个数字。
    • 如果它们相等,请使用每个数组的下一个数字重复此过程。
    • 否则,此比较的结果是最终结果。

如果一个数组比另一个数组短,那么您没有真正指定要执行的操作。我将把实际的算法留给你(尽管其他答案似乎已经为你做了)。但是,我会指出,因为值是私有字段,除非您为字段实现getter或将其公开,否则您将无法使用该字段进行比较。

public int[] getValues() { return values; }

答案 6 :(得分:2)

如果您的数组是private并且您确实想要提供一个访问器方法,那么为该类实现Comparable并执行类似下面的代码。

如果您有访问者方法,请在Comparator

中执行此操作

此代码does not assume arrays to be of same size因此适用于不同大小的数组。请根据您的需要进行更改。

PS - 未编译/测试

public int compareTo(Object o) {
        CustomClass other = (CustomClass ) o;

        int i = 0;
        while (i <= numbers.length && i <= other.numbers.length) {
            if (numbers[i] < other.numbers[i])
                return -1;
            else if (numbers[i] > other.numbers[i])
                return 1;
            ++i;
        }

        if (numbers.length < other.numbers.length)
            return -1;
        else if (numbers.length > other.numbers.length)
            return 1;

        return 0;
    }