Сравнение байтовых массивов C #

У меня есть два байтовых массива на C # с использованием .NET 3.0.

Каков наиболее эффективный способ сравнить, содержат ли два байтовых массива одинаковое содержимое для каждого элемента?

Например, байтовый массив {0x1, 0x2} совпадает с {0x1, 0x2}. Но байтовый массив {0x1, 0x2} и байтовый массив {0x2, 0x1} - это не одно и то же.


person George2    schedule 07.09.2009    source источник
comment
Дубликат stackoverflow.com/questions/43289   -  person Hafthor    schedule 02.04.2010


Ответы (5)


Что ж, вы можете использовать:

public static bool ByteArraysEqual(byte[] b1, byte[] b2)
{
    if (b1 == b2) return true;
    if (b1 == null || b2 == null) return false;
    if (b1.Length != b2.Length) return false;
    for (int i=0; i < b1.Length; i++)
    {
        if (b1[i] != b2[i]) return false;
    }
    return true;
}

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

Здесь есть несколько оптимизаций, которые SequenceEqual не могут (или не работают) - например, предварительная проверка длины. Прямой доступ к массиву также будет немного более эффективным, чем использование перечислителя.

По общему признанию, в большинстве случаев это вряд ли будет иметь существенное значение ...

Вы могли бы возможно сделать это быстрее в неуправляемом коде, заставив его сравнивать 32 или 64 бита за раз вместо 8, но я бы не хотел кодировать это на лету.

person Jon Skeet    schedule 07.09.2009
comment
Для больших массивов, скажем 10k, будет ли распараллеливание эффективной оптимизацией или накладные расходы на потоки будут слишком большими? - person Darren; 27.11.2009
comment
Что вы думаете о IStructuralEquatable для этой цели? - person LCJ; 01.03.2013
comment
@Lijo: Признаюсь, я никогда полностью не понимал IStructuralEquatable и когда это уместно. - person Jon Skeet; 01.03.2013

Вы можете использовать метод SequenceEqual:

bool areEqual = firstArray.SequenceEqual(secondArray);

Как упоминалось в комментариях, SequenceEqual требуется .NET 3.5 (или LINQBridge, если вы используете VS2008 и ориентирована на более раннюю версию платформы).

person LukeH    schedule 07.09.2009
comment
Думаю, самый действенный способ. - person Vitaliy Ulantikov; 07.09.2009
comment
Не с точки зрения времени исполнения это не так. - person Jon Skeet; 07.09.2009
comment
@Veton: Это определенно меньше всего печатает! См. Ответ Джона для нескольких дополнительных оптимизаций. - person LukeH; 07.09.2009
comment
(Попробуйте сравнить массив байтов, содержащий 1 000 000 записей, с массивом, содержащим 1 000 001 запись, с помощью SequenceEqual. Он будет идти до конца, прежде чем вы заметите, что они имеют разную длину ...) - person Jon Skeet; 07.09.2009
comment
Это также не работает (без чего-то вроде LINQBridge) в .NET 3.0, как требуется в вопросе ... хотя возможно, что OP имел в виду .NET 3.5. - person Jon Skeet; 07.09.2009
comment
Я использую .Net 3.0, кажется, этот метод работает только в .Net 3.5. :-( - person George2; 07.09.2009

Джон упомянул одновременное сравнение нескольких байтов с использованием небезопасного кода, поэтому мне пришлось попробовать:

public unsafe bool ByteArraysEqual(byte[] b1, byte[] b2) {
   if (b1 == b2) return true;
   if (b1 == null || b2 == null) return false;
   if (b1.Length != b2.Length) return false;
   int len = b1.Length;
   fixed (byte* p1 = b1, p2 = b2) {
      int* i1 = (int*)p1;
      int* i2 = (int*)p2;
      while (len >= 4) {
         if (*i1 != *i2) return false;
         i1++;
         i2++;
         len -= 4;
      }
      byte* c1 = (byte*)i1;
      byte* c2 = (byte*)i2;
      while (len > 0) {
         if (*c1 != *c2) return false;
         c1++;
         c2++;
         len--;
      }
   }
   return true;
}

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

person Guffa    schedule 07.09.2009
comment
Хорошая концепция, хотя код не компилируется: нельзя присвоить p1, потому что это «фиксированная переменная» - person Edward Brey; 16.03.2011
comment
@ Эдвард Брей: Вы правы, это не сработает. Вам нужно объявить новые указатели внутри блока, чтобы их можно было изменять. Я исправил код. - person Guffa; 16.03.2011
comment
Быстрее использовать длинные (8 байт) в comp даже на 32-битных машинах. - person Joe; 28.07.2011

Если вас не слишком беспокоит производительность, вы можете рассмотреть IStructuralEquatable.

.NET Framework Поддерживается в версиях: 4.5, 4

Структурное равенство означает, что два объекта равны, потому что имеют равные значения. Он отличается от эталонного равенства.

Пример:

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
  IStructuralEquatable eqa1 = a1;
  return eqa1.Equals(a2, StructuralComparisons.StructuralEqualityComparer);
}

ССЫЛКА

  1. Какую проблему решает IStructuralEquatable и IStructuralComparable?
  2. Почему IStructuralEquatable и IStructuralComparable не являются общими?
  3. IStructuralEquatable Interface
person LCJ    schedule 01.03.2013
comment
Подчеркните этот пост: это может быть способ сделать что-то, когда мы используем последние версии фреймворка. - person timmi4sa; 14.08.2013

Если вы хотите, чтобы это было действительно быстро, вы можете использовать небезопасный код (что не всегда возможно):

    public static bool ArraysEqual(byte[] b1, byte[] b2)
    {
        unsafe
        {
            if (b1.Length != b2.Length)
                return false;

            int n = b1.Length;

            fixed (byte *p1 = b1, p2 = b2)
            {
                byte *ptr1 = p1;
                byte *ptr2 = p2;

                while (n-- > 0)
                {
                    if (*ptr1++ != *ptr2++)
                        return false;
                }
            }

            return true;
        }
    }
person Philippe Leybaert    schedule 07.09.2009