我有两个相同对象的集合, Collection<Foo> oldSetCollection<Foo> newSet 所需的逻辑如下:

  • 如果foo在(*) oldSet但不在newSet ,则调用doRemove(foo)
  • 否则如果foo不在oldSet但在newSet ,则调用doAdd(foo)
  • 否则,如果foo在两个集合中但已修改,请调用doUpdate(oldFoo, newFoo)
  • 否则,如果!foo.activated && foo.startDate >= now ,请调用doStart(foo)
  • 否则如果foo.activated && foo.endDate <= now ,则调用doEnd(foo)

(*)“in”表示唯一标识符匹配,不一定是内容。

当前(遗留)代码进行了许多比较,以找出removeSetaddSetupdateSetstartSetendSet ,然后循环以对每个项目进行操作。

代码非常混乱(部分是因为我已经遗漏了一些意大利面条逻辑)而我正在尝试重构它。 更多背景信息:

  • 据我所知, oldSetnewSet实际上是由ArrayList支持的
  • 每套包含少于100件物品,最多可能是20件
  • 这个代码经常被调用(以百万/天为单位),尽管这些代码很少有所不同

我的问题:

  • 如果我将oldSetnewSet转换为HashMap<Foo> (此处不关注顺序),将ID作为键,是否会使代码更容易阅读并更容易比较? 转换损失了多少时间和内存性能?
  • 迭代这两组并执行适当的操作会更有效和简洁吗?

===============>>#1 票数:34

Apache的commons.collections库有一个CollectionUtils类,它为Collection操作/检查提供了易于使用的方法,例如intersection,difference和union。

org.apache.commons.collections.CollectionUtils API文档在这里

===============>>#2 票数:20

例如,您可以使用Java 8流

set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toSet());

或者从Guava中 设置类:

Set<String> intersection = Sets.intersection(set1, set2);
Set<String> difference = Sets.difference(set1, set2);
Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2);
Set<String> union = Sets.union(set1, set2);

===============>>#3 票数:10

我已经使用Java中的Collections Framework创建了我认为您正在寻找的近似值。 坦率地说,我认为这可能是过度的,因为@Mike Deck指出。 对于这样一小组要比较和处理的项目,我认为数组从程序角度来看是更好的选择,但这里是我的伪编码(因为我很懒)解决方案。 我假设Foo类基于它的唯一id而不是它的内容中的所有数据是可比较的:

Collection<Foo> oldSet = ...;
Collection<Foo> newSet = ...;

private Collection difference(Collection a, Collection b) {
    Collection result = a.clone();
    result.removeAll(b)
    return result;
}

private Collection intersection(Collection a, Collection b) {
    Collection result = a.clone();
    result.retainAll(b)
    return result;
}

public doWork() {
    // if foo is in(*) oldSet but not newSet, call doRemove(foo)
    Collection removed = difference(oldSet, newSet);
    if (!removed.isEmpty()) {
        loop removed {
            Foo foo = removedIter.next();
            doRemove(foo);
        }
    }
    //else if foo is not in oldSet but in newSet, call doAdd(foo)
    Collection added = difference(newSet, oldSet);
    if (!added.isEmpty()) {
        loop added  {
            Foo foo = addedIter.next();
            doAdd(foo);
        }
    }

    // else if foo is in both collections but modified, call doUpdate(oldFoo, newFoo)
    Collection matched = intersection(oldSet, newSet);
    Comparator comp = new Comparator() {
        int compare(Object o1, Object o2) {
            Foo f1, f2;
            if (o1 instanceof Foo) f1 = (Foo)o1;
            if (o2 instanceof Foo) f2 = (Foo)o2;
            return f1.activated == f2.activated ? f1.startdate.compareTo(f2.startdate) == 0 ? ... : f1.startdate.compareTo(f2.startdate) : f1.activated ? 1 : 0;
        }

        boolean equals(Object o) {
             // equal to this Comparator..not used
        }
    }
    loop matched {
        Foo foo = matchedIter.next();
        Foo oldFoo = oldSet.get(foo);
        Foo newFoo = newSet.get(foo);
        if (comp.compareTo(oldFoo, newFoo ) != 0) {
            doUpdate(oldFoo, newFoo);
        } else {
            //else if !foo.activated && foo.startDate >= now, call doStart(foo)
            if (!foo.activated && foo.startDate >= now) doStart(foo);

            // else if foo.activated && foo.endDate <= now, call doEnd(foo)
            if (foo.activated && foo.endDate <= now) doEnd(foo);
        }
    }
}

至于你的问题:如果我将oldSet和newSet转换为HashMap(此处不关注顺序),将ID作为键,是否会使代码更容易阅读并更容易比较? 转换损失了多少时间和内存性能? 我认为你可能会通过使用Map BUT使代码更具可读性...你可能会在转换过程中使用更多的内存和时间。

迭代这两组并执行适当的操作会更有效和简洁吗? 是的,这将是两全其美的,特别是如果您遵循@Mike Sharek的建议,使用专门的方法滚动您自己的列表,或者按照访客设计模式来运行您的收集和处理每个项目。

===============>>#4 票数:2

我认为最简单的方法是使用apache collections api - CollectionUtils.subtract(list1,list2),只要列表属于同一类型。

===============>>#5 票数:2

我会移动到列表并以这种方式解决它:

  1. 如果列表中的对象不是Comparable,则使用自定义Comparator按ID升序对两个列表进行排序
  2. 迭代两个列表中的元素,如合并排序算法中的合并阶段,但不是合并列表,而是检查逻辑。

代码或多或少会像这样:

/* Main method */
private void execute(Collection<Foo> oldSet, Collection<Foo> newSet) {
  List<Foo> oldList = asSortedList(oldSet);
  List<Foo> newList = asSortedList(newSet);

  int oldIndex = 0;
  int newIndex = 0;
  // Iterate over both collections but not always in the same pace
  while( oldIndex < oldList.size() 
      && newIndex < newIndex.size())  {
    Foo oldObject = oldList.get(oldIndex);
    Foo newObject = newList.get(newIndex);

    // Your logic here
    if(oldObject.getId() < newObject.getId()) {
      doRemove(oldObject);
      oldIndex++;
    } else if( oldObject.getId() > newObject.getId() ) {
      doAdd(newObject);
      newIndex++;
    } else if( oldObject.getId() == newObject.getId() 
            && isModified(oldObject, newObject) ) {
      doUpdate(oldObject, newObject);
      oldIndex++;
      newIndex++;
    } else {
      ... 
    }
  }// while

  // Check if there are any objects left in *oldList* or *newList*

  for(; oldIndex < oldList.size(); oldIndex++ ) {
    doRemove( oldList.get(oldIndex) );  
  }// for( oldIndex )

  for(; newIndex < newList.size(); newIndex++ ) {
    doAdd( newList.get(newIndex) );
  }// for( newIndex ) 
}// execute( oldSet, newSet )

/** Create sorted list from collection 
    If you actually perform any actions on input collections than you should 
    always return new instance of list to keep algorithm simple.
*/
private List<Foo> asSortedList(Collection<Foo> data) {
  List<Foo> resultList;
  if(data instanceof List) {
     resultList = (List<Foo>)data;
  } else {
     resultList = new ArrayList<Foo>(data);
  }
  Collections.sort(resultList)
  return resultList;
}

===============>>#6 票数:0

public static boolean doCollectionsContainSameElements(
        Collection<Integer> c1, Collection<Integer> c2){

    if (c1 == null || c2 == null) {
        return false;
    }
    else if (c1.size() != c2.size()) {
        return false;
    } else {    
        return c1.containsAll(c2) && c2.containsAll(c1);
    }       
}

===============>>#7 票数:-1

对于一个通常不值得将数组转换为HashMap / set的集合。 事实上,你可能最好将它们保存在一个数组中,然后按键对它们进行排序,并同时迭代这两个列表进行比较。

===============>>#8 票数:-2

对于comaparing列表或集合,我们可以使用Arrays.equals(object[], object[]) 它只会检查值。 要获取Object[]我们可以使用Collection.toArray()方法。

  ask by ckpwong translate from so

未解决问题?本站智能推荐:

5回复

比较Java中的两个集合

我在Java类中有两个集合。第一个集合包含以前的数据,第二个集合包含前一个集合中的更新数据。 我想比较两个集合,但我不确定有效实现它的最佳方法。两个集合将包含相同数量的项目。 然后根据我想要执行carType方法的每个集合中的carType相同。 任何帮助表示赞赏
1回复

Java:比较两个集合的新对象或更新对象

我有两个集合或数组列表,它们的大小可能不相等,需要进行比较。 一个集合是从REST负载中新下载的对象集合。 另一个是来自对象集合本地存储库的集合。 两个集合都不会有重复的对象。 这个想法是要找到本地存储库中不存在的新对象,或者从本地存储库中的现有对象中找到已下载集合中已更新的对象。
4回复

使用Comparator而不是equals()比较两个Java集合

问题陈述 我有两个与我想要比较的相同类型对象的集合。 在这种情况下,我想基于不考虑对象的equals()的属性来比较它们。 在我的例子中,我使用名称的排名集合,例如: 我想比较两个集合来断言,对于每个集合中的位置i ,该位置上每个Name的weightedRank是相同的值。
1回复

Java中的最佳集合,供用户定义的对象使用两个比较器进行搜索

我想要一个Java集合,它可以使用两个比较器(一个比较对象的Int成员,另一个比较对象的String成员)来更快地进行搜索。 是否可以进行任何收藏? 其次,我将数据从db2加载到对象集合(包含2个Int,2个字符串和2个对象引用)。 从数据库加载的行数最多可以达到10000。那么,
7回复

一个集合,表示Java中两个集合的串联

是否有一个类表示集合与另一个集合的串联? 这个类本身应该是一个Collection,并且应该将所有方法委托给底层(内部)集合 - 不应该分配额外的内存,也不应该修改任何原始集合。 用法示例:
1回复

Java-拦截两个集合并获取其余元素

我在Java中有两个String集合: 我假装得到一个新集合,其中包含所有在A中但不在B中的元素,在数学上类似:C = A \\ B 在这种情况下,C类似于: 如何在Java中使用集合高效地完成此任务? 谢谢。
13回复

如何根据不同Java类的字段比较两个“等价”集合?

给定任何两个类,例如下面的ClassA和ClassB : 以及任何两种不同的Collection类型,一种具有ClassA元素,另一种具有ClassB元素,例如: 根据指定的字段子集,判断两个Collection是否“等价”(*)的最简单方法是什么? (*)使用“等同”一词
4回复

如何比较Java中的两个哈希映射

嗨,我正在使用Java中的HashMap,我有一个场景,我必须比较2个HashMaps 在比较这两个哈希映射之后,我得到的hashmap将包含Key作为First HashMap1的值,Value作为第二个HashMap2的值。
1回复

合并两个Arraylist并保留Java中的所有值

我有2 arrayList的PresentErrorList & PastErrorList两者都具有3个字段errorCode , presentErrorCount & pastErrorCount 。 presentErrorList pastErrorList 在为
3回复

合并两个HashTable并删除Java中的重复项

我有两个带有<int,string>对的Hashtables。 现在它们每个都有重复的值,我想合并两个哈希表以给我不同的值。 我怎样才能做到这一点!? 谢谢编辑#1我正在从目录中读取文件内容。 并将这些内容作为令牌存储在两个不同的哈希表中。 现在,我需要将它们合并到单个