简体   繁体   English

Java:在两个列表之间获取不同的条目Map <String, Object>

[英]Java: Get different entries between two lists Map<String, Object>

I have two lists Map. 我有两个列表Map。 I want to get different entries between them base on some fields of Object. 我想基于对象的某些字段在它们之间获取不同的条目。 I've created a method as below: 我创建了如下方法:

private List<Map<String,Object>> GetDifferentEntries(List<Map<String,Object>> setA, List<Map<String,Object>> setB) {
      List<Map<String,Object>> tmp = new ArrayList<Map<String,Object>>(setA);
            for(Map<String,Object> a : tmp){
                for(Map<String,Object> b: setB){
                    if(a.get("ID").equals(b.get("ID")) && a.get("ID1").equals(b.get("ID1")) ){
                        setA.remove(a);
                    }
                }        }return setA;}

Can I write above method again by using Stream class in java 8? 我可以在Java 8中使用Stream类再次编写上述方法吗?

I have an example: 我有一个例子:

 List1={ [ID=1, actor="A", ID1 = 1, film="F"], 
    [ID=2, actor="B", ID1 = 1, film="F"],
    [ID=3, actor="C", ID1 = 1, film="F"]}

List2={ [ID=1, director="A", ID1 = 1, film="F"], 
    [ID=5, director="E", ID1 = 1, film="F"]}

Result = {  [ID=2, actor="B", ID1 = 1, film="F"],
        [ID=3, actor="C", ID1 = 1, film="F"] }

I'm not that familiar with Java 8 streams yet but your method could be optimized (as already stated in my comment) and thus be easier to be refactored to streams. 我还不太熟悉Java 8流,但是可以优化您的方法(正如我的评论中所述),因此可以更容易地将其重构为流。

First you should not use nested loops but rather 2 separate loops. 首先,您不应使用嵌套循环,而应使用2个单独的循环。 Additionally you might want to use a special key object that provides for ID and ID1 as well as appropriate implementations of hashCode() and equals() . 另外,您可能想使用提供IDID1的特殊键对象以及hashCode()equals()适当实现。 Example: 例:

class Key {
  String id;
  String id1;

  int hashCode() { ... }
  boolean equals(Object o) { ... }
}

Then build a LinkedHashMap for the first list (you could use a HashMap but that way you retain the element order): 然后为第一个列表构建一个LinkedHashMap (您可以使用HashMap但是那样可以保留元素顺序):

Map<Key, Map<String, Object>> mapA = new LinkedHashMap<>();
for( for(Map<String,Object> a : setA){      
  mapA.put( new Key( a.get("ID"), a.get("ID1") ), a );
}

Note that this assumes your lists don't contain duplicates, if they do, you need to do it slightly differently (but I'll leave that to you). 请注意,这是假设您的列表中不包含重复项,如果重复,那么您需要做的略有不同(但我会留给您)。

Now that you've got a map for setA you can eliminate the elements that are contained in setB : 现在您已经有了setA的映射,可以消除setB中包含的元素了:

for(Map<String,Object> b: setB){
  //if the key is not present nothing will be removed
  mapA.remove( new Key( b.get("ID"), b.get("ID1") ) );
}

Finally built a list out of mapA : 最终根据mapA建立了一个列表:

List<Map<String,Object>> prunedSetA = new ArrayList<>( mapA.values() );

As you can see, you now have 2 loops and at least the second loop might make use of streams. 如您所见,您现在有2个循环,至少第二个循环可能利用了流。 You could do that for the first loop as well but you might lose the ordering. 您也可以在第一个循环中执行此操作,但是可能会丢失顺序。 That's not a problem if order isn't important or you re-sort the list at the end. 如果订单不重要,或者您在列表末尾重新排序,那么这不是问题。

First of all you should be careful as the method has a side effect on the given parameters. 首先,您应该小心,因为该方法会对给定的参数产生副作用。 You should NOT modify input parameters (here setA). 您不应修改输入参数(此处为setA)。

Your copy is not deep enough. 您的副本不够深。 You created a temporary list. 您创建了一个临时列表。 That is ok. 那没问题。 But you do not copy the entries as this will cause the side effect of modified input parameters as they come by reference and not by value. 但是,请勿复制条目,因为这将导致修改的输入参数(通过引用而不是通过值)产生副作用。 The caller cannot trust the input parameters to be the same after he passes them into your method. 调用者将输入参数传递给您的方法后,无法相信它们是相同的。 If you want to avoid the side effect you have also to copy the Maps. 如果要避免副作用,则还必须复制地图。 Beside that you should only remove elements from the copy. 除此之外,您只应从副本中删除元素。

Next your are only handling the case for the elements that are in SetA. 接下来,仅处理SetA中元素的情况。 Elements in SetB that are not in SetA ar not recognized. SetB中不在SetA中的元素无法识别。 Is this correct? 这个对吗?

private List<Map<String, Object>> getDifferentEntries(List<Map<String, Object>> setA, List<Map<String, Object>> setB) {

    List<Map<String, Object>> result = makeDeepCopyOf(setA);

    setA.stream().forEach((entryA) -> {

        setB.stream().forEach((entryB) -> {

            if (entryA.get("ID").equals(entryB.get("ID")) && entryA.get("ID1").equals(entryB.get("ID1"))) {
                result.remove(entryA);
            }

        });

    });

    return result;

}

To go with parallelism you can use parallel streams. 要使用并行性,可以使用并行流。 It will load your CPUs without caring about thread handling: 它将加载您的CPU,而无需关心线程处理:

setA.stream().parallel().forEach(...

setB.stream().parallel().forEach(...

Then you have to extract the statement that removes an element to a synchronized method as this method will be called asynchronously. 然后,您必须提取将元素删除到同步方法的语句,因为该方法将被异步调用。

private synchronized removeElementFrom(List<Map<String, Object>> list, Map<String, Object> entry) {
    list.remove(entry);
}

OR you consider to wrap the copy of setA into a suitable synchronized datastructure: 或者,您考虑将setA的副本包装到合适的同步数据结构中:

     List<Map<String, Object>> result = Collections.synchronizedList(makeDeepCopyOf(setA));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM