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

В Java, как вы сортируете один список на основе другого?

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

Мой вариант использования: пользователь имеет список элементов изначально (listA). Они переупорядочивают элементы и хотят сохранить этот порядок (listB), однако из-за ограничений я не могу сохранить порядок на сервере, поэтому мне нужно отсортировать список A после того, как я его извлечу.

Итак, в основном у меня есть 2 ArrayLists (listA и listB). Один с конкретным заказом списки должны быть в (listB), а другой - список элементов (listA). Я хочу сортировать listA на основе listB.

4b9b3361

Ответ 1

Collections.sort(listB, new Comparator<Item>() {
    public int compare(Item left, Item right) {
        return Integer.compare(listA.indexOf(left), listA.indexOf(right));
    }
});

Это довольно неэффективно, и вам, вероятно, следует создать Map<Item, Integer> из списка A, чтобы быстрее находить позиции элементов.

У Guava есть готовый к использованию компаратор для этого: Ordering.explicit()

Ответ 2

Java 8:

Collections.sort(listToSort, 
    Comparator.comparing(item -> listWithOrder.indexOf(item)));

или лучше:

listToSort.sort(Comparator.comparingInt(listWithOrder::indexOf));

Ответ 3

Скажем, у вас есть список listB, который определяет порядок сортировки listA. Это всего лишь пример, но он демонстрирует порядок, который определяется списком, а не естественный порядок типа данных:

List<String> listB = Arrays.asList("Sunday", "Monday", "Tuesday", "Wednesday",
    "Thursday", "Friday", "Saturday");

Теперь скажем, что listA нужно сортировать в соответствии с этим упорядочением. Это a List<Item>, а Item имеет метод public String getWeekday().

Создайте Map<String, Integer>, который отображает значения всего в listB на то, что можно легко отсортировать, например индекс, т.е. "Sunday" = > 0,..., "Saturday" = > 6. Это обеспечит быстрый и удобный поиск.

Map<String, Integer> weekdayOrder = new HashMap<String, Integer>();
for (int i = 0; i < listB.size(); i++)
{
    String weekday = listB.get(i);
    weekdayOrder.put(weekday, i);
}

Затем вы можете создать свой собственный Comparator<Item>, который использует Map, чтобы создать заказ:

public class ItemWeekdayComparator implements Comparator<Item>
{
    private Map<String, Integer> sortOrder;

    public ItemWeekdayComparator(Map<String, Integer> sortOrder)
    {
        this.sortOrder = sortOrder;
    }

    @Override
    public int compare(Item i1, Item i2)
    {
        Integer weekdayPos1 = sortOrder.get(i1.getWeekday());
        if (weekdayPos1 == null)
        {
            throw new IllegalArgumentException("Bad weekday encountered: " +
               i1.getWeekday());
        }
        Integer weekdayPos2 = sortOrder.get(i2.getWeekday());
        if (weekdayPos2 == null)
        {
            throw new IllegalArgumentException("Bad weekday encountered: " +
               i2.getWeekday());
        }
        return weekdayPos1.compareTo(weekdayPos2);
    }
}

Затем вы можете отсортировать listA с помощью пользовательского Comparator.

Collections.sort(listA, new ItemWeekdayComparator(weekdayOrder));

Ответ 4

Улучшение скорости на JB Nizet отвечает (из предложения, которое он сделал сам). С помощью этого метода:

  • Сортировка списка 1000 пунктов в 100 раз повышает скорость 10 раз на моем модульные тесты.

  • Сортировка списка 10000 элементов в 100 раз повышает скорость 140 раз (265 мс для всей партии вместо 37 секунд) на моем модульные тесты.

Этот метод также будет работать, если оба списка не идентичны:

/**
 * Sorts list objectsToOrder based on the order of orderedObjects.
 * 
 * Make sure these objects have good equals() and hashCode() methods or
 * that they reference the same objects.
 */
public static void sortList(List<?> objectsToOrder, List<?> orderedObjects) {

    HashMap<Object, Integer> indexMap = new HashMap<>();
    int index = 0;
    for (Object object : orderedObjects) {
        indexMap.put(object, index);
        index++;
    }

    Collections.sort(objectsToOrder, new Comparator<Object>() {

        public int compare(Object left, Object right) {

            Integer leftIndex = indexMap.get(left);
            Integer rightIndex = indexMap.get(right);
            if (leftIndex == null) {
                return -1;
            }
            if (rightIndex == null) {
                return 1;
            }

            return Integer.compare(leftIndex, rightIndex);
        }
    });
}

Ответ 5

Вот решение, которое увеличивает временную сложность на 2n, но выполняет то, что вы хотите. Также не волнует, если List R, который вы хотите отсортировать, содержит элементы Comparable, если другой List L, который вы используете для сортировки, равномерно сопоставим.

public HeavyPair<L extends Comparable<L>, R> implements Comparable<HeavyPair<L, ?>> {
    public final L left;
    public final R right;

    public HeavyPair(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public compareTo(HeavyPair<L, ?> o) {
        return this.left.compareTo(o.left);
    }

    public static <L extends Comparable<L>, R> List<R> sort(List<L> weights, List<R> toSort) {
        assert(weights.size() == toSort.size());
        List<R> output = new ArrayList<>(toSort.size());
        List<HeavyPair<L, R>> workHorse = new ArrayList<>(toSort.size());
        for(int i = 0; i < toSort.size(); i++) {
            workHorse.add(new HeavyPair(weights.get(i), toSort.get(i)))
        }
        Collections.sort(workHorse);
        for(int i = 0; i < workHorse.size(); i++) {
            output.add(workHorse.get(i).right);
        }
        return output;
    }
}

Извините за любые ужасные действия, которые я использовал при написании этого кода. Я был в спешке.

Просто позвоните HeavyPair.sort(listB, listA);

Изменить: Исправлена ​​эта строка return this.left.compareTo(o.left);. Теперь он действительно работает.

Ответ 6

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

    ArrayList<String> listA = new ArrayList<String>();
    ArrayList<String> listB = new ArrayList<String>();
    int j = 0;
    // list of returns of the compare method which will be used to manipulate
    // the another comparator according to the sorting of previous listA
    ArrayList<Integer> sortingMethodReturns = new ArrayList<Integer>();

    public void addItemstoLists() {
        listA.add("Value of Z");
        listA.add("Value of C");
        listA.add("Value of F");
        listA.add("Value of A");
        listA.add("Value of Y");

        listB.add("this is the value of Z");
        listB.add("this is the value off C");
        listB.add("this is the value off F");
        listB.add("this is the value off A");
        listB.add("this is the value off Y");

        Collections.sort(listA, new Comparator<String>() {

            @Override
            public int compare(String lhs, String rhs) {
                // TODO Auto-generated method stub
                int returning = lhs.compareTo(rhs);
                sortingMethodReturns.add(returning);
                return returning;
            }

        });
        // now sort the list B according to the changes made with the order of
        // items in listA
        Collections.sort(listB, new Comparator<String>() {

            @Override
            public int compare(String lhs, String rhs) {
                // TODO Auto-generated method stub

                // comparator method will sort the second list also according to
                // the changes made with list a
                int returning = sortingMethodReturns.get(j);
                j++;
                return returning;
            }

        });

    }

Ответ 7

Один из способов сделать это - перебирать через listB и добавлять элементы во временный список, если listA содержит их:

List<?> tempList = new ArrayList<?>();
for(Object o : listB) {
    if(listA.contains(o)) {
        tempList.add(o);
    }
}
listA.removeAll(listB);
tempList.addAll(listA);
return tempList;

Ответ 8

Не совсем понятно, что вы хотите, но если это так: А: [с, Ь, а] Б: [2,1,0]

И вы хотите загрузить их оба, а затем произвести: С: [а, б, в]

Тогда, может быть, это?

List c = new ArrayList(b.size());
for(int i=0;i<b.size();i++) {
  c.set(b.get(i),a.get(i));
}

который требует дополнительной копии, но я думаю, что к ней на месте гораздо менее эффективно, и всевозможные непонятные:

for(int i=0;i<b.size();i++){
    int from = b.get(i);
    if(from == i) continue;
    T tmp = a.get(i);
    a.set(i,a.get(from));
    a.set(from,tmp);
    b.set(b.lastIndexOf(i),from); 
}

Заметьте, я тоже не тестировал, может быть, знак перевернулся.

Ответ 9

Другим решением, которое может работать в зависимости от вашего параметра, является не сохранение экземпляров в listB, а вместо него индексы из списка. Это можно сделать, обернув listA внутри пользовательского отсортированного списка следующим образом:

public static class SortedDependingList<E> extends AbstractList<E> implements List<E>{
    private final List<E> dependingList;
    private final List<Integer> indices;

    public SortedDependingList(List<E> dependingList) {
        super();
        this.dependingList = dependingList;
        indices = new ArrayList<>();
    }

    @Override
    public boolean add(E e) {
        int index = dependingList.indexOf(e);
        if (index != -1) {
            return addSorted(index);
        }
        return false;
    }

    /**
     * Adds to this list the element of the depending list at the given
     * original index.
     * @param index The index of the element to add.
     * 
     */
    public boolean addByIndex(int index){
        if (index < 0 || index >= this.dependingList.size()) {
            throw new IllegalArgumentException();
        }
        return addSorted(index);
    }

    /**
     * Returns true if this list contains the element at the
     * index of the depending list.
     */
    public boolean containsIndex(int index){
        int i = Collections.binarySearch(indices, index);
        return i >= 0;
    }

    private boolean addSorted(int index){
        int insertIndex = Collections.binarySearch(indices, index);
        if (insertIndex < 0){
            insertIndex = -insertIndex-1;
            this.indices.add(insertIndex, index);
            return true;
        }
        return false;
    }

    @Override
    public E get(int index) {
        return dependingList.get(indices.get(index));
    }

    @Override
    public int size() {
        return indices.size();
    }
}

Затем вы можете использовать этот настраиваемый список следующим образом:

public static void main(String[] args) {
    class SomeClass{
        int index;
        public SomeClass(int index) {
            super();
            this.index = index;
        }
        @Override
        public String toString() {
            return ""+index;
        }
    }

    List<SomeClass> listA = new ArrayList<>();
    for (int i = 0; i < 100; i++) {
        listA.add(new SomeClass(i));
    }
    SortedDependingList<SomeClass> listB = new SortedDependingList<>(listA);
    Random rand = new Random();

    // add elements by index:
    for (int i = 0; i < 5; i++) {
        int index = rand.nextInt(listA.size());
        listB.addByIndex(index);
    }

    System.out.println(listB);

    // add elements by identity:
    for (int i = 0; i < 5; i++) {
        int index = rand.nextInt(listA.size());
        SomeClass o = listA.get(index);
        listB.add(o);
    }
    System.out.println(listB);      
}

Конечно, этот пользовательский список будет действителен только в том случае, если элементы в исходном списке не изменяются. Если изменения возможны, вам нужно как-то прослушать изменения в исходном списке и обновить индексы внутри настраиваемого списка.

Обратите внимание также, что SortedDependingList в настоящее время не позволяет добавить элемент из списка A второй раз - в этом отношении он фактически работает как набор элементов из списка A, потому что это обычно то, что вы хотите в такой настройке.

Предпочтительный способ добавить что-то в SortedDependingList - это уже знать индекс элемента и добавить его, вызвав sortedList.addByIndex(index);

Ответ 10

попробуйте это для java 8:

listB.sort((left, right) -> Integer.compare(list.indexOf(left), list.indexOf(right)));

или

listB.sort(Comparator.comparingInt(item -> list.indexOf(item)));

Ответ 11

Проблема: сортировка списка Pojo на основе одного из полей всех возможных значений, присутствующих в другом списке.

Посмотрите на это решение, может быть, это то, что вы пытаетесь достичь:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Test {

  public static void main(String[] args) {
    List<Employee> listToSort = new ArrayList<>();
    listToSort.add(new Employee("a", "age11"));
    listToSort.add(new Employee("c", "age33"));
    listToSort.add(new Employee("b", "age22"));
    listToSort.add(new Employee("a", "age111"));
    listToSort.add(new Employee("c", "age3"));
    listToSort.add(new Employee("b", "age2"));
    listToSort.add(new Employee("a", "age1"));

    List<String> listWithOrder = new ArrayList<>();
    listWithOrder.add("a");
    listWithOrder.add("b");
    listWithOrder.add("c");

    Collections.sort(listToSort, Comparator.comparing(item -> 
    listWithOrder.indexOf(item.getName())));
    System.out.println(listToSort);
  }

}


class Employee {
  String name;
  String age;

  public Employee(String name, String age) {
    super();
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public String getAge() {
    return age;
  }

  @Override
  public String toString() {
    return "[name=" + name + ", age=" + age + "]";
  }
}

ВЫХОД [[name = a, age = age11], [name = a, age = age111], [name = a, age = age1], [name = b, age = age22], [name = b, age = age2 ], [name = c, age = age33], [name = c, age = age3]]

Ответ 12

Если ссылки на объекты должны быть одинаковыми, вы можете инициализировать listA new.

listA = new ArrayList(listB)

Ответ 13

Как и Тим Херольд, если ссылки на объекты должны быть одинаковыми, вы можете просто скопировать listB в listA, либо:

listA = new ArrayList(listB);

Или это, если вы не хотите изменять список, список которого относится к:

listA.clear();
listA.addAll(listB);

Если ссылки не совпадают, но есть некоторая взаимосвязь эквивалентности между объектами в listA и listB, вы можете отсортировать listA с помощью пользовательского Comparator, который находит объект в listB и использует его индекс в listB как ключ сортировки. Наивное выполнение, которое переборная команда listB не будет лучшей по производительности, но будет функционально достаточной.

Ответ 14

ИМО, вам нужно упорствовать в чем-то еще. Может быть, не полный списокB, а что-то. Может быть просто указателями элементов, которые пользователь изменил.

Ответ 15

Попробуйте это. Код ниже является общим назначением для сценария, где listA является списком Objects, поскольку вы не указали конкретный тип.

Object[] orderedArray = new Object[listA.size()];

for(int index = 0; index < listB.size(); index ++){
    int position = listB.get(index); //this may have to be cast as an int
    orderedArray[position] = listA.get(index);
}
//if you receive UnsupportedOperationException when running listA.clear()
//you should replace the line with listA = new List<Object>() 
//using your actual implementation of the List interface
listA.clear(); 
listA.addAll(orderedArray);

Ответ 16

Просто столкнулся с той же проблемой.
У меня есть список упорядоченных ключей, и мне нужно заказать объекты в списке в соответствии с порядком ключей.
Мои списки достаточно длинны, чтобы сделать решения с временной сложностью N ^ 2 непригодными для использования.
Мое решение:

<K, T> List<T> sortByOrder(List<K> orderedKeys, List<T> objectsToOrder, Function<T, K> keyExtractor) {
    AtomicInteger ind = new AtomicInteger(0);
    Map<K, Integer> keyToIndex = orderedKeys.stream().collect(Collectors.toMap(k -> k, k -> ind.getAndIncrement(), (oldK, newK) -> oldK));
    SortedMap<Integer, T> indexToObj = new TreeMap<>();
    objectsToOrder.forEach(obj -> indexToObj.put(keyToIndex.get(keyExtractor.apply(obj)), obj));
    return new ArrayList<>(indexToObj.values());
}

Сложность времени - O (N * Log (N)).
Решение предполагает, что все объекты в сортировке списка имеют разные ключи. Если нет, то просто замените SortedMap<Integer, T> indexToObj на SortedMap<Integer, List<T>> indexToObjList.

Ответ 17

Чтобы избежать очень неэффективного поиска, вы должны проиндексировать элементы в listB а затем отсортировать listA на основе этого.

Map<Item, Integer> index = IntStream.range(0, listB.size()).boxed()
  .collect(Collectors.toMap(listB::get, x -> x));

listA.sort((e1, e2) -> Integer.compare(index.get(c1), index.get(c2));

Ответ 18

import java.util.Comparator;
import java.util.List;

public class ListComparator implements Comparator<String> {

    private final List<String> orderedList;
    private boolean appendFirst;

    public ListComparator(List<String> orderedList, boolean appendFirst) {
        this.orderedList = orderedList;
        this.appendFirst = appendFirst;
    }

    @Override
    public int compare(String o1, String o2) {
        if (orderedList.contains(o1) && orderedList.contains(o2))
            return orderedList.indexOf(o1) - orderedList.indexOf(o2);
        else if (orderedList.contains(o1))
            return (appendFirst) ? 1 : -1;
        else if (orderedList.contains(o2))
            return (appendFirst) ? -1 : 1;
        return 0;
    }
}

Вы можете использовать этот универсальный компаратор для сортировки списка на основе другого списка. Например, когда appendFirst имеет значение false, ниже будет вывод.

Упорядоченный список: [a, b]

Неупорядоченный список: [d, a, b, c, e]

Вывод: [a, b, d, c, e]

Ответ 19

Если два списка гарантированно содержат одинаковые элементы, просто в разном порядке, вы можете использовать List<T> listA = new ArrayList<>(listB) и это будет O(n) сложность по времени. В противном случае, я вижу много ответов с использованием Collections.sort(), однако есть альтернативный метод, который гарантирует время выполнения O(2n), которое теоретически должно быть быстрее, чем сложность наихудшего времени sort O(nlog(n)), по стоимости 2n хранения

Set<T> validItems = new HashSet<>(listB);
listA.clear();
listB.forEach(item -> {
  if(validItems.contains(item)) {
    listA.add(item);
  }
});

Ответ 20

В Java есть набор классов, которые могут быть полезны для сортировки списков или массивов. В большинстве приведенных ниже примеров будут использоваться списки, но для массивов может применяться одна и та же концепция. Пример покажет это.

Мы можем использовать это, создав список целых чисел и отсортировав их с помощью Collections.sort(). Класс Collections (Java Doc) (часть Java Collection Framework) предоставляет список статических методов, которые мы можем использовать при работе с такими коллекциями, как список, набор и т.п. Итак, вкратце, мы можем отсортировать список, просто позвонив: java.util.Collections.sort(список), как показано в следующем примере:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class example {
  public static void main(String[] args) {
    List<Integer> ints = new ArrayList<Integer>();
    ints.add(4);
    ints.add(3);
    ints.add(7);
    ints.add(5);
    Collections.sort(ints);
    System.out.println(ints);
  }
}

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