【问题标题】:Java: Comparing two lists, if either contains the value of the other, add all values of both to a new listJava:比较两个列表,如果其中一个包含另一个的值,则将两者的所有值添加到一个新列表中
【发布时间】:2017-11-14 04:18:45
【问题描述】:

我有一个带有字符串键和 MyObject 值的 ArrayList 的 HashMap,现在我想创建一个包含所有重叠 MyObjects 的列表的新列表。

例如:如果 ArrayList1 包含 MyObjectA、MyObjectB、MyObjectC,而 ArrayList2 包含 MyObjectA、MyObjectD、MyObjectE,那么我想将 MyObjectA - E 添加到新列表中,并将所有这些列表放入主列表中。如果任何值重叠,我想基本上将所有每个 ArrayList 的值组合到一个新列表中。

到目前为止,我只是遍历地图,遍历每个列表,然后再次嵌套迭代,如果任何值匹配,则对嵌套中的两个 ArrayLists 进行另一次迭代以将它们添加到不同的列表中,但这导致新列表中出现重复。

抱歉,如果这不是很清楚。

有没有人有任何建议或者更好的方法来完成这个?

谢谢!

这是我的代码:

public class DetermineOverlaps {

HashMap<String, ArrayList<CustomObject>> pgsPerStMap;

HashSet<HashSet<String>> competingPgs;

public DetermineOverlaps (HashMap<String, ArrayList<CustomObject>> pgsPerStMap2){

    pgsPerStMap = new HashMap<String, ArrayList<CustomObject>>(pgsPerStMap2);

    competingPgs = calculateCompetingPgs();

}

public HashSet<HashSet<String>> calculateCompetingPgs (){

    //This will be the hashset which gets returned from this method
    HashSet<HashSet<String>> competingProdGros = new HashSet<HashSet<String>>();

    //I will iterate over each term (key) within the pgsPerStMap map, which contains search terms | all customObject for that search term
    for (String searchTerm : pgsPerStMap.keySet()){

        //I will iterate over each customObject for the search term
        ArrayList<CustomObject> searchTermsPgs = pgsPerStMap.get(searchTerm);

        for (CustomObject curProdGroup : searchTermsPgs){

            String curProdGroupName = curProdGroup.key;

            //I will store all found matches in this hashset, which I will later put in the competingProdGros map
            HashSet<String> tempPgSet = new HashSet<String>();

            //Compare every other key/value combination of the map to every other key/value combination of the map
            for (String searchTerm2ndLevel : pgsPerStMap.keySet()){
                //Iterate over the customObject
                ArrayList<CustomObject> searchTermsPgsLev2 = pgsPerStMap.get(searchTerm2ndLevel);

                for (CustomObject curProdGroup2ndLevel : searchTermsPgsLev2){

                     String curProdGroupLevel2Name = curProdGroup2ndLevel.key;
                    //If these are different keys, but the same value, i.e. you've got a value which has multiple
                    //overlapping keys, and the temporary hashset doesn't contain the value already
                    //Then add all values from both arraylist<CustomObject> into the temporary hashset...
                    if (!searchTerm2ndLevel.equals(searchTerm) && curProdGroupLevel2Name.equals(curProdGroupName)
                    && !tempPgSet.contains(curProdGroupLevel2Name)){

                        for (CustomObject levelOnePg : searchTermsPgs){

                            String levelOnePgKey = levelOnePg.key;

                            tempPgSet.add(levelOnePgKey);

                        }

                        for (CustomObject levelTwoPg : searchTermsPgsLev2){

                            String levelTwoPgKey = levelTwoPg.key;
                            tempPgSet.add(levelTwoPgKey);

                        }

                    }

                }

            }

            //Add the temporary hashset into the competingProdGros hashset
            if(!competingProdGros.contains(tempPgSet)){competingProdGros.add(tempPgSet);}

        }

    }

    //return the competingprodgros hashset
    return competingProdGros;

}

【问题讨论】:

  • 我认为CODE REVIEW 会更适合这个问题。
  • 我推荐了解List接口的addAll(联合)和retainAll(交集)方法。

标签: java list arraylist hashmap hashset


【解决方案1】:

我无法确定你正在尝试做的事情,但我可以给你一些想法来帮助你实现它。

你说你想要一个对象 A-E 的列表,所以我会说你不允许重复。在这种情况下,您可以使用 String 到 Sets 的映射来减少主列表的大小

我会给你4个场景:

场景 1:不允许重复,列表是对具有唯一内容的对象的引用

Map<String, Set<MyObject> map = new HashMap<String, Set<MyObject>>();
Set<MyObject> set1 = new LinkedHashSet<MyObject>();
Set<MyObject> set2 = new LinkedHashSet<MyObject>();
MyObject a = new MyObject("A");
MyObject b = new MyObject("B");
MyObject c = new MyObject("C");
MyObject d = new MyObject("D");
MyObject e = new MyObject("E");
set1.add(a);
set1.add(b);
set1.add(c);
set2.add(c);
set2.add(d);
set2.add(e);
set2.removeAll(set1)
set1.addAll(set2);
map.put("Mykey", set1);

这表示集合中的对象是相同的,因此将在两个集合之间被删除。 set1的内容是:

[ "A", "B", "C", "D", "E" ]

场景 2:您确实允许重复,并且列表是对相同对象的引用。在这里,我猜你会想要一对一的删除,所以你循环遍历一组交叉点直到为空,然后将列表添加到组合列表中。如果您需要维护列表的值,您可以随时实例化一个新的并将列表的内容传递给它

最后几点注意事项:在增强的 for 循环中删除树集的元素可能会引发 ConcurrentModificationException

MyObject a = new MyObject("A");
MyObject b = new MyObject("B");
MyObject c = new MyObject("C");
MyObject d = new MyObject("D");
MyObject e = new MyObject("E");
List<CodeTester> l = new ArrayList<CodeTester>();
List<CodeTester> l2 = new ArrayList<CodeTester>();
l.add(a);
l.add(c);
l.add(c);
l.add(b);
l.add(b);
l.add(d);
l2.add(c);
l2.add(d);
l2.add(d);
l2.add(e);
Set<CodeTester> set = new HashSet<CodeTester>(l);
set.retainAll(l2); // get the intersections
while(!set.isEmpty()) {
    l2.remove(0);
    set.retainAll(l2);
}
map.put("myKey", set);

这些内容的输出是:

[ "A", "C", "C", "B", "B", "D", "D", "E" ]

请注意,“C”和“D”的 1 个实例被排除在最终列表之外,因为另一个列表有它们

场景 3:不允许重复,但不保证列表元素是对具有唯一内容的对象的引用

如果我不得不猜测,我会说这就是你要找的那个。 此处 MyObject 实例化具有不同哈希码/id 的新实例。这使得所有 MyObject 实例无论内容如何都是唯一的,因此如果您使用相同的 String 声明两个 MyObject,则默认 Collections 操作不会删除/保留它们。要比较内容,您需要实现一些覆盖的方法。然后,您将能够以相同的方式使用上述过程。唯一的区别是使用 TreeSet 而不是 HashSet 来调用重写的 equals 方法,然后我们可以实现 Comparable 或实现 Comparator 来使用 TreeSet 并避免它会从其默认 Comparator 抛出的 ClassCastException。

对于 Set 强制唯一性:

public class MyObject implements Comparable<MyObject>
String s;
Map<String, Set<MyObject> map = new HashMap<String, Set<MyObject>>();
Map<String, List<MyObject> map = new HashMap<String, List<MyObject>>();

public MyObject(String s){
    this.s = s;
}

public void myExample1(){
    Set<MyObject> set1 = new TreeSet<MyObject>();
    Set<MyObject> set2 = new TreeSet<MyObject>();
    MyObject a = new MyObject("A");
    MyObject b = new MyObject("B");
    MyObject c = new MyObject("C");
    MyObject d = new MyObject("D");
    MyObject e = new MyObject("E");
    MyObject b2 = new MyObject(new StringBuilder("F").replace(0, 1, "B").toString());
    MyObject d2 = new MyObject(new StringBuilder("G").replace(0, 1, "D").toString());
    set1.add(a);
    set1.add(b);
    set1.add(c);
    set2.add(b2);
    set2.add(d);
    set2.add(d2);
    set2.add(e);
    set2.removeAll(set1)
    set1.addAll(set2);
    mapS.put("Mykey", set1);
}

public void myExample2(){
    List<MyObject> l = new ArrayList<MyObject>();
    List<MyObject> l2 = new ArrayList<MyObject>();
    l.add(a);
    l.add(c);
    l.add(c);
    l.add(b2);
    l.add(d);
    l2.add(b);
    l2.add(d);
    l2.add(d2);
    l2.add(e);
    Set<MyObject> set = new TreeSet<MyObject>(l);
    set.retainAll(l2); // get the intersections
    Set<MyObject> set2 = new HashSet<MyObject>(set);
    while(!set.isEmpty()) {
        l2.remove(0);
        set.retainAll(l2);
    }
    l.addAll(l2);
    mapL.put("myKey", l);
}

@Override
public int compareTo(MyObject o){
    return s.compareTo(o.getS);
}

public String getS(){
    return s;
}

@Override
public boolean equals(Object o){
    return o instanceof MyObject && compareTo((MyObject) o);
}

}

myExample1 输出:

[ "A", "B", "C", "D", "E" ]

具有显示唯一性的代码:

[A 865113938, B 1442407170, C 1028566121] [B 1118140819, D 1975012498, E 1808253012]
[A 865113938, B 1442407170, C 1028566121, D 1975012498, E 1808253012]

myExample1 输出:

[ "A", "B", "C", "D", "E" ]

用哈希码表示唯一性:

[A 865113938, C 1442407170, C 1442407170, B 1028566121, D 1118140819] [B 1975012498, D 1118140819, D 1808253012, E 589431969]
[A 865113938, C 1442407170, C 1442407170, B 1028566121, D 1118140819, E 589431969]

【讨论】:

    【解决方案2】:

    使用 addAll() 函数。 例如:ArrayList1.addAll(ArrayList2) 现在 ArrayList1 将包含所有没有重复的值

    【讨论】:

    • 我觉得答案比这更复杂。 OP 使用的是 ArrayList,所以也许他想允许重复?无论如何,列表调用的 addAll 都不会解决重复或交叉问题,所以这个答案是错误的
    猜你喜欢
    • 2021-12-07
    • 2017-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多