Я пытаюсь оптимизировать фрагмент кода, который сравнивает элементы списка.
Например.
public void compare(Set<Record> firstSet, Set<Record> secondSet){
for(Record firstRecord : firstSet){
for(Record secondRecord : secondSet){
// comparing logic
}
}
}
Учтите, что количество записей в наборах будет большим.
Спасибо
Шехар
java
performance
set
Шехар
источник
источник
Ответы:
Это действительно зависит от того, что вы хотите сделать в логике сравнения ... т.е. что произойдет, если вы обнаружите элемент в одном наборе, которого нет в другом? У вашего метода есть
void
возвращаемый тип, поэтому я предполагаю, что вы выполните необходимую работу в этом методе.Более точный контроль, если он вам нужен:
Если вам нужно получить элементы, которые находятся в одном наборе, а не в другом.
РЕДАКТИРОВАТЬ:
set.removeAll(otherSet)
возвращает логическое значение, а не набор. Чтобы использовать removeAll (), вам нужно скопировать набор, а затем использовать его.Если содержимое
one
иtwo
пусто, значит, эти два набора равны. Если нет, то у вас есть элементы, которые сделали наборы неравными.Вы упомянули, что количество записей может быть большим. Если базовая реализация - это,
HashSet
то выборка каждой записи выполняетсяO(1)
вовремя, так что вы не можете добиться большего успеха, чем это.TreeSet
естьO(log n)
.источник
equals
быстрее, чем два вызоваcontainsAll
в худшем случае; увидеть мой ответ.Если вы просто хотите узнать, равны ли наборы,
equals
методAbstractSet
реализован примерно так, как показано ниже:Обратите внимание, как он оптимизирует общие случаи, когда:
После этого
containsAll(...)
вернется,false
как только найдет элемент в другом наборе, которого также нет в этом наборе. Но если все элементы присутствуют в обоих наборах, необходимо будет протестировать их все.Таким образом, в худшем случае производительность возникает, когда два набора равны, но не являются одинаковыми объектами. Эта стоимость обычно составляет
O(N)
илиO(NlogN)
зависит от реализацииthis.containsAll(c)
.И вы получите производительность, близкую к наихудшей, если наборы большие и отличаются лишь небольшим процентом элементов.
ОБНОВИТЬ
Если вы готовы потратить время на реализацию настраиваемого набора, существует подход, который может улучшить «почти такой же» случай.
Идея состоит в том, что вам нужно предварительно вычислить и кэшировать хэш для всего набора, чтобы вы могли получить текущее значение хэш-кода набора
O(1)
. Затем вы можете сравнить хэш-код для двух наборов в качестве ускорения.Как можно реализовать такой хэш-код? Хорошо, если бы установленный хэш-код был:
тогда вы можете дешево обновлять кэшированный хэш-код набора каждый раз, когда вы добавляете или удаляете элемент. В обоих случаях вы просто выполняете XOR хэш-кода элемента с текущим установленным хэш-кодом.
Конечно, это предполагает, что хэш-коды элементов стабильны, в то время как элементы являются членами наборов. Также предполагается, что функция хэш-кода классов элементов дает хороший разброс. Это связано с тем, что, когда два набора хэш-кода совпадают, вам все равно придется вернуться к
O(N)
сравнению всех элементов.Вы могли бы развить эту идею немного дальше ... по крайней мере, теоретически.
ПРЕДУПРЕЖДЕНИЕ. Это весьма умозрительно. «Мысленный эксперимент», если хотите.
Предположим, что у вашего класса элемента set есть метод для возврата криптографических контрольных сумм для элемента. Теперь реализуйте контрольные суммы набора, выполняя операцию XOR с контрольными суммами, возвращаемыми для элементов.
Что это нам дает?
Что ж, если мы предположим, что ничего скрытого не происходит, вероятность того, что любые два неравных элемента набора имеют одинаковые N-битные контрольные суммы, равна 2 -N . И вероятность того, что 2 неравных набора имеют одинаковые N-битные контрольные суммы, также равна 2 -N . Итак, моя идея состоит в том, что вы можете реализовать
equals
как:В предположениях выше, это даст вам неправильный ответ только один раз в 2- N раз. Если вы сделаете N достаточно большим (например, 512 бит), вероятность неправильного ответа станет незначительной (например, примерно 10 -150 ).
Обратной стороной является то, что вычисление криптографических контрольных сумм для элементов очень дорогое, особенно при увеличении количества битов. Так что вам действительно нужен эффективный механизм для запоминания контрольных сумм. И это может быть проблематично.
Другой недостаток заключается в том, что ненулевая вероятность ошибки может быть неприемлемой, независимо от того, насколько мала вероятность. (Но если это так ... как поступить со случаем, когда космический луч переворачивает критический бит? Или если он одновременно меняет один и тот же бит в двух экземплярах избыточной системы?)
источник
В Guava есть метод,
Sets
который может здесь помочь:источник
У вас есть следующее решение с https://www.mkyong.com/java/java-how-to-compare-two-sets/
Или, если вы предпочитаете использовать один оператор возврата:
источник
equals()
метод изAbstractSet
(поставляется с JDK), который почти такой же, как и решение здесь, за исключением дополнительных проверок на null . Интерфейс набора Java-11Есть решение O (N) для очень конкретных случаев, когда:
В следующем коде предполагается, что оба набора основаны на сопоставимых записях. Аналогичный метод может быть основан на компараторе.
источник
Если вы используете
Guava
библиотеку, это можно сделать:А потом сделайте на основании этого вывод.
источник
Я бы поместил второй набор в HashMap перед сравнением. Таким образом вы сократите время поиска второго списка до n (1). Как это:
источник
источник
Я думаю, что можно использовать ссылку на метод с методом equals. Мы предполагаем, что тип объекта без тени сомнения имеет свой метод сравнения. Простой и понятный пример здесь,
источник
set.equals(set2)