在Java中,如何根据另一个列表对一个列表进行排序?

时间:2013-08-08 15:17:06

标签: java list sorting arraylist

我已经看到了其他几个与此类似的问题,但我真的找不到能解决问题的任何问题。

我的用例是这样的:用户最初有一个项目列表(listA)。他们重新排序项目并希望保留该顺序(listB),但是,由于限制,我无法在后端保留订单,所以我必须在检索后对listA进行排序。

基本上,我有2个ArrayLists(listA和listB)。一个具有特定顺序的列表应该在(listB)中,另一个具有项目列表(listA)。我想基于listB对listA进行排序。

22 个答案:

答案 0 :(得分:59)

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

这是非常低效的,你应该从listA创建一个Map<Item, Integer>来更快地查找项目的位置。

Guava有一个随时可用的比较器:Ordering.explicit()

答案 1 :(得分:35)

Java 8:

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

或更好:

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

答案 2 :(得分:10)

假设您有一个listB列表,用于定义您要对listA进行排序的顺序。这只是一个示例,但它演示了一个由列表定义的顺序,而不是数据类型的自然顺序:

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

现在,让我们说listA需要根据这个排序进行排序。它是List<Item>Itempublic String getWeekday()方法。

创建Map<String, Integer>,将listB中所有内容的值映射到可以轻松排序的内容,例如索引,即"Sunday" =&gt; 0,...,"Saturday" =&gt; 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);
    }
}

然后,您可以使用自定义listAComparator进行排序。

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

答案 3 :(得分:9)

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

答案 4 :(得分:5)

这是一种解决方案,可以将时间复杂度提高2n,但可以实现您想要的效果。它也不关心你要排序的List R是否包含Comparable元素,只要你用它来排序它们的另一个List L是统一的Comparable。

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);。现在它确实有效。

答案 5 :(得分:2)

以下是如何对列表进行排序,然后根据对第一个数组列表所做的更改在另一个列表中进行更改的示例。这个技巧永远不会失败,并确保列表中的项目之间的映射。两个列表的大小必须相同才能使用此技巧。

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

        });

    }

答案 6 :(得分:1)

这样做的一种方法是循环遍历列表并将项目添加到临时列表中,如果listA包含它们:

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

答案 7 :(得分:1)

尝试使用java 8:

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

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

答案 8 :(得分:1)

根据您的设置可能有效的另一种解决方案不是在listB中存储实例,而是在listA中存储索引。这可以通过将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目前不允许第二次从listA添加元素 - 在这方面它实际上像listA中的一组元素一样工作,因为这通常是您在这样的设置中所需的。

向SortedDependingList添加内容的首选方法是已经知道元素的索引并通过调用sortedList.addByIndex(index)添加它;

答案 9 :(得分:1)

不完全清楚你想要什么,但如果是这种情况: 答:[C,B,A] B:[2,1,0]

你想加载它们然后产生: C:[A,B,C]

那么也许这个?

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

注意我也没有测试,也许有一个标志翻转。

答案 10 :(得分:0)

IMO,你需要坚持别的东西。可能不是完整的listB,但。可能只是用户更改的项目的索引。

答案 11 :(得分:0)

试试这个。下面的代码是一个场景的一般用途,其中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);

答案 12 :(得分:0)

就像Tim Herold所写,如果对象引用应该相同,你可以将listB复制到listA,或者:

listA = new ArrayList(listB);

或者,如果您不想更改listA引用的List:

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

如果引用不相同但listA和listB中的对象之间存在某种等价关系,则可以使用自定义Comparator对listA进行排序,该自定义{{1}}在listB中查找对象并将其在listB中的索引用作排序键。粗暴搜索listB的天真实现不是性能最佳的,但在功能上是足够的。

答案 13 :(得分:0)

如果对象引用应该相同,则可以初始化listA new。

listA = new ArrayList(listB)

答案 14 :(得分:0)

遇到了同样的问题。
我有一个已排序键的列表,我需要根据键的顺序来排序列表中的对象。
我的列表足够长,以至于无法使用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

答案 15 :(得分:0)

为避免查找效率很低,您应该为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));

答案 16 :(得分:0)

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]

答案 17 :(得分:0)

问题:根据另一个列表中该字段的所有可能值之一对Pojo列表进行排序。

看看这个解决方案,也许这就是您要实现的目标:

"dog";"3"
"cat";"2"
"bird";"1"
"e""sc";"4"

O U T P U T [[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]]

答案 18 :(得分:0)

如果保证两个列表包含相同的元素,只是顺序不同,则可以使用List<T> listA = new ArrayList<>(listB),这将是O(n)的时间复杂度。否则,我会在这里使用Collections.sort()看到很多答案,但是有一种替代方法可以保证O(2n)的运行时间,从理论上讲,它应该比sort最糟糕的时间复杂度{ {1}},但需要O(nlog(n))的存储空间

2n

答案 19 :(得分:0)

所以对我来说,要求是将originalListorderedList进行排序。 originalList始终包含orderedList中的所有元素,反之则不然。没有新元素。

fun <T> List<T>.sort(orderedList: List<T>): List<T> {
    return if (size == orderedList.size) {
        orderedList
    } else {
        var keepIndexCount = 0
        mapIndexed { index, item ->
            if (orderedList.contains(item)) {
                orderedList[index - keepIndexCount]
            } else {
                keepIndexCount++
                item
            }
    }
}}

P.S。我的情况是我有一个列表,用户可以通过拖放对其进行排序,但是某些项目可能会被过滤掉,因此我们保留了隐藏项目的位置。

答案 20 :(得分:0)

List<String> listA;
Comparator<B> comparator = Comparator.comparing(e -> listA.indexOf(e.getValue()));
//call your comparator inside your list to be sorted
listB.stream().sorted(comparator)..

答案 21 :(得分:-1)

在Java中,有一组类可用于对列表或数组进行排序。以下大多数示例将使用列表,但可以对阵列应用相同的概念。一个例子将显示这一点。

我们可以通过创建一个Integers列表并使用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);
  }
}

上面的类创建了一个包含四个整数的列表,并使用集合排序方法对此列表进行排序(在一行代码中),而无需担心排序算法。