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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18129807/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 22:36:31  来源:igfitidea点击:

In Java how do you sort one list based on another?

javalistsortingarraylist

提问by Debacle

I've seen several other questions similiar to this one but I haven't really been able to find anything that resolves my problem.

我已经看到了其他几个与此类似的问题,但我还没有真正找到任何可以解决我的问题的问题。

My use case is this: user has a list of items initially (listA). They reorder the items and want to persist that order (listB), however, due to restrictions I'm unable persist the order on the backend so I have to sort listA after I retrieve it.

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

So basically, I have 2 ArrayLists (listA and listB). One with the specific order the lists should be in (listB) and the other has the list of items (listA). I want to sort listA based on listB.

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

回答by Khinsu

If the object references should be the same, you can initialize listA new.

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

listA = new ArrayList(listB)

回答by Khinsu

One way of doing this is looping through listB and adding the items to a temporary list if listA contains them:

这样做的一种方法是循环遍历 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;

回答by Jason C

Like Tim Herold wrote, if the object references should be the same, you can just copy listB to listA, either:

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

listA = new ArrayList(listB);

Or this if you don't want to change the List that listA refers to:

或者,如果您不想更改 listA 所指的列表:

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

If the references are not the same but there is some equivalence relationship between objects in listA and listB, you could sort listA using a custom Comparatorthat finds the object in listB and uses its index in listB as the sort key. The naive implementation that brute force searches listB would not be the best performance-wise, but would be functionally sufficient.

如果引用不相同但 listA 和 listB 中的对象之间存在某种等价关系,则可以使用自定义对 listA 进行排序,该自定义Comparator在 listB 中查找对象并使用其在 listB 中的索引作为排序键。蛮力搜索 listB 的幼稚实现在性能方面不是最好的,但在功能上是足够的。

回答by JB Nizet

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

This is quite inefficient, though, and you should probably create a Map<Item, Integer>from listA to lookup the positions of the items faster.

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

Guava has a ready-to-use comparator for doing that: Ordering.explicit()

Guava 有一个现成的比较器可以做到这一点: Ordering.explicit()

回答by Brad Tofel

Not completely clear what you want, but if this is the situation: A:[c,b,a] B:[2,1,0]

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

And you want to load them both and then produce: C:[a,b,c]

并且您想同时加载它们然后生成:C:[a,b,c]

Then maybe this?

那么也许这个?

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

that requires an extra copy, but I think to to it in place is a lot less efficient, and all kinds of not clear:

这需要一个额外的副本,但我认为到位效率要低得多,而且各种不清楚:

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

Note I didn't test either, maybe got a sign flipped.

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

回答by rgettman

Let's say you have a listBlist that defines the order in which you want to sort listA. This is just an example, but it demonstrates an order that is defined by a list, and not the natural order of the datatype:

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

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

Now, let's say that listAneeds to be sorted according to this ordering. It's a List<Item>, and Itemhas a public String getWeekday()method.

现在,假设listA需要根据此顺序进行排序。它是一个List<Item>, 并且Item有一个public String getWeekday()方法。

Create a Map<String, Integer>that maps the values of everything in listBto something that can be sorted easily, such as the index, i.e. "Sunday"=> 0, ..., "Saturday"=> 6. This will provide a quick and easy lookup.

创建一个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);
}

Then you can create your custom Comparator<Item>that uses the Mapto create an order:

然后,您可以创建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);
    }
}

Then you can sort listAusing your custom Comparator.

然后您可以listA使用自定义Comparator.

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

回答by Balder

Another solution that may work depending on your setting is not storing instances in listB but instead indices from listA. This could be done by wrapping listA inside a custom sorted list like so:

另一种可能根据您的设置起作用的解决方案不是将实例存储在 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();
    }
}

Then you can use this custom list as follows:

然后您可以按如下方式使用此自定义列表:

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

Of course, this custom list will only be valid as long as the elements in the original list do not change. If changes are possible, you would need to somehow listen for changes to the original list and update the indices inside the custom list.

当然,这个自定义列表只有在原始列表中的元素不发生变化时才有效。如果可能发生更改,您需要以某种方式侦听原始列表的更改并更新自定义列表中的索引。

Note also, that the SortedDependingList does currently not allow to add an element from listA a second time - in this respect it actually works like a set of elements from listA because this is usually what you want in such a setting.

另请注意,SortedDependingList 当前不允许第二次添加 listA 中的元素 - 在这方面,它实际上像 listA 中的一组元素一样工作,因为这通常是您在此类设置中想要的。

The preferred way to add something to SortedDependingList is by already knowing the index of an element and adding it by calling sortedList.addByIndex(index);

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

回答by gargAman

In Java there are set of classes which can be useful to sort lists or arrays. Most of the following examples will use lists but the same concept can be applied for arrays. A example will show this.

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

We can use this by creating a list of Integers and sort these using the Collections.sort(). The Collections (Java Doc) class (part of the Java Collection Framework) provides a list of static methods which we can use when working with collections such as list, set and the like. So in a nutshell, we can sort a list by simply calling: java.util.Collections.sort(the list) as shown in the following example:

我们可以通过创建一个整数列表并使用 Collections.sort() 对它们进行排序来使用它。Collections (Java Doc) 类(Java 集合框架的一部分)提供了一个静态方法列表,我们可以在处理列表、集合等集合时使用这些方法。因此,简而言之,我们可以通过简单地调用 java.util.Collections.sort(the list) 来对列表进行排序,如下例所示:

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

The above class creates a list of four integers and, using the collection sort method, sorts this list (in one line of code) without us having to worry about the sorting algorithm.

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

回答by ArturoTena

IMO, you need to persist something else. May be not the full listB, but something. May be just the indexes of the items that the user changed.

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

回答by M7Hymans

Try this. The code below is general purpose for a scenario where listA is a list of Objectssince you did not indicate a particular type.

尝试这个。下面的代码适用于 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);