Подтвердить что ты не робот

Как я могу сортировать массивы в коллекции?

У меня есть список объектов. Эти объекты имеют (в частности) частный массив int (если это помогает, я могу перенести его в список). Для этого массива существует публичный Getter. Все массивы имеют одинаковый размер.

Я хочу отсортировать объект на основе их массивов следующим образом:

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 каждого массива (и так далее)
  • Если они не равны, результатом сравнения является конечный результат.

Мне удается найти их, например. только первый элемент массива с обычным компаратором, но я не знаю, как их искать для всех.

4b9b3361

Ответ 1

Хорошее решение для 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);
};

Тогда, учитывая a List<CustomObject>, вы можете сделать

list.sort(COMPARATOR);

(Comparator работает только для массивов одинаковой длины. Возможно, вы захотите его изменить).

Ответ 2

У меня есть коллекция (предпочтительный какой-то Список) объектов [...] Теперь я хочу отсортировать объект на основе своих массивов

Чтобы он был значимым, рассматриваемый Collection должен быть тем, который сохраняет порядок и позволяет изменять порядок элементов. В терминах интерфейсов коллекций высокого уровня только List имеет необходимые свойства, поэтому допустим, что ваш Collection, действительно, List.

Стандартный способ сортировки a List - использовать один из двух методов Collections.sort(). Один требует, чтобы элементы списка реализовали Comparable, а другой, более общий, требует, чтобы вы предоставили объект, реализующий Comparator для использования при определении желаемого относительного порядка объектов.

Массивы не реализуют Comparable (что эквивалентно утверждению, что у них нет "естественного порядка" ), но класс объектов, содержащих их, можно сделать так. Однако, вероятно, лучше создать отдельный класс Comparator, который реализует нужный вам порядок, и использовать экземпляр этого класса.

Ответ 3

Если я правильно понимаю это, следует следовать прямолинейному подходу:

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

Он производит:

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

и то, что вы ожидаете.

Ответ 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() factory принимает два аргумента: a Function для "извлечения" ключа из каждого объекта в списке и Comparator, который может сравнивать извлеченные ключи. Если извлеченный ключ имеет естественный порядок и реализует Comparable (например, это a String), тогда нет необходимости указывать Comparator.

В качестве альтернативы, если вы можете изменить MyClass, вы можете дать ему естественный порядок, выполнив Comparable и перемещая сравнение int[] с его методом compareTo(), как показано в других ответах. Затем вы можете использовать метод sort() без аргументов.

Ответ 5

Чтобы отсортировать список массивов int, вам нужна функция, которая выполняет лексикографическое сравнение массива int. Обычно это выражается как Comparator<int[]> в Java. Обычный способ реализации такой функции - сравнивать соответствующие значения слева направо, пока не будет обнаружено несоответствие; это несоответствие определяет порядок. Если конец массива достигнут без нахождения несоответствия, более короткий массив обычно считается менее длинным. Если длины равны, и все значения равны, массивы считаются равными.

Другие ответы использовали анонимные внутренние классы или лямбды для этой функции, но для таких вещей я предпочитаю обычный метод, который имеет правильную "форму", то есть принимает два аргумента 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));

Ответ 6

Рассмотрим этот объект:

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. Вы должны прочитать что такое интерфейс Comparable, и как его реализовать.

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

Ответ 7

Если ваш массив 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;
    }