我有一个高值(大于int 32)的字符串列表,如何在不解析的情况下按升序对它们进行排序?
List = {"4852154879","2652154879","9852154879","1952154879","0652154879"}
我尝试解析如下,但在没有解析
的情况下寻找替代方案和更好的方法Sorted List = List.OrderBy(x => long.Parse(x.serialNumber)).ToList();
答案 0 :(得分:10)
首先,我几乎肯定不会采取以下方法。我要么将输入转换为List<long>
,要么只使用你已经得到的代码,至少在我完全证明它不够好之前。
然而,由于这是一个非常有趣的问题,让我们尝试编写一个快速IComparer<T>
。这取决于:
比较两个值时,如果值的长度相同,我们可以使用序数字符串比较。否则:
这设法在没有对象分配的情况下执行每次比较。
像(完全未经测试)的东西:
public sealed class NumericComparer : IComparer<string>
{
public static readonly IComparer<string> Instance { get; } = new NumericComparer();
private NumericComparer() {}
public int Compare(string x, string y)
{
if (x.Length == y.Length)
{
return string.Compare(x, y, StringComparison.Ordinal);
}
int xIndex = FindFirstNonZeroIndex(x);
int yIndex = FindFirstNonZeroIndex(y);
int lengthComparison = (x.Length - xIndex).CompareTo(y.Length - yIndex);
if (lengthComparison != 0)
{
return lengthComparison;
}
return string.Compare(x, xIndex, y, yIndex, x.Length, StringComparison.Ordinal);
}
private static int FindFirstNonZeroIndex(string text)
{
for (int i = 0; i < text.Length; i++)
{
if (text[i] != '0')
{
return i;
}
}
// All zeroes? Return text.Length - 1, so that we treat this as
// "0".
return text.Length - 1;
}
}
然后,您可以使用以下内容对列表进行排序:
list.Sort(NumericComparer.Instance);
现在我刚刚对此进行基准测试......就我所知,它看起来与解析大致相同。实际上非常差 - 但比填充形式要好得多。
基准代码:
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
public class Program
{
private readonly List<string> list;
public Program()
{
list = Enumerable.Range(0, 100000)
.Select(_ => GenerateValue())
.ToList();
}
// Just to test the impact of copying...
[Benchmark]
public List<string> NoSorting()
{
var copy = new List<string>(list);
return copy;
}
[Benchmark]
public List<string> NoParsing()
{
var copy = new List<string>(list);
copy.Sort(NumericComparer.Instance);
return copy;
}
[Benchmark]
public List<string> WithParsing() => list.OrderBy(x => long.Parse(x)).ToList();
static void Main(string[] args)
{
BenchmarkRunner.Run<Program>();
}
[Benchmark]
public List<string> WithPadding()
{
int maxLength = list.Max(y => y.Length);
return list.OrderBy(x => x.PadLeft(maxLength, '0')).ToList();
}
// Use the same seed on all tests
static readonly Random random = new Random(1);
static string GenerateValue()
{
// Up to 11 digits...
long leading = random.Next(100000);
long trailing = random.Next(1000000);
long value = leading * 1000000 + trailing;
// Pad to 9, 10 or 11 randomly
int width = random.Next(3) + 9;
return value.ToString().PadLeft(width, '0');
}
}
// NumericComparer as per post
结果:
Method | Mean | Error | StdDev |
------------ |-------------:|-------------:|------------:|
NoSorting | 473.3 us | 9.359 us | 25.62 us |
NoParsing | 46,684.7 us | 932.466 us | 1,366.80 us |
WithParsing | 43,149.8 us | 790.116 us | 700.42 us |
WithPadding | 275,843.4 us | 3,083.376 us | 2,733.33 us |
替代想法,这绝对更简单:
(我还没有基准测试。)
答案 1 :(得分:2)
查看最低有效数字基数排序:https://en.wikipedia.org/wiki/Radix_sort#Least_significant_digit_radix_sorts
此方法不需要任何解析,它将正确排序字符串,因为数字的字符串值的顺序正确。它也不需要任何填充,即使这确实不是问题:)
但请注意,您必须自己编写代码,如果您不关心效率,使用Hatchet / Mong Zhu的解决方案会更简单。
答案 2 :(得分:2)
正如hatchet已在此处建议的那样是使用0
方法的填充
List<string> temp = new List<string> { "4852154879", "2652154879", "9852154879", "1952154879", "0652154879" };
int maxLength = temp.Max(y=>y.Length);
temp = temp.OrderBy(x=>x.PadLeft(maxLength, '0')).ToList();
免责声明:如果数字不是纯int
或long
而是浮点数(如评论中所指出),这种方法当然会失败。
编辑:
正如阴影所示,由于答案的发展,我将补充这一点:
通过此更改,您可以将Jon的基准测试速度提高约5%:
temp.OrderBy(x => (x.Length==maxLength) ? x : x.PadLeft(maxLength, '0')).ToList();
答案 3 :(得分:-3)
我猜您应该使用List<long>
代替List<string>
List<long> list = new List<long>(){4852154879,2652154879,9852154879,1952154879,0652154879};
list = list.OrderBy(x => x).ToList();
答案 4 :(得分:-8)
将它们排序为字符串,它将起作用。排序striungs做字母表,但也正确排序数字