3

Let's say I have an arrayList1 of Points. The data structure is like this :

(1,2)->(2,2)->(3,2)->(4,2)->(5,2)

I have another arrayList2 of Points :

(2,2)->(1,2)->(8,5)->(9,3)

How do I compare the two lists and add non-existing values from arrayList2 to arrayList1?

current solution

The only method I can think of now is using a for loop to compare each of the Points in arrayList1 such as, if(!arrayList1.contains(arrayList2.get(i))){ arrayList1.add(arrayList2.get(i)); } i++;.

Is there a more efficient way or already prepared method from a class? Because I have arrayList1 until arrayList6 to compare and replace....

Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
John Evans Solachuk
  • 1,953
  • 5
  • 31
  • 67

7 Answers7

4
  1. For one-liner lovers (running demo):

    List<Point> list3 = new ArrayList<Point>(new HashSet<Point>(list1){{ addAll(list2); }});
    
  2. Safe version * (running demo):

    Set<String> tmpSet = new HashSet<String>(arrayList1);
    tmpSet.addAll(arrayList2);
    List<String> mergedList = new ArrayList<String>(tmpSet);
    

    * As correctly pointed out by Bruce Wayne, Double Brace initialization (the one-liner example, also used in both examples to populate the first two lists) should be used with care, due to the potential drawbacks described in the following article:

    Don’t be “Clever”: The Double Curly Braces Anti Pattern

Explanation: Sets can't contain duplicates, so use one as transition vector.

Example 1 code:

List<String> arrayList1 = new ArrayList<String>(){{ add("One"); add("Two");   }};
List<String> arrayList2 = new ArrayList<String>(){{ add("Two"); add("Three"); }};   
List<String> mergedList = new ArrayList<String>(new HashSet<String>(arrayList1){{ addAll(arrayList2); }});
System.out.println(mergedList);

Output: [One, Two, Three]

Example 2 code:

List<String> arrayList1 = new ArrayList<String>(){{ add("One"); add("Two");   }}; 
List<String> arrayList2 = new ArrayList<String>(){{ add("Two"); add("Three"); }}; 
Set<String> tmpSet = new HashSet<String>(arrayList1);
tmpSet.addAll(arrayList2);
List<String> mergedList = new ArrayList<String>(tmpSet);
System.out.println(mergedList);

Output: [One, Two, Three]

Community
  • 1
  • 1
Andrea Ligios
  • 49,480
  • 26
  • 114
  • 243
  • This is cool! Let's say if I have arrayList3, can I do it in one line too? Like `List mergedList = new ArrayList(new HashSet(arrayList1){{ addAll(arrayList2); addAll(arrayList3); }});` – John Evans Solachuk May 27 '15 at 12:51
  • 1
    Sorry, I was excited until I forgot to try lol. Thanks! This saved lots of my lines in code! – John Evans Solachuk May 27 '15 at 12:59
  • @Mark, you're excitement is justified but understand that you're creating **too many** anonymous inner classes in return for *syntactic sugar*. Read [this](http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/) blog post before you decide that double brace initialization is really what you want. Also, readability **almost always trumps** saving lines of code and I'm not sure if using this pattern is the best thing for you. – Srini May 27 '15 at 13:10
  • Agreed @Mark, with your updated answer, the OP is certainly more equipped to make an informed decision. Thanks for taking the time to incorporate my suggestion. – Srini May 27 '15 at 13:57
2

If time complexity is your main priority, add all the points in List1 to a HashSet<Point>.

Then, for each list thereafter, loop through it and see if the set contains each point and if not, add it to List1.

Set<Point> pointsInList1 = new HashSet<>(list1);
for(Point p : list2)
{
    if(!pointsInList1.contains(p)) {
        list1.add(p);
        pointsInList1.add(p);
    }
}

//Repeat for other lists

This solution is linear with respect to the size of the largest list.

Srini
  • 1,626
  • 2
  • 15
  • 25
  • Isn't this the same or a little bit less complex than my current solution? Just that u are using `HashSet` and a `foreach` loop? You're basically going through each lists, compare each node and add it in if it doesn't exist, right? – John Evans Solachuk May 27 '15 at 12:35
  • @Mark, The time complexity of your solution (from what I can infer from the snippet) is quadratic or *O(m x n)* where *m* and *n* are the size of the two lists. By using a `HashSet`, you are trading time complexity for space and the check to see if a `Point` exists in `list1` is now an O(1) operation. Therefore the overall time complexity is **linear**. – Srini May 27 '15 at 12:39
1

You should use a Set. It is a collection with no duplicates. So you can add the same value twice, it will be present only one time.

It means you can add many List in your Set, you will not have duplicates in it.

    Set setA = new HashSet(); 

    ArrayList<Point> points1 = new ArrayList<Point>();
    ArrayList<Point> points2 = new ArrayList<Point>();

    Point element1 = new Point(0,0);
    Point element2 = new Point(0,1);
    Point element3 = new Point(0,0);
    Point element4 = new Point(0,2);

    points1.add(element1); 
    points1.add(element2); 
    points1.add(element3);

    points2.add(element1);
    points2.add(element4);

    setA.addAll(points1);
    setA.addAll(points2);

    Iterator<Point> it = setA.iterator();
    while(it.hasNext())
        System.out.println(it.next());

Output :

java.awt.Point[x=0,y=0]
java.awt.Point[x=0,y=1]
java.awt.Point[x=0,y=2]
romfret
  • 391
  • 2
  • 11
1

It can have multiple solutions. As you are using java.awt.Point class which already has equals method overridden(based on the coordinates). So, you can easily use contains method of List class.

for(Point point : list2){
     if(!list1.contains(point)){
         list1.add(point);
     }
}

Make sure to use for each loop for a better performance (Do not use index based loop (It makes a difference if you are using LinkedList)).

ii) Another alternative is to use java.util.Set and use its method addAll(Set). As Set does not all duplicates and hence will merge the elements efficiently.

Ouney
  • 1,164
  • 1
  • 10
  • 22
  • How does a for each loop make for a better, significant performance? Basically, the algorithm is the same with my current solution, right? Compare each node in list2 with list1 and if it doesn't exist in list1, add it to list1, right? – John Evans Solachuk May 27 '15 at 12:39
  • It does not make any difference with arrayList because it is indexed. So, get(i) takes O(1). But in a linkedList get(i) is O(i) operation because it will have to traverse. So, if you use a index based loop, everytime you call get(i) it will start from 0 and scan but in case of for each - it employs an iterator which holds a reference to current element and does not start from 0. Yes you are doing the same thing but this code was not there when i replied. You can use Set otherwise. – Ouney May 27 '15 at 12:43
0

You can do something like this

    list2.removeAll(list1);
    list1.addAll(list2);
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
0

You have to override your equal function in your Point Class

and then you could iterate over these two list, and compare their values.

Lrrr
  • 4,755
  • 5
  • 41
  • 63
0

How do I compare the two lists

That one is easy, just use equals.

add non-existing values from arrayList2 to arrayList1

  • remove all elements of arrayList1 from arrayList2 and add it to the arrayList2. That way only the new elements will be added to arrayList2
  • get the difference (arrayList1 - arrayList2) and add these to arrayList2 (for instance with CollectionUtils)

Your current solution is probably wrong (it will either skip one element or run forever, depending on your loop):

if(arrayList1.contains(arrayList2.get(i))) {
  i++; // this shouldn't be there if done in the loop
} else {
  arrayList1.add(arrayList2.get(i)); // here a ++ is needed if not in the loop
}

Is there a more efficient way

A little advice:
First, make it work (and have a good UnitTest coverage). Then (and only then!) optimize if needed!

Burkhard
  • 14,596
  • 22
  • 87
  • 108