Альтернатива вложенному циклу для сравнения

Я в настоящее время пишу программу, которая должна сравнить каждый файл в ArrayList переменного размера. Прямо сейчас способ, которым я делаю это, через вложенный цикл кода:

         if(tempList.size()>1){
            for(int i=0;i<=tempList.size()-1;i++)
                //Nested loops.  I should feel dirty?
                for(int j=i+1;j<=tempList.size()-1;j++){
                    //*Gets sorted.
                    System.out.println(checkBytes(tempList.get(i), tempList.get(j)));
                }
            }

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

Сразу, каждое сравнение испытывает необходимость, чтобы быть сделанным, так или иначе, таким образом, производительность должна быть довольно устойчивой, но я умеренно убежден, что существует более чистый способ сделать это. Какие-либо указатели?

Править:: Это - только часть функции для ясности. Файлы были сравнены и помещены в блоки на основе длины - после прохождения через карты набора и нахождения блока, который больше, чем один в длине, это выполняет это. Таким образом - это все файлы того же размера. Я буду делать сравнение контрольной суммы, прежде чем я доберусь до байтов также, но прямо сейчас я просто пытаюсь очистить цикл.

Кроме того, святая корова этот сайт отвечает быстро.Спасибо, ребята.

EDIT2:: Извините, для дальнейшего разъяснения: часть обработки файла, на которой у меня есть достойное схватывание, я думаю - сначала, я выдерживаю сравнение и вид длиной, затем контрольной суммой, затем байтами - проблема, которую я имею, - то, как правильно иметь дело с необходимостью сравнить все файлы в ArrayList эффективно, предполагая, что они все должны быть сравнены. Если вложенный цикл достаточен для этого, это прохладно, я просто хотел проверить, что это было подходящим методом, мудрым конвенцией.

5
задан KGVT 23 April 2010 в 22:42
поделиться

5 ответов

Мой ответ на ваш вопрос EDIT2 состоит из двух частей

Часть состоит в том, что если у вас небольшое количество файлов, то ваш подход с использованием вложенного цикла должен будь умницей. Производительность составляет O (N ** 2) , а оптимальное решение - O (N) . Однако, если N достаточно мало, не будет иметь большого значения, какой подход вы используете. Вам нужно только рассмотреть альтернативное решение, если вы уверены, что N может быть большим.

Во второй части описывается алгоритм, который использует хэши файлов для получения решения O (N) для обнаружения дубликатов. Это то, на что ссылались предыдущие ответы.

  1. Создайте класс FileHash для представления хеш-значений файлов. Для этого необходимо определить методы equals (Object) и hashCode () , которые реализуют побайтовое равенство хэшей файлов.

  2. Создайте экземпляр карты HashMap > .

  3. Для каждого файла во входных данных ArrayList :

    1. Вычислите хэш файла и создайте для него объект FileHash .
    2. Найдите FileHash на карте:
    3. Если вы нашли запись, выполните побайтовое сравнение вашего текущего файла с каждым из файлов в списке, который вы получили с карты. Если вы найдете в списке повторяющийся файл, BINGO! В противном случае добавьте текущий файл в список.
    4. Если вы не нашли запись, создайте новую запись карты с «FileHash» в качестве ключа и текущим файлом в качестве первого элемента списка значений.

(Обратите внимание, что карта выше действительно мульти-карта и что существуют сторонние реализации, например, в коллекциях Apache Commons и Google. Для простоты я представил алгоритм в приведенной выше форме.)

Некоторые проблемы с производительностью:

  • Если вы используете хорошую криптографическую хеш-функцию для генерации хэшей файлов, то шансы найти запись в 3.3, которая имеет более одного элемента в списке, исчезающе мала, а шансы, что побайтовое сравнение файлов не будет говорят, что файлы равны, также исчезающе малы. Однако стоимость вычисления крипто-хэша будет больше, чем стоимость вычисления хэша более низкого качества.

  • Если вы действительно используете хеш-код более низкого качества, вы можете уменьшить потенциальные затраты сравнения большего количества файлов, посмотрев на размеры файлов, прежде чем выполнять побайтовое сравнение. Если вы это сделаете, вы можете сделать тип карты HashMap > , где FileTuple - это класс, содержащий как File , так и его длину. .

  • Вы можете потенциально снизить стоимость хеширования, используя хеш только (скажем) первого блока каждого файла. Но это увеличивает вероятность того, что два файла могут иметь одинаковый хэш, но при этом отличаться; например во 2-м блоке. Насколько это важно, зависит от характера файлов.(Но, например, если вы просто просчитали контрольную сумму первых 256 байтов коллекции файлов исходного кода, вы можете получить огромное количество коллизий ... из-за наличия идентичных заголовков авторских прав!)

3
ответ дан 13 December 2019 в 22:04
поделиться

Хорошей оптимизацией было бы сначала вычислить все хэши файлов, а затем выполнить один цикл по списку.

Это в основном потому, что вам все равно придется проверять каждую пару файлов в вашем списке, но это будет означать только O (1) сложность для каждой пары вместо того, чтобы вычислять много вещей для каждого файла, который вы собираетесь проверить. .

Вы можете ввести что-то вроде:

HashSet<YourFile> fileSet = new HashSet<YourFile>();
ArrayList<YourFile> files = new ArrayList<YourFile>();

class YourFile
{
  int hashcode = -1;

  public int hashCode()
  {
     // override it to provide an hashcode based on file contents
     // you can also cache it to avoid recalculating anything

     if (hashcode == -1)
       hashcode = calculateIt();

     return hashcode;
  }
}

// fill up files
files.add(...);

// do comparisons
for (YourFile f : files)
{
  if (fileSet.contains(f))
    // f and fileSet.get(f) are equal: this is a tricky utilization of the hashCode() method so be careful about it!
  else
  {
    fileSet.put(f);
    // since there's not a file with same hashcode you just add this one
  }
}

Это фактически отбросит внутренний цикл, поскольку при использовании hashSet.contains он проверит все уже добавленные файлы, но со сложностью O (1) .

Как указано в doublep, вы должны быть осторожны с производительностью, так как при простой проверке байтов вы остановитесь, как только обнаружите два разных байта, при вычислении хэша вам нужно будет проверить весь файл. Это будет хорошо работать, когда у вас много файлов или когда файл довольно мал ... Лучше всего протестировать оба подхода и посмотреть, есть ли заметные различия.

3
ответ дан 13 December 2019 в 22:04
поделиться

В зависимости от того, что именно вы делаете, вы можете получить значительное ускорение, никогда не сравнивая файлы разных размеров. Среди файлов одинакового размера сравнивайте только файлы с одинаковым хешем (по любому алгоритму), как это предлагается в других ответах.

РЕДАКТИРОВАТЬ:

Однако вычисление хэша может быть весьма продуктивным. Во-первых, никогда не делайте этого, если вы сравниваете файл только друг с другом: вам нужно полностью прочитать файл, чтобы построить хэш, и одного чтения уже достаточно для сравнения, поэтому вы ничего не получите.

Во-вторых, если вы редко ожидаете совпадения, а на самом деле файлы будут значительно отличаться (на ранней стадии), вычисление хэша может быть контрпродуктивным независимо от количества файлов для сравнения. Это связано с тем, что неудачное сравнение в такой ситуации завершится ошибкой раньше (т. Е. Не будет прочитан весь файл), в то время как для построения хэша вам потребуется полное чтение. В качестве альтернативы вы можете создать «частичный» хеш (например, хеш первых 10 КБ файла), но затем не забудьте использовать одинаковые фрагменты всех файлов.

2
ответ дан 13 December 2019 в 22:04
поделиться

Сравнение всего со всем остальным, как это, обязательно будет O (n²). Но есть уловки, которые можно попробовать. Главный из них - удешевить сравнения; это можно сделать, сгенерировав хэш-код для каждого файла и сначала сравнив их, что позволит, по крайней мере, избежать большинства сравнений (используйте достаточно хороший алгоритм, и вы избежите практически каждого). Вы также можете ускорить процесс, если вам не нужно сохранять информацию о том, какие файлы равны; создайте набор хэш-кодов для каждого файла и в конце проверьте, совпадает ли размер набора с размером списка файлов.

1
ответ дан 13 December 2019 в 22:04
поделиться

Одна крошечная очистка заключается в удалении теста начального размера - если размер меньше 2, он просто выпадет без каких-либо сравнений. Лучшее соблюдение соглашений о кодировании Java в циклах будет заключаться в сравнении i вместо i <= tempList.size () - 1 - это будет просто сделайте ваш код более понятным для других программистов. Ни одно из этих изменений не влияет на производительность.

for (int i = 0; i < tempList.size(); i++)
    for (int j = i + 1; j < tempList.size(); j++) {
        //*Gets sorted.
        System.out.println(checkBytes(tempList.get(i), tempList.get(j)));
    }
1
ответ дан 13 December 2019 в 22:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: