Frage

I create a list like this:

private List<byte[]> shaList = new List<byte[]>();

and fill it with millions of shas. Afterwards I wanna sort it like this:

shaList.Sort();

But it throws an exception:

Unbehandelte Ausnahme: System.InvalidOperationException: Fehler beim Vergleichen
von zwei Elementen im Array. ---> System.ArgumentException: Mindestens ein 
Objekt muss IComparable implementieren.
bei System.Collections.Comparer.Compare(Object a, Object b)
bei System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
bei System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] 
keys, IComparer`1 comparer, Int32 a, Int32 b)
bei System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 
left, Int32 right, IComparer`1 comparer)
bei System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index,
Int32 length, IComparer`1 comparer)
--- Ende der internen Ausnahmestapelüberwachung ---
bei System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index,
Int32 length, IComparer`1 comparer)
bei System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 
comparer)
bei System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, I
Comparer`1 comparer)
bei System.Collections.Generic.List`1.Sort()

I don't have a clue how to sort it on my own. I only had Bubble and Insertion Sort in school but sorting millions of hashes with bubble sort... xD

//3vilc00kie

War es hilfreich?

Lösung

You can implement a custom comparer:

class ByteArrayComparer : IComparer<byte[]> {
    public int Compare(byte[] x, byte[] y) {
        // implement your comparison criteria here
    }
}

And then sort your list like this:

List<byte[]> shaList = new List<byte[]>();
shaList.Sort(new ByteArrayComparer());

What your Compare function should return is defined here: http://msdn.microsoft.com/en-us/library/xh5ks3b3(v=vs.110).aspx

Basically, you have to return:

  • < 0 if x < y
  • 0 if x == y
  • > 0 if x > y

Andere Tipps

You get the exception because you are trying to sort a list with byte arrays. Since byte[] does not implement IComparable you cant do this

list.OrderBy(b => BitConverter.ToInt64(b, 0))

Not clear what you want, but maybe this:

shaList.Sort(System.Collections.StructuralComparisons.StructuralComparer.Compare);

StructuralComparisons is a static class introduced in .NET version 4.0 (2010). Its property StructuralComparer gives an object which compares "by structure", which is something like lexicographically after each entry in the array (or tuple). It does this by the method Compare; above, Compare is turned into a delegate by a method group conversion.

Important addition: This seems to only work if all byte arrays in your list have the same length.

Test code:

static void Main()
{
    var shaList = new List<byte[]>
    {
        new byte[] { 20, 29, },
        new byte[] { 22, 29, },
        new byte[] { 2, 255, },
        new byte[] { 22, 0, },
    };

    shaList.Sort(System.Collections.StructuralComparisons.StructuralComparer.Compare);
}

You can sort a byte[] but how do you want to sort a List<Byte()>? Which array comes first, and which last? Y

Here's an example using the overload of List.Sort that takes a Comparison<T>. It compares the sum of all bytes.

shaList.Sort((b1, b2) => b1.Sum(b => b).CompareTo(b2.Sum(b => b)));

Array.Sort() accepts either an IComparer<T> interface implementation or a Comparison<T> delegate. In either case, as noted by Paolo Tedesco, you must write a function that accepts two byte arrays and returns a numeric comparison result.

Here is a succinct implementation that efficiently does this:

int CompareByteArrays(byte[] a, byte[] b)
{
    var length = Math.Min(a.Length, b.Length);
    for (var i = 0; i < length; i++)
    {
        var diff = a[i] - b[i];
        if (diff != 0)
            return diff;
    }
    return a.Length - b.Length;
}

I stumbled upon this question which is quite old but very close to what I'm attempting to do nowadays, that's why I would like to contribute to this thread. I'm trying to sort a List<Byte[]>, whose values are the basically hashes and this is what I came up with:

[SuppressUnmanagedCodeSecurity]
internal static class NativeMethods
{
    [DllImport("MSVCRT.dll", CallingConvention=CallingConvention.Cdecl, ExactSpelling=true)]
    private static extern Int32 memcmp([In] IntPtr pointer1, [In] IntPtr pointer2, [In] UIntPtr count);

    internal static Int32 CompareSequences(Byte[] array1, Byte[] array2)
    {
        if (ReferenceEquals(array1, array2))
            return 0;

        if (array1 == null)
            return 1;

        if (array2 == null)
            return -1;

        if (array1.Length != array2.Length)
            return -Math.Min(Math.Max(array1.Length - array2.Length, -1), 1);

        unsafe
        {
            fixed (Byte* pointer1 = array1, pointer2 = array2)
                return -Math.Min(Math.Max(memcmp((IntPtr)pointer1, (IntPtr)pointer2, (UIntPtr)array1.Length), -1), 1);
        }
    }
}

Usage:

List<Byte[]> hashes = ComputeHashes();
hashes.Sort(NativeMethods.CompareSequences);

This is what it does:

  1. Null arrays are placed at the bottom of the list.
  2. Non-null arrays are sorted by length, then by "content":
    • In presence of mismatching lengths, bigger arrays prevail.
    • In presence of identical lengths, the array whose first mismatching byte is greater prevails.

Since you are working with hashes, there should be no null values and all the arrays in your list are supposed to have the same length. Hence, the comparison function could be simplified as follows:

internal static Int32 CompareSequences(Byte[] array1, Byte[] array2)
{
    unsafe
    {
        fixed (Byte* pointer1 = array1, pointer2 = array2)
            return -Math.Min(Math.Max(memcmp((IntPtr)pointer1, (IntPtr)pointer2, (UIntPtr)array1.Length), -1), 1);
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top