使用列表高效获取匹配项的索引的方法

8

我有两个列表A和B。我想找出在A中与B的元素匹配的索引。类似于这样:

ArrayList listA = new ArrayList();
listA.add(1);listA.add(2);listA.add(3);listA.add(4);
ArrayList listB = new ArrayList();
listB.add(2);listB.add(4);
ArrayList listC = new ArrayList();
for(int i=0; i<listB.size();i++) {
   int element = listB.get(i);
   for(int j=0; j<listA.size(); j++) {
      if(listA.get(j) == element) listC.add(j);
   }
}

我猜这是一种丑陋的方法。找出所有与B中所有元素匹配的A的索引的最佳方法是什么?我相信在集合API中存在一个叫做containsAll的方法,但不认为它会返回匹配的索引。


2
这两个列表中是否可能包含重复的值? - mre
集合(Set)无法工作的原因是什么?http://download.oracle.com/javase/1.4.2/docs/api/java/util/Set.html - madmik3
我猜它们不能包含重复的值。 - Jay
我需要元素的索引 - 使用Set与使用List会相同吗? - Jay
6个回答

6
如果必须使用ArrayList,您可以从ArrayList创建一个HashSet。这将使得调用contains的时间复杂度为O(1)。创建HastSet需要O(n)的时间复杂度。如果可以直接使用HashSet,那将是最好的选择。
public static void main(String[] args) 
{
    List listA = new ArrayList();
    listA.add(1);
    listA.add(2);
    listA.add(3);
    listA.add(4);
    List listB = new ArrayList();
    listB.add(2);
    listB.add(4);
    Set hashset = new HashSet(listA);

    for(int i = 0; i < listB.size(); i++) 
    {
        if(hashset.contains(listB.get(i))) 
        {
            listC.add(i);
            System.out.println(i);
        }
    }   
}

1
在列表A中循环n个元素,并且对于列表A中的每个元素,在列表B中循环m个元素似乎不如O(n)高效。 - DwB
@Jay - 你需要循环来创建HashSet并在其中一个ArrayList中进行搜索。如果你从HashSet开始而不是ArrayList,你只需要循环一次。 - SwDevMan81
你确定从ArrayList创建HashSet的复杂度是O(n)吗? - Jay
顺便说一句,这里的问题https://dev59.com/jlrUa4cB1Zd3GeqPgA3t 中提到的时间复杂度为O(n^2),而不是O(n)+O(n)。 - Jay
@Jay - 如果你看一下Jon Skeet在他的回答中的评论,他说了和我一样的话。这是O(n)的,因为HashSet中的查找是O(1)的。这个答案和我的一样。把其中一个ArrayList转换成HashSet。 - SwDevMan81
显示剩余8条评论

2
Guava库提供了一个方法。
"SetView com.google.common.collect.Sets.intersection(Set a, Set b)

这将给出两个集合中包含的元素,但不包括索引。虽然之后获取索引应该很容易。


2
为了这么简单的事情,需要使用第三方库吗? - Jay
@Jay,是的,那是Java...如果我换成C#世界,我可以想象写一行非常简单的LINQ... - Peter Perháč

1

简单:

List<Integer> listA = new ArrayList<Integer>();
listA.add(1);
listA.add(2);
listA.add(3);
listA.add(4);

List<Integer> listB = new ArrayList<Integer>();
listB.add(2);
listB.add(4);

List<Integer> listC = new ArrayList<Integer>();

for ( Integer item : listA ) {
    int index = listB.indexOf( item );
    if ( index >= 0 ) {
        listC.add(index);
    }
}

但是这仅适用于没有重复的情况,如果有重复的索引,您必须按照您所做的方式浏览整个列表。

编辑

我以为你想要元素,而不是索引,集合不会给你索引,只会给你元素。


使用contains()方法的时间复杂度是多少?其次,我需要的是这些索引而不是实际元素。 - Jay
"contains" 是一个 O(n) 的操作。 - Maurício Linhares
这是线性复杂度。那么找索引呢? - Jay
将您的答案和问题标题更新为“索引”,而不是匹配项。 - Maurício Linhares

1

假设没有重复的值,为什么不使用 ArrayList.indexOf 呢?


public final class ArrayListDemo {
    public static void main(String[]args){
        findIndices(createListA(), createListB());      
    }

    private static final List<Integer> createListA(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(3);
        list.add(5);

        return list;
    }

    private static final List<Integer> createListB(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(0);
        list.add(2);
        list.add(3);
        list.add(4);

        return list;
    }

    private static void findIndices(List<Integer> listA, List<Integer> listB){
        for(int i = 0; i < listA.size(); i++){  
            // Get index of object in list b
            int index = listB.indexOf(listA.get(i));

            // Check for match
            if(index != -1){
                System.out.println("Found match:");
                System.out.println("List A index = " + i);
                System.out.println("List B index = " + index);
            }
        }
    }
}

输出

Found match:
List A index = 1
List B index = 2

get 操作的时间复杂度为常数时间(即 O(1)),而 indexOf 操作的时间复杂度为线性时间(即 O(n))。 - mre
for循环意味着您将执行与A中元素数量相同的indexOf操作 - 您认为SwDevMan81在下面的答案更有效吗? - Jay

1
如果列表A和列表B按相同顺序排序(我假设是升序,但降序也可以),则此问题具有O(n)的解决方案。以下是一些(非正式且未经测试的)代码。当循环退出时,indexMap应包含与列表B中元素匹配的列表A中每个元素的索引以及匹配元素在列表B中的索引。
  int currentA;
  int currentB;
  int listAIndex = 0;
  int listBIndex = 0;
  Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
currentA = listA.get(listAIndex); currentB = listB.get(listBIndex); while ((listAIndex < listA.length) && (listBIndex < listB.length)) { if (currentA == currentB) { indexMap.put(listAIndex, listBIndex); ++listAIndex; } else if (currentA < currentB) { ++listAIndex; } else // if (currentA > currentB) { ++listBIndex; } }

如果列表没有排序,我猜想 - 排序所涉及的额外复杂性将会增加到总复杂度中,不是吗? - Jay
复杂度成为排序的最低效率,相对于O(n)。通常这意味着排序的复杂度。 - DwB

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接