3

I have a controller with some @RequestParam(required = false) which have to return all medicines with some filters if they are exist, service for this controller, which contains logic for it, but it is not working. The main problem is that I have 3 ArrayList and I cannot come up with how to find all elements which exist in all three ArrayLists :)

    public List<Medicine> readAllMedicine(Double lessThenPrice, Double moreThenPrice, String name) {
    //when lessThenPrice < moreThenPrice, result of their queries will not have general elements
    if ((lessThenPrice != null && moreThenPrice != 0) && lessThenPrice < moreThenPrice) {
        return Collections.emptyList();
    }

    List<Medicine> resultList = new ArrayList<>();
    List<Medicine> lessList = new ArrayList<>();
    List<Medicine> moreList = new ArrayList<>();
    List<Medicine> nameList = new ArrayList<>();

    //checking if there are arguments from the controller
    if (lessThenPrice != null) {
        lessList.addAll(medicineDao.findMedicinesByPriceIsLessThan(lessThenPrice));
    }
    if (moreThenPrice != null) {
        moreList.addAll(medicineDao.findMedicinesByPriceIsGreaterThan(moreThenPrice));
    }
    if (name != null) {
        nameList.addAll(medicineDao.findMedicinesByName(name));
    }

    //saving general elements
    //this part is not working
    if (!lessList.isEmpty() || !moreList.isEmpty() || !nameList.isEmpty()) {
        List<Medicine> temp = new ArrayList<>(); //it will contain general part of lessList and moreList
        for (Medicine medicine : lessList) {
            if (moreList.contains(medicine)) {
                temp.add(medicine);
            }
        }
        for (Medicine medicine : nameList) {
            if (temp.contains(medicine)) {
                resultList.add(medicine);
            }
        }
        return resultList;
    }
    //if there is no args, just return all medicines
    return medicineDao.findAll();
}
vyesman
  • 41
  • 3

3 Answers3

5

The Collection#retainAll() method will only keep items that appear in another Collection; in other words, the intersection of two collections. Use it twice to get the intersection of three collections:

List<String> list1 = List.of("a", "b", "c", "q");
List<String> list2 = List.of("b", "d", "e", "q");
List<String> list3 = List.of("b", "q", "r", "s");
List<String> intersection = new ArrayList<String>(list1);
intersection.retainAll(list2);
intersection.retainAll(list3);
System.out.println(intersection); // [b, q]
Shawn
  • 47,241
  • 3
  • 26
  • 60
3

One option to get all the common elements from multiple lists would be to iterate on the first list and filter out all the elements which exist in every other list, so:

List<Medicine> resultList = lessList.stream()
        .filter(moreList::contains)
        .filter(namesList::contains)
        .collect(Collectors.toList());

While this works, in case of bigger lists, you may consider using HashSets instead of lists, for better performance.

Also, this code assumes Medicine class has proper implementation for equals and hashcode.

Ervin Szilagyi
  • 14,274
  • 2
  • 25
  • 40
  • Were equals and hashcode implemented, could he just add them all to a list and convert it to a Set? – Adrian M. Jan 30 '22 at 01:40
  • That wont work, the requirements are for the [intersection](https://en.wikipedia.org/wiki/Intersection_(set_theory)) of the elements. Adding all the elements into a list and convert the list into a set, will represent the [union](https://en.wikipedia.org/wiki/Union_(set_theory)) of the elements. – Ervin Szilagyi Jan 30 '22 at 01:46
  • Good call, I don't know how I lost focus on the actual problem. – Adrian M. Jan 30 '22 at 01:48
  • Method `contains()` has `O(n)` time complexity for a list, hence your solution will have overall `quadratic` time complexity which is very inefficient. – Alexander Ivanchenko Jan 30 '22 at 04:59
  • @AlexanderIvanchenko "While this works, in case of bigger lists, you may consider using HashSets instead of lists, for better performance." - I think you missed this in my answer. – Ervin Szilagyi Jan 30 '22 at 14:32
  • Moreover, stream evaluation is [lazy](https://stackoverflow.com/questions/21219667/stream-and-lazy-evaluation), meaning that complexity will be similar as you would get with `retainAll` for lists. Nevertheless, for linear complexity we should use sets. – Ervin Szilagyi Jan 30 '22 at 14:55
  • @AlexanderIvanchenko I did not take it personal, don't worry. :) – Ervin Szilagyi Jan 30 '22 at 22:41
2

The best way is using retainAll method which eliminates all uncommon items from the list. https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#retainAll(java.util.Collection)