订购另一个集合

时间:2014-09-11 17:27:46

标签: java collections

我有两个ArrayList个集合,我们称之为ABAB的子集,A的元素少于BA有大约10%的元素B)。但是,A中的元素的排序方式与它们在B中的排序方式不同,我需要它们。

我可以想出一个算法,它会像在原始集合中那样对它们进行排序。我要问的是,如果有一些很好的方法,可能是JDK的一部分或Apache Commons的一部分。

2 个答案:

答案 0 :(得分:2)

您需要一个比较器,根据较大列表中的索引比较较小列表中的对象。

实现这一点肯定有几种选择。

非常简单,但非常效率低的一种方法是创建一个比较器,用largerList.indexOf(element)检查元素的索引。但除了最小的列表之外,所有这些都会有可怕的运行时间。

更复杂的解决方案是将映射从元素存储到较大列表中的索引,以便可以直接查找索引。这将显着显着更快,代价是Map的一些额外存储空间。

这两种方法都是在这里实现的:

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

public class SameOrderTest
{
    public static void main(String[] args)
    {
        List<String> listA = new ArrayList<String>();
        listA.add("S");
        listA.add("A");
        listA.add("M");
        listA.add("P");
        listA.add("L");
        listA.add("E");
        listA.add("W");
        listA.add("O");
        listA.add("R");
        listA.add("D");

        List<String> listB = new ArrayList<String>();
        listB.add("A");
        listB.add("M");
        listB.add("S");
        listB.add("E");
        listB.add("L");

        List<String> listB0 = new ArrayList<String>(listB);
        Collections.sort(listB0, simpleSameOrderComparator(listA));

        List<String> listB1 = new ArrayList<String>(listB);
        Collections.sort(listB1, efficientSameOrderComparator(listA));

        System.out.println("A : "+listA);
        System.out.println("B0: "+listB0);
        System.out.println("B1: "+listB1);
    }

    // WARNING: Simple but VERY inefficient
    private static <T> Comparator<T> simpleSameOrderComparator(
        final List<T> reference)
    {
        return new Comparator<T>()
        {
            @Override
            public int compare(T t0, T t1)
            {
                return reference.indexOf(t0)-reference.indexOf(t1);
            }
        };
    }

    private static <T> Comparator<T> efficientSameOrderComparator(
        final List<T> reference)
    {
        final Map<T, Integer> map = new LinkedHashMap<T, Integer>();
        for (int i=0; i<reference.size(); i++)
        {
            map.put(reference.get(i), i);
        }
        return new Comparator<T>()
        {
            @Override
            public int compare(T t0, T t1)
            {
                return map.get(t0)-map.get(t1);
            }
        };
    }

}

我很好奇是否有一个解决方案不会影响排序算法的运行时间,并且需要额外的存储空间。如果较小列表的大小仅为10%,则可能会反转查找并仅存储实际包含在较小列表中的元素的索引,但在这种情况下,运行时间将取决于列表和大小的比例,事先很难说这是否会有所回报。


编辑:受answer of Eugene Kuleshov(+1)的启发,我创建了一个使用他的方法的测试,但是使用了一个集合而不是列表:它的运行时间应为O(m + n) mn是列表的大小。

(EDIT2:根据评论更新)

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public class SameOrderTest2
{
    public static void main(String[] args)
    {
        List<String> listA = new ArrayList<String>();
        listA.add("S");
        listA.add("A");
        listA.add("M");
        listA.add("P");
        listA.add("L");
        listA.add("E");
        listA.add("W");
        listA.add("O");
        listA.add("R");
        listA.add("D");

        List<String> listB = new ArrayList<String>();
        listB.add("A");
        listB.add("M");
        listB.add("S");
        listB.add("E");
        listB.add("L");

        List<String> listB0 = sorted(listA, listB);
        System.out.println("A : "+listA);
        System.out.println("B0: "+listB0);
    }

    private static <T> List<T> sorted(List<T> reference, List<T> toSort)
    {
        List<T> referenceList = new ArrayList<T>(reference);
        referenceList.retainAll(new LinkedHashSet<T>(toSort));
        return referenceList;
    }


}

这还需要额外的存储,但是非常简洁和优雅,如果您可以为第二个列表创建新实例,而不必对其进行就地排序。< / p>

答案 1 :(得分:2)

您可以复制较大的集合,然后调用retainAll()方法删除缺少的元素,保留顺序:

ArrayList result = new ArrayList(B).retainAll(A);

有效地,它会在您的收藏集A上调用contains(),因此对于大型收藏集,Set的效果会优于ArrayList