I have 2 list containing some Objects (Fruit class). I am using a 3rd list to add these elements based on following 2 criteria.
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.
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;
}
}
My advice is to override Object#equals(java.lang.Object)
for the Fruit
class.
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Fruit))
return false;
return id.equals(((Fruit)obj).getId());
}
. . .
for (Fruit fruit : firstList) {
if (secondList.contains(fruit)) {
int index = secondList.indexOf(fruit);
fruit = secondList.get(index);
}
finalList.add(fruit);
}
In order to mantain the desired sort order - 2nd list elements first, then the unmatching elements in the 2nd - one can do:
List<Fruit> finalList = new ArrayList<>(secondList);
finalList.retainAll(firstList);
for (Fruit fruit : firstList) {
if (!finalList.contains(fruit))
finalList.add(fruit);
}
The finalList
order is now [f2, f5, f1, f3, f4]
.
This works again because of the equals
method override.
See java.util.List.retainAll()
.
Each of these approaches use a linear O(n) computational time for every loop and search operation - like indexOf
and retainAll
.
Related to the topic, I think it's not really clear what you want to achieve:
f2
andf5
went in front of the order as they came from second list (they matched elements in first list and hadisChecked
set totrue
)
What should happen if isChecked
in the first list were set to false
? Do the elements in the second list come always first and isChecked
order is retained so basically you want two different kind of sorting in the same resulting list?
I think you could use Map
to build result list. It takes O(n) additional space and O(n) time to build third list.
public static List<Fruit> buildFinalList(List<Fruit> firstList, List<Fruit> secondList) {
Map<String, Fruit> map = secondList.stream().collect(Collectors.toMap(Fruit::getId, Function.identity()));
List<Fruit> finalList = firstList.stream()
.filter(fruit -> map.containsKey(fruit.getId()))
.map(fruit -> map.get(fruit.getId()))
.collect(Collectors.toList());
firstList.stream()
.filter(fruit -> !map.containsKey(fruit.getId()))
.forEach(finalList::add);
return finalList;
}
Output:
Fruit{id='2', name='f2', isChecked=true}
Fruit{id='5', name='f5', isChecked=true}
Fruit{id='1', name='f1', isChecked=false}
Fruit{id='3', name='f3', isChecked=false}
Fruit{id='4', name='f4', isChecked=false}
You can try below
Please see the code below,
finalList.addAll(firstList);
for (Fruit secondfruit : secondList) {
boolean isRemoved = finalList.removeIf(firstfruit -> firstfruit.compareTo(secondfruit) == 0);
if(isRemoved) {
finalList.add(secondfruit);
}
}
for (Fruit fruit : finalList) {
System.out.println(fruit.getName());
}
Fruit class with Comparable,
class Fruit implements Comparable<Fruit> {
private String id;
private String name;
private boolean isChecked;
@Override
public int compareTo(Fruit o) {
if (o.isChecked && this.id.equalsIgnoreCase(o.id)) {
return 0;
}
else {
return 1;
}
}
}
Result is, f1 f3 f4 f2 f5
Assume the firstList length is n,the secondList length is m,your can cost O(nlogn) + O(mlogm) +O(n+m)
to solve the problem. The algorithm like merge sort.
public static List<Fruit> merge(List<Fruit> list1, List<Fruit> list2) {
// sort list1 and list2
Comparator<Fruit> comparator = Comparator.comparingInt(o -> Integer.parseInt(o.getId()));
Collections.sort(list1, comparator);
Collections.sort(list2, comparator);
List<Fruit> finalList1 = new ArrayList<>();
List<Fruit> finalList2 = new ArrayList<>();
int length1 = list1.size();
int length2 = list2.size();
int index1 = 0;
int index2 = 0;
while (index1 < length1 && index2 < length2) {
Fruit fruit1 = list1.get(index1);
Fruit fruit2 = list2.get(index2);
if (fruit1.getId().equals(fruit2.getId()) && fruit2.isChecked()) {
finalList2.add(fruit2);
index2++;
index1++;
} else {
finalList1.add(fruit1);
index1++;
}
}
while (index1 < length1) {
finalList1.add(list1.get(index1));
index1++;
}
finalList2.addAll(finalList1);
return finalList2;
}
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.