c# - 在不复制源列表的情况下高效地对 IList<T> 进行排序

标签 c# .net collections

鉴于下面的测试用例,我如何:

  1. IList<TestObject> 进行排序基于一个匹配的索引IdIList<int>列表。
  2. 不匹配的值被移动到列表的末尾并按其原始索引排序。在这种情况下,由于索引列表中不存在 3 和 4,我们希望看到 list[3] == 3list[4] == 4 .
  3. 虽然我知道这可以通过 linq 实现,但我需要求助于原始列表而不是创建一个新列表(由于列表的存储方式)。
  4. 源列表必须是 IList (我不能使用 List<T> )

这是测试:

    public class TestObject
    {
        public int Id { get; set; }
    }

    [Test]
    public void Can_reorder_using_index_list()
    {
        IList<TestObject> list = new List<TestObject>
        {
            new TestObject { Id = 1 },
            new TestObject { Id = 2 },
            new TestObject { Id = 3 },
            new TestObject { Id = 4 },
            new TestObject { Id = 5 }
        };

        IList<int> indexList = new[] { 10, 5, 1, 9, 2 };

        // TODO sort

        Assert.That(list[0].Id, Is.EqualTo(5));
        Assert.That(list[1].Id, Is.EqualTo(1));
        Assert.That(list[2].Id, Is.EqualTo(2));
        Assert.That(list[3].Id, Is.EqualTo(3));
        Assert.That(list[4].Id, Is.EqualTo(4));
    }

更新:

按照要求,这是我尝试过的,但是 1) 它只适用于 List<T>和 2) 我不确定这是最有效的方法:

       var clone = list.ToList();
        list.Sort((x, y) =>
        {
            var xIndex = indexList.IndexOf(x.Id);
            var yIndex = indexList.IndexOf(y.Id);

            if (xIndex == -1)
            {
                xIndex = list.Count + clone.IndexOf(x);
            }
            if (yIndex == -1)
            {
                yIndex = list.Count + clone.IndexOf(y);
            }

            return xIndex.CompareTo(yIndex);
        });

更新 2:

感谢@leppie、@jamiec、@mitch wheat - 这是工作代码:

    public class TestObjectComparer : Comparer<TestObject>
    {
        private readonly IList<int> indexList;
        private readonly Func<TestObject, int> currentIndexFunc;
        private readonly int listCount;

        public TestObjectComparer(IList<int> indexList, Func<TestObject, int> currentIndexFunc, int listCount)
        {
            this.indexList = indexList;
            this.currentIndexFunc = currentIndexFunc;
            this.listCount = listCount;
        }

        public override int Compare(TestObject x, TestObject y)
        {
            var xIndex = indexList.IndexOf(x.Id);
            var yIndex = indexList.IndexOf(y.Id);

            if (xIndex == -1)
            {
                xIndex = listCount + currentIndexFunc(x);
            }
            if (yIndex == -1)
            {
                yIndex = listCount + currentIndexFunc(y);
            }

            return xIndex.CompareTo(yIndex);
        }
    }

    [Test]
    public void Can_reorder_using_index_list()
    {
        IList<TestObject> list = new List<TestObject>
        {
            new TestObject { Id = 1 },
            new TestObject { Id = 2 },
            new TestObject { Id = 3 },
            new TestObject { Id = 4 },
            new TestObject { Id = 5 }
        };

        IList<int> indexList = new[] { 10, 5, 1, 9, 2, 4 };

        ArrayList.Adapter((IList)list).Sort(new TestObjectComparer(indexList, x => list.IndexOf(x), list.Count));

        Assert.That(list[0].Id, Is.EqualTo(5));
        Assert.That(list[1].Id, Is.EqualTo(1));
        Assert.That(list[2].Id, Is.EqualTo(2));
        Assert.That(list[3].Id, Is.EqualTo(3));
        Assert.That(list[4].Id, Is.EqualTo(4));
    }

最佳答案

看了一会儿,确实如前所述,您将需要 ArrayList.Adapter ,但是您会注意到它采用非通用 IList,因此需要进行一些转换:

ArrayList.Adapter((IList)list)

您还需要编写一个比较器,其中包含进行排序的逻辑。请原谅这个名字但是:

public class WeirdComparer : IComparer,IComparer<TestObject>
{
    private IList<int> order;
    public WeirdComparer(IList<int> order)
    {
        this.order = order;
    }
    public int Compare(object x, object y)
    {
        return Compare((TestObject) x, (TestObject) y);
    }

    public int Compare(TestObject x, TestObject y)
    {
        if(order.Contains(x.Id))
        {
            if(order.Contains(y.Id))
            {
                return order.IndexOf(x.Id).CompareTo(order.IndexOf(y.Id));    
            }
            return -1;
        }
        else
        {
            if (order.Contains(y.Id))
            {
                return 1;
            }
            return x.Id.CompareTo(y.Id);
        }
    }
}

编辑:为上面的比较器添加了实现

那么用法如下:

IList<int> indexList = new[] { 10, 5, 1, 9, 2 };
ArrayList.Adapter((IList)list).Sort(new WeirdComparer(indexList));

顺便说一下,this thread解释了一种将其转变为扩展方法的好方法,这将使您的代码更可重用且更易于阅读 IMO。

关于c# - 在不复制源列表的情况下高效地对 IList<T> 进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6660650/

相关文章:

c# - 何时在 C# 中使用 Stack<T> 集合?

c# - 泛型、重载决议和委托(delegate)(抱歉,找不到更好的标题)

.Net multipart/form-data 表单 enctype 和 UTF-8 "special"个字符 => � (MVC w/HttpPostedFileBase)

java - Comparator/able 中的 equals() 与 compareTo()(理论)

c++ - C++ Map中如何使用自定义结构

c# - Xamarin.UITest REPL。如何从 ListView 元素获取对象列表?

c# - 确定所有引用的程序集

c# - 服务器端错误的浏览器检测

.net - 将图像文件从 iOS 上传到 ASP.NET 时使用什么请求 header ?

java - java集合中重复删除背后的机制是什么(例如LinkedHashSet)