Optimierung der Deduplizierung von Listensammlungen


Kürzlich gab es eine weitere Codeüberprüfungsrunde und ich habe einige Codes gefunden, die die Entfernung von Duplikaten implementieren. Ich verwende list.contain ...

Bild

Ich habe darüber nachgedacht: Haben viele Anfänger auch dieses Problem der Deduplizierung?

Also habe ich beschlossen, das zu klären und zu teilen.

Text

Erstellen Sie zunächst eine Liste simulierter Daten mit insgesamt 2.000 Elementen. Die Hälfte der Daten, 1.000 Elemente, sind Duplikate:

public static List<String> getTestList() {
    
    
    List<String> list = new ArrayList<>();
    for (int i = 1; i <= 10000; i++) {
    
    
        list.add(String.valueOf(i));
    }
    for (int i = 10000; i >= 1; i--) {
    
    
        list.add(String.valueOf(i));
    }
    return list;
}

enthält, um Duplikate zu entfernen

Schauen wir uns zunächst den Code an, den wir zum Entfernen von Duplikaten verwenden:

/**
 * 使用 list.contain 去重
 *
 * @param testList
 */
private static void useContain2Distinct(List<String> testList) {
    
    
    System.out.println("contains 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = new ArrayList<>();
    for (String str : testList) {
    
    
        if (!testListDistinctResult.contains(str)) {
    
    
            testListDistinctResult.add(str);
        }
    }
    System.out.println("contains 去重完毕,条数:" + testListDistinctResult.size());
}

Rufen wir es an und sehen, wie lange es dauert:

public static void main(String[] args) {
    
    
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useContainDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

zeitaufwendig:

Bild

Bewertung: Die Effizienz von list.contain. Mein Vorschlag ist, es einfach zu kennen und nicht zu verwenden.

Wie wir alle wissen, gibt es in Set keine doppelten Daten. Werfen wir also einen Blick auf die Leistung der Verwendung von HashSet zum Entfernen von Duplikaten:

Legen Sie fest, dass Duplikate entfernt werden sollen

ps: Hier verwenden wir die Add-Methode von Set, um Duplikate zu entfernen.

/**
 * 使用set去重
 *
 * @param testList
 */
private static void useSetDistinct(List<String> testList) {
    
    
    System.out.println("HashSet.add 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = new ArrayList<>(new HashSet(testList));
    System.out.println("HashSet.add 去重完毕,条数:" + testListDistinctResult.size());
}

Rufen wir es an und sehen, wie lange es dauert:

public static void main(String[] args) {
    
    
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useSetDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

zeitaufwendig:

Bild

Bewertung: Die Effizienz von HashSet, mein Vorschlag ist empfehlenswert.

Warum ist der Zeitunterschied so groß?

Schauen wir uns ohne weitere Umschweife den Quellcode an:

list.contains(o):

Bild

Sie können sehen, dass index(o) im Inneren verwendet wird:

Bild

Zeitkomplexität: O(n) n: Anzahl der Elemente

Sehen wir uns also an, wie set.add(o) aussieht:

Bild

Wenn ich die Karte hinzufüge, werde ich nicht auf das Klischee eingehen. Nach dem Hashing wird sie direkt an eine bestimmte Position gestopft. Zeitkomplexität: O(1).

Welches ist also schneller, O(n) oder O(1)? Offensichtlich.

Bild

ps: Lassen Sie uns übrigens über den Inhalt des Hashsets sprechen.

Bild

Die Zeitkomplexität ist auch: O(1)

Bild

Schauen wir uns also abschließend andere Deduplizierungen an:

Doppelte for-Schleife, entfernen, um Duplikate zu entfernen

/**
 * 使用双for循环去重
 * @param testList
 */
private static void use2ForDistinct(List<String> testList) {
    
    
    System.out.println("list 双循环 开始去重,条数:" + testList.size());
    for (int i = 0; i < testList.size(); i++) {
    
    
        for (int j = i + 1; j < testList.size(); j++) {
    
    
            if (testList.get(i).equals(testList.get(j))) {
    
    
                testList.remove(j);
            }
        }
    }
    System.out.println("list 双循环  去重完毕,条数:" + testList.size());
}
public static void main(String[] args) {
    
    
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    use2ForDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

zeitaufwendig:

Bild

Bewertung: Wissen Sie es einfach, es ist nur zum Spaß, verwenden Sie es nicht, es ist zu langsam und der Code sieht chaotisch aus

Deutliche Deduplizierung des Streams:

/**
 * 使用Stream 去重
 *
 * @param testList
 */
private static void useStreamDistinct(List<String> testList) {
    
    
    System.out.println("stream 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = testList.stream().distinct().collect(Collectors.toList());
    System.out.println("stream 去重完毕,条数:" + testListDistinctResult.size());
}
public static void main(String[] args) {
    
    
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useStreamDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

zeitaufwendig:

Bild

Bewertung: Nicht schlecht, vor allem weil der Code recht prägnant und ein wenig verlockend ist.

Supongo que te gusta

Origin blog.csdn.net/weixin_44030143/article/details/131146229
Recomendado
Clasificación