Ich habe 2 Listen mit einigen Objekten (Fruchtklasse). Ich verwende eine dritte Liste, um diese Elemente basierend auf den folgenden 2 Kriterien hinzuzufügen.

Ich möchte, dass jedes Objekt in der 1. Liste zur 3. Liste hinzugefügt wird. Wenn ich jedoch ein übereinstimmendes Objekt in der zweiten Liste habe (Übereinstimmung basierend auf id und isChecked), möchte ich das Objekt aus der zweiten Liste zur dritten Liste hinzufügen und das Objekt in der ersten Liste ignorieren.

Wenn ich den unter Punkt 1 erwähnten Schalter ausgeführt habe, möchte ich dieses Objekt zum ersten Element der dritten Liste verschieben.

Ich habe es mit folgendem Code arbeiten. Aber ich finde es sehr ineffizient. Gibt es einen besseren Weg, um das zu umgehen?

Denken Sie daran, dass ich keine Kontrolle über die zweite Liste habe, aber die erste Liste von einem Rest-Endpunkt stammt und ich sie derzeit als Liste erfasse. Unsicher, ob ich mich für eine Karte hätte entscheiden sollen. Bitte beraten.

Beispiel:

Im folgenden Beispiel lautet die erwartete Listenausgabe [f2, f5, f1, f3, f4] (basierend auf dem Namen).

Es ist, weil ich alle Elemente aus der ersten Liste habe. f2 und f5 standen vor der Reihenfolge, als sie aus der zweiten Liste stammten (sie stimmten mit den Elementen in der ersten Liste überein und hatten isChecked auf true gesetzt).

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
kang 18 Jän. 2019 im 12:38

4 Antworten

Beste Antwort

Ich denke, Sie könnten Map verwenden, um eine Ergebnisliste zu erstellen. Es dauert O (n) zusätzlichen Speicherplatz und O (n) Zeit, um die dritte Liste zu erstellen.

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

Ausgabe:

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
oleg.cherednik 18 Jän. 2019 im 10:44

Mein Rat ist, Object#equals(java.lang.Object) für die Fruit Klasse.

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

Um die gewünschte Sortierreihenfolge beizubehalten - zuerst die 2. Listenelemente, dann die nicht übereinstimmenden Elemente in der 2. - kann man Folgendes tun:

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

Die Reihenfolge finalList lautet jetzt [f2, f5, f1, f3, f4].

Dies funktioniert aufgrund der Überschreibung der equals -Methode erneut.

Siehe {{ X0}}.

Jeder dieser Ansätze verwendet eine lineare O (n) Rechenzeit für jede Schleife und Suchoperation - wie indexOf und retainAll.


In Bezug auf das Thema denke ich, dass es nicht wirklich klar ist, was Sie erreichen wollen:

f2 und f5 gingen vor die Reihenfolge, als sie aus der zweiten Liste kamen (sie stimmten mit den Elementen in der ersten Liste überein und hatten isChecked auf true gesetzt)

Was soll passieren, wenn isChecked in der ersten Liste auf false gesetzt wurde? Stehen die Elemente in der zweiten Liste immer an erster Stelle und wird die Reihenfolge isChecked beibehalten, sodass Sie grundsätzlich zwei verschiedene Arten der Sortierung in derselben resultierenden Liste wünschen?

3
fantaghirocco 18 Jän. 2019 im 13:49

Sie können unten versuchen

  1. Fügen Sie alle Elemente aus der 1. Liste zur 3. Liste hinzu
  2. Vergleichen Sie die 2. und 3. Liste und entfernen Sie die allgemeinen Elemente oder die Bedingung (siehe compareTo-Methode in der Obstklasse).
  3. Wenn Elemente aus der 3. Liste entfernt werden, fügen Sie dasselbe Element aus der 2. bis 3. Liste hinzu

Bitte beachten Sie den Code unten,

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

Obstklasse mit Vergleichbar,

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

Ergebnis ist f1 f3 f4 f2 f5

0
PrabaharanKathiresan 18 Jän. 2019 im 10:14

Angenommen, die Länge der ersten Liste ist n , die Länge der zweiten Liste ist m , Sie können O(nlogn) + O(mlogm) +O(n+m) kosten, um das Problem zu lösen. Der Algorithmus wie 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;
}
0
TongChen 18 Jän. 2019 im 13:11