高效比较2个要替换和订购的列表

时间:2019-01-18 09:38:10

标签: java

我有2个列表,其中包含一些对象(水果类)。我正在使用第三个列表根据以下两个条件添加这些元素。

我希望将第一列表中的每个对象添加到第三列表中。但是,如果我在第二个列表中有一个匹配的对象(基于id和isChecked进行匹配),我想将第二个列表中的对象添加到第三个列表中,而忽略第一个列表中的对象。

如果我做了第一个要点上提到的开关,我想将该对象移到第三个列表的第一个元素上。

我可以使用以下代码。但是我发现它效率很低。有更好的解决方法吗?

请记住,我无法控制第二个列表,但是第一个列表来自Rest端点,我目前正在将其捕获为列表。不确定我是否应该选择地图。请指教。

示例:

在以下示例中,预期列表输出为[f2,f5,f1,f3,f4](基于名称)。

是cos,我拥有第一个列表中的所有元素。 f2和f5来自第二个列表(它们与第一个列表中的元素匹配,并且isChecked设置为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;
    }
}

4 个答案:

答案 0 :(得分:3)

我的建议是为Object#equals(java.lang.Object)类覆盖Fruit

$ ls -l /Users/username/workspace/Swift/sis/sis/data.json
-rwxrwxrwx@ 1 username  staff  165563 16 Jan 17:14 /Users/username/workspace/Swift/sis/sis/data.json

。 。 。

@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);
}

List<Fruit> finalList = new ArrayList<>(secondList); finalList.retainAll(firstList); for (Fruit fruit : firstList) { if (!finalList.contains(fruit)) finalList.add(fruit); } 的订单现在为finalList

由于[f2, f5, f1, f3, f4]方法的重写,此方法再次起作用。

请参见java.util.List.retainAll()

这些方法中的每一个都为每个循环和搜索操作使用线性 O(n)计算时间-例如equalsindexOf


与该主题相关,我认为您还不清楚要实现什么:

  

retainAllf2排在第二个列表的前面(它们与第一个列表中的元素匹配,并且f5设置为isChecked)在顺序的前面

如果第一个列表中的true设置为isChecked,该怎么办?第二个列表中的元素是否始终排在最前面,并且false的顺序得以保留,所以基本上您希望在同一结果列表中进行两种不同的排序?

答案 1 :(得分:2)

我认为您可以使用Map来建立结果列表。构建第三张列表需要 O(n)额外的空间和 O(n)时间。

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;
}

输出:

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}

答案 2 :(得分:0)

您可以在下面尝试

  1. 将所有项目从第一列表添加到第三列表
  2. 比较第二和第三列表并删除常见项目或条件(请参见Fruit Class中的compareTo方法)
  3. 如果从第3个列表中删除了项目,则将第2个列表中的相同项目添加到第3个列表中

请参见下面的代码,

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());
}

具有可比性的水果类

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;
        }
    }
}

结果是, f1 f3 f4 f2 f5

答案 3 :(得分:0)

假设firstList的长度为n,secondList的长度为m,则您可能需要花费O(nlogn) + O(mlogm) +O(n+m)来解决问题。像合并排序这样的算法。

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;
}