Java比较两个列表

117
我有两个列表(不是Java列表,可以说是两列)。
例如:
**List 1**            **Lists 2**
  milan                 hafil
  dingo                 iga
  iga                   dingo
  elpha                 binga
  hafil                 mike
  meat                  dingo
  milan
  elpha
  meat
  iga                   
  neeta.peeta    
我希望有一个方法能够返回相同元素的数量。在这个例子中,应该是3,并且它应该返回两个列表中相似值和不同值。如果我需要使用哈希表,那么我应该使用什么方法来得到我的结果呢?请帮帮我。P.S:这不是一项学校任务 :) 所以如果您只是指导我,那就足够了。

1
请建议任何数据结构,该列表不是Java列表、哈希映射或任何数据结构。 - user238384
2
一定要考虑在异常情况下应该做什么。列表是否可以包含相同的值两次?如果是这样,如果“dingo”在两个列表中都出现了两次,那么这算作两个共同元素还是只算一个? - JavadocMD
你能修改其中一个列表吗? - Anthony Forloney
如何编辑?是的,每个列表可以多次包含相似的值。 - user238384
问题下方的标签后面应该有一个“编辑”小链接。 - OscarRyz
<嗅嗅>闻起来像是一份家庭作业任务。<嗅嗅> - IAbstract
11个回答

180

编辑

这里有两个版本。一个使用ArrayList,另一个使用HashSet

比较它们并从中创建你自己的版本,直到你得到所需的结果。

这应该足以涵盖你问题中的:

P.S:这不是一项学校作业 :) 所以如果你只是指导我就足够了

部分。

继续原始答案:

你可以使用java.util.Collection和/或java.util.ArrayList来实现。

retainAll方法的作用如下:

仅保留此集合中包含在指定集合中的元素

看这个示例:

import java.util.Collection;
import java.util.ArrayList;
import java.util.Arrays;

public class Repeated {
    public static void main( String  [] args ) {
        Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
        Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));

        listOne.retainAll( listTwo );
        System.out.println( listOne );
    }
}

编辑

对于第二部分(相似值),您可以使用removeAll方法:

删除此集合中也包含在指定集合中的所有元素。

这个第二个版本还可以给出相似的值,并处理重复的值(通过丢弃它们)。

这次,Collection可以是Set而不是List(区别是,Set不允许重复的值)

import java.util.Collection;
import java.util.HashSet;
import java.util.Arrays;

class Repeated {
      public static void main( String  [] args ) {

          Collection<String> listOne = Arrays.asList("milan","iga",
                                                    "dingo","iga",
                                                    "elpha","iga",
                                                    "hafil","iga",
                                                    "meat","iga", 
                                                    "neeta.peeta","iga");

          Collection<String> listTwo = Arrays.asList("hafil",
                                                     "iga",
                                                     "binga", 
                                                     "mike", 
                                                     "dingo","dingo","dingo");

          Collection<String> similar = new HashSet<String>( listOne );
          Collection<String> different = new HashSet<String>();
          different.addAll( listOne );
          different.addAll( listTwo );

          similar.retainAll( listTwo );
          different.removeAll( similar );

          System.out.printf("One:%s%nTwo:%s%nSimilar:%s%nDifferent:%s%n", listOne, listTwo, similar, different);
      }
}

输出:

$ java Repeated
One:[milan, iga, dingo, iga, elpha, iga, hafil, iga, meat, iga, neeta.peeta, iga]

Two:[hafil, iga, binga, mike, dingo, dingo, dingo]

Similar:[dingo, iga, hafil]

Different:[mike, binga, milan, meat, elpha, neeta.peeta]

如果它无法完全满足你的需求,它会给你一个很好的开始,让你能够从这里开始处理。
读者提问:如何包含所有重复的值?

@Oscar,我的确有同样的想法,但我不确定我们是否可以修改listOne的内容,无论如何+1! - Anthony Forloney
@poygenelubricants,你所说的“原始类型”是指非泛型吗?为什么不使用泛型呢? - OscarRyz
Oscar,你看到我的更新问题了吗?它支持重复值吗? - user238384
@Oscar: http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.8 在引入泛型后编写的代码中强烈不建议使用原始类型。未来版本的Java编程语言可能禁止使用原始类型。 - polygenelubricants
2
@polygenelubricants的答案已更新以处理重复和原始类型。顺便说一句,Java的*..未来版本...*永远不会发生。 ;) - OscarRyz
显示剩余5条评论

44
您可以尝试使用intersection()subtract()方法,这些方法来自于CollectionUtils库,可以处理相似元素。 intersection()方法返回包含共同元素的集合,而subtract()方法则返回所有不共同的元素。

6
请注意,这个解决方案需要使用 Apache Commons 库。 - Sir Codesalot

15
如果您想方便地测试两个集合是否相等,可以使用org.apache.commons.collections.CollectionUtils.isEqualCollection,它会比较两个集合而不考虑顺序。

12

在所有的方法中,我认为使用org.apache.commons.collections.CollectionUtils#isEqualCollection是最好的方法。以下是原因:

  • 我不需要声明任何额外的列表/集合
  • 我不会改变输入列表
  • 它非常高效。它以O(N)复杂度检查相等性。

如果无法将apache.commons.collections作为依赖项,则建议实现其遵循的算法来检查列表的相等性,因为它具有高效性。


11

这些是真正的列表(有序,可重复),还是集合(无序,不可重复)?

因为如果是后者,那么你可以使用例如 java.util.HashSet<E> 并使用方便的retainAll 以期望线性时间完成操作。

    List<String> list1 = Arrays.asList(
        "milan", "milan", "iga", "dingo", "milan"
    );
    List<String> list2 = Arrays.asList(
        "hafil", "milan", "dingo", "meat"
    );

    // intersection as set
    Set<String> intersect = new HashSet<String>(list1);
    intersect.retainAll(list2);
    System.out.println(intersect.size()); // prints "2"
    System.out.println(intersect); // prints "[milan, dingo]"

    // intersection/union as list
    List<String> intersectList = new ArrayList<String>();
    intersectList.addAll(list1);
    intersectList.addAll(list2);
    intersectList.retainAll(intersect);
    System.out.println(intersectList);
    // prints "[milan, milan, dingo, milan, milan, dingo]"

    // original lists are structurally unmodified
    System.out.println(list1); // prints "[milan, milan, iga, dingo, milan]"
    System.out.println(list2); // prints "[hafil, milan, dingo, meat]"

我真的不知道它应该是哪种数据结构。它有重复项。现在你可以看到更新后的问题。 - user238384
它会从数据集中删除重复的值吗?因为我不想丢失任何值 :( - user238384
@agazerboy:我已经尝试回答了两个问题。如果需要更多的澄清,请随时提问。 - polygenelubricants
谢谢Poly。我试了一下你的程序,加入了重复项,例如在第一个列表中我添加了两次“iga”,但它仍然返回3作为答案。而现在应该是4,因为列表1有4个相似的值。如果我多次添加一个条目,它应该可以工作。你觉得呢?还有其他的数据结构吗? - user238384

7
使用Java 8的removeIf功能。
public int getSimilarItems(){
    List<String> one = Arrays.asList("milan", "dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta");
    List<String> two = new ArrayList<>(Arrays.asList("hafil", "iga", "binga", "mike", "dingo")); //Cannot remove directly from array backed collection
    int initial = two.size();

    two.removeIf(one::contains);
    return initial - two.size();
}

看起来不错,但如果我想保持列表不被修改,我必须克隆其中一个列表,在某些情况下这是不希望的。 - Sebastian D'Agostino

6
简单的解决方案:
    List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "d", "c"));
    List<String> list2 = new ArrayList<String>(Arrays.asList("b", "f", "c"));

    list.retainAll(list2);
    list2.removeAll(list);
    System.out.println("similiar " + list);
    System.out.println("different " + list2);

输出:

similiar [b, c]
different [f]

1
假设 hash1hash2
List< String > sames = whatever
List< String > diffs = whatever

int count = 0;
for( String key : hash1.keySet() )
{
   if( hash2.containsKey( key ) ) 
   {
      sames.add( key );
   }
   else
   {
      diffs.add( key );
   }
}

//sames.size() contains the number of similar elements.

他想要相同键的列表,而不是有多少个键是相同的。我认为。 - Rosdi Kasim
感谢Stefan的帮助。是的,Rosdi和你都是正确的。我需要相似值的总数和相似值本身。 - user238384
使用库,这种方法非常业余,请看这个方法: List<Integer> list = List.of(1,2,3,4,5); List<Integer> target = List.of(1,2,3,40,50); List<Integer> result = list.stream().filter(v -> !target.contains(v)).collect(Collectors.toList());// 打印 [4, 5] - 列表中未在目标中找到的项 System.out.println(result); - AAI INGENIERIA

1
我在List Compare找到了一个非常基本的列表比较示例。该示例首先验证大小,然后检查一个列表中特定元素在另一个列表中的可用性。

0
public static boolean compareList(List ls1, List ls2){
    return ls1.containsAll(ls2) && ls1.size() == ls2.size() ? true :false;
     }

public static void main(String[] args) {

    ArrayList<String> one = new ArrayList<String>();
    one.add("one");
    one.add("two");
    one.add("six");

    ArrayList<String> two = new ArrayList<String>();
    two.add("one");
    two.add("six");
    two.add("two");

    System.out.println("Output1 :: " + compareList(one, two));

    two.add("ten");

    System.out.println("Output2 :: " + compareList(one, two));
  }

1
当两个包含3个“one”时,此解决方案返回错误的结果。它会错误地产生一个true结果。 - Joseph Fitzgerald
感谢这部分代码:&& ls1.size() == ls2.size() - Nouar
2
你认为在你的代码片段中需要 ? true : false 的任何原因吗? - Krzysztof Tomaszewski

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