简体   繁体   中英

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

I have two lists 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?

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.

First you should not use nested loops but rather 2 separate loops. 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() . 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):

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 :

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 :

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. 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).

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. Elements in SetB that are not in SetA ar not recognized. 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:

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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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