1
\$\begingroup\$

I have 2 list containing some Objects (Fruit class). I am using a 3rd list to add these elements based on following 2 criteria.

  1. I want every object in the 1st list added to 3rd list. But if I have a matching object in the 2nd list (matching based on id and isChecked), I want to add the object from the 2nd list to the 3rd list and ignore the one in 1st list.

  2. If I did the switch mentioned on point one, I want to move that object up to the first element of the 3rd list.

I have it working with following code. But I find it very inefficient. Is there a better way around it?

Bear in mind I have no control over the second list, but the first list is coming from a Rest endpoint and I am currently capturing it as a list. Unsure if I should have opted for a Map. Please advice.

Example:

In the following example, expected list output is [f2, f5, f1, f3, f4] (based on name).

It is cos I have all the elements from the first list. f2 and f5 went in front of the order as they came from second list (they matched elements in first list and had isChecked set to true).

import lombok.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class App {
    public static void main(String[] args) {

        Fruit fruit1 = new Fruit("1", "f1", false);
        Fruit fruit2 = new Fruit("2", "f2", false);
        Fruit fruit3 = new Fruit("3", "f3", false);
        Fruit fruit4 = new Fruit("4", "f4", false);
        Fruit fruit5 = new Fruit("5", "f5", false);

        List<Fruit> firstList = Arrays.asList(fruit1, fruit2, fruit3, fruit4, fruit5);

        Fruit fruit6 = new Fruit("2", "f2", true);
        Fruit fruit7 = new Fruit("7", "f7", false);
        Fruit fruit8 = new Fruit("5", "f5", true);
        Fruit fruit9 = new Fruit("9", "f9", false);
        Fruit fruit10 = new Fruit("10", "f10", false);

        List<Fruit> secondList = Arrays.asList(fruit6, fruit7, fruit8, fruit9, fruit10);

        List<Fruit> finalList = new ArrayList<>();

        // expected list = [f2, f5, f1, f3, f4]

        // this loop is checking and adding objects to finalList.
        // must match the first list and isChecked.
        // in this case, only f6 and f8 matches the first list (id match) and is also 'checked'.
        for (Fruit first : firstList){
            for (Fruit second : secondList){
                if(first.getId().equals(second.getId()) && second.isChecked()){
                    finalList.add(second);
                    break;
                }
            }
        }

        // not done yet. Still need to loop and add back the elements from the first list
        // which were not added in the above loop
        boolean addedFirst = false;
        outer:
        for(Fruit first : firstList){
            for(Fruit finalFruit : finalList){
                if(first.getId().equals(finalFruit.getId())){
                   continue outer;
                }
            }
            finalList.add(first);
        }

        for(Fruit fruit : finalList){
            System.out.println(fruit);
        }
    }
}

@Getter
@Setter
@ToString
class Fruit{
    private String id;
    private String name;
    private boolean isChecked;

    Fruit(String id, String name, boolean isChecked) {
        this.id = id;
        this.name = name;
        this.isChecked = isChecked;
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

It sounds like you want the fruits unique to the first list to go at the end of the final list, and the fruits in both the first and second list go at the beginning of the final list.

For a problem like this, it might be better to use a LinkedList instead of an ArrayList, at least for the final list. Usually you're better off with an ArrayList, but in this case you can benefit from inserting an element at the front of the list is an \$O(1)\$ operation instead of \$O(n)\$.

You're on the right track with using a Map to speed this up with large data sets. Here is a potentially more efficient implementation:

  1. Loop over the first list, filling a map firstMap with key: firstItem value: false

  2. Loop over the second list, and for each item, insert the result of firstMap.containsKey(secondItem) into firstMap with a key of secondItem

  3. Finally, loop over the first array, if firstMap.get(firstItem) is true, insert the item at the front of the final list (it was in both lists). Else add it to the end of the final list (it was only in the first list).

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Linked list is slower than array list up to 10k elements or so when inserting at head. Because of cache coherency and no indirection. Please benchmark before deciding to use linked lists. \$\endgroup\$
    – Emily L.
    Jan 28, 2019 at 8:49
  • \$\begingroup\$ Really? 10k, not only 10? A reference would be nice. \$\endgroup\$ Oct 27, 2019 at 7:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.