Отберите все дубликаты в наборе

Я использую Set, чтобы изолировать уникальные значения списка (в этом случае я получаю набор баллов):

Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);

Это вернет набор уникальных точек, но для каждого элемента в listToCull я бы хотел проверить следующее: если есть дубликат, отберите все дублирующиеся элементы. Другими словами, я хочу, чтобы pointSet представлял набор элементов в listToCull, которые уже являются уникальными (у каждого элемента в pointSet не было дубликатов в listToCull). Любые идеи о том, как реализовать?

РЕДАКТИРОВАТЬ - Я думаю, мой первый вопрос нуждается в уточнении. Ниже приведен код, который будет выполнять то, что я прошу, но я хотел бы знать, есть ли более быстрый путь. Предполагая, что listToCull представляет собой список PVectors с дубликатами:

Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);
    List<PVector> uniqueItemsInListToCull = new ArrayList<PVector>();
    for(PVector pt : pointSet){
        int counter=0;
        for(PVector ptCheck : listToCull){
            if(pt==ptCheck){
                counter++;
            }
        }
        if(counter<2){
            uniqueItemsInListToCull.add(pt);
        }
    }

uniqueItemsInListToCull будет отличаться от pointSet. Я хотел бы сделать это без петель, если это возможно.

4 ответа

Решение

Вам придется заняться программированием самостоятельно: создать два пустых набора; Он будет содержать уникальные элементы, остальные дубликаты. Затем переберите элементы listToCull, Для каждого элемента проверьте, находится ли он в дублирующем наборе. Если это так, игнорируйте это. В противном случае проверьте, находится ли он в уникальном наборе элементов. Если это так, удалите его и добавьте в набор дубликатов. В противном случае добавьте его в набор уникальных элементов.

Если твой PVector класс хороший hashCode() метод, HashSetОни довольно эффективны, поэтому производительность не будет слишком плохой.

Непроверенные:

Set<PVector> uniques = new HashSet<>();
Set<PVector> duplicates = new HashSet<>();
for (PVector p : listToCull) {
    if (!duplicates.contains(p)) {
        if (uniques.contains(p)) {
            uniques.remove(p);
            duplicates.add(p);
        }
        else {
            uniques.add(p);
        }
    }
}

Кроме того, вы можете использовать стороннюю библиотеку, которая предлагает Bag или же MultiSet, Это позволяет подсчитать, сколько вхождений каждого элемента в коллекции, а затем в конце отбросить все элементы, где количество отличается от 1.

Итак, вы хотите, чтобы pointSet содержал элементы в listToCull, у которых нет дубликатов? Это правильно?

Я был бы склонен создать карту, затем дважды выполнить итерацию по списку, в первый раз добавив нулевое значение для каждого PVector, во второй раз добавив единицу к значению для каждого PVector, поэтому в конце у вас есть карта с на счет. Теперь вас интересуют ключи карты, для которых значение точно равно единице.

Это не совсем эффективно - вы работаете с элементами списка больше, чем это абсолютно необходимо, но это довольно чисто и просто.

Хорошо, вот решение, которое я придумала, я уверен, что есть лучшие, но это работает для меня. Спасибо всем, кто дал направление!

Чтобы получить уникальные предметы, вы можете запустить Set, где listToCull - это список PVectors с дубликатами:

    List<PVector> culledList = new ArrayList<PVector>();
    Set<PVector> pointSet = new LinkedHashSet<PVector>(listToCull);
    culledList.addAll(pointSet);

Чтобы пойти дальше, предположим, что вам нужен список, в котором вы удалили все элементы в listToCull, которые имеют дубликаты. Вы можете просмотреть список и проверить, есть ли он в наборе для каждого элемента. Это давайте сделаем один цикл, а не вложенный цикл:

    Set<PVector> pointSet = new HashSet<PVector>(listToCull);
    Set<PVector> removalList = new HashSet<PVector>();//list to remove

    for (PVector pt : listToCull) {
        if (pointSet.contains(pt)) {
            removalList.add(pt);
        }
        else{
            pointSet.add(pt);
        }
    }
    pointSet.removeAll(removalList);
    List<PVector> onlyUniquePts = new ArrayList<PVector>();
    onlyUniquePts.addAll(pointSet);

То, что вы ищете, это intersection:

При условии, что PVector (ужасное имя, кстати) реализует hashCode() а также equals() правильно Set устранит дубликаты.

Если вы хотите intersection из List и существующий Set создать Set от List затем используйте Sets.intersection() из Гуавы, чтобы получить общие для обоих наборов.

public static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2) 

Возвращает неизменяемое представление пересечения двух множеств. Возвращенный набор содержит все элементы, которые содержатся в обоих вспомогательных наборах. Порядок итерации возвращаемого набора совпадает с порядком в set1. Результаты не определены, если set1 и set2 являются наборами, основанными на различных отношениях эквивалентности (как HashSet, TreeSet и keySet IdentityHashMap все).

Примечание. Возвращенное представление работает немного лучше, когда set1 является меньшим из двух наборов. Если у вас есть основания полагать, что один из ваших наборов, как правило, будет меньше другого, передайте его первым. К сожалению, поскольку этот метод устанавливает общий тип возвращаемого набора на основе типа первого переданного набора, это может в редких случаях вынудить вас выполнить приведение, например:

Set aFewBadObjects =... Установить manyBadStrings =...

// невозможно, чтобы строка не находилась в пересечении
SuppressWarnings("unchecked") Set badStrings = (Set) Sets.intersection( aFewBadObjects, manyBadStrings); Это прискорбно, но должно появляться очень редко.

Вы также можете сделать union, complement, difference а также cartesianProduct так же как filterОчень легко.

Другие вопросы по тегам