- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
检查下面的更新3
我发现我遇到的问题与.Net 4.0、4.0客户端和4.5的C#字符串比较器的一个已知严重问题有关,这将导致字符串列表的排序顺序不一致(导致输出依赖于其中的顺序)。输入和使用的排序算法)。该问题已于2012年12月报告给Microsoft,并以“无法解决”的方式关闭。可以使用一种解决方法,但是它慢得多,以致于大集合几乎不可行。
在实现不变的PatriciaTrie时,我想将其性能与System.Collections.Generic.SortedList进行比较。我使用以下文件https://github.com/rkapsi/patricia-trie/blob/master/src/test/resources/org/ardverk/collection/hamlet.txt创建了用于测试的输入单词表。
当使用Comparer<string>.Default
或StringComparer.InvariantCulture
作为键比较器在C#SortedList中插入每个单词时,无法使用常规搜索方法检索成功插入的多个条目(例如ContainsKey
返回false),但是键存在于通过迭代列表可以看到该列表。
更奇怪的是,比较器将从排序列表中检索的键与使用ContainsKey
找不到的搜索键进行比较时返回值“0”。
下面的完整示例在我的系统上演示了此问题。
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
// the problem is possibly related to comparison.
var fail = true;
var comparer = fail ? StringComparer.InvariantCulture : StringComparer.Ordinal;
// read hamlet (contains duplicate words)
var words = File
.ReadAllLines("hamlet.txt")
.SelectMany(l => l.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries))
.Select(w => w.Trim())
.Where(w => !string.IsNullOrEmpty(w))
.Distinct(comparer)
.ToArray();
// insert hamlet's words in the sorted list.
var list = new SortedList<string, int>(comparer);
var ndx = 0;
foreach (var word in words)
list[word] = ndx++;
// search for each of the added words.
foreach (var keyToSearch in words)
{
if (!list.ContainsKey(keyToSearch))
{
// was inserted, but cannot be retrieved.
Console.WriteLine("Error - Key not found: \"{0}\"", keyToSearch);
// however when we iterate over the list, we see that the entry is present
var prefix = keyToSearch.Substring(0, Math.Min(keyToSearch.Length, 3));
foreach (var wordCloseToSearchKey in list.Keys.Where(s => s.StartsWith(prefix)))
{
// and using the SortedList's supplied comparison returns 0, signaling equality
var comparisonResult = list.Comparer.Compare(wordCloseToSearchKey, keyToSearch);
Console.WriteLine("{0} - comparison result = {1}", wordCloseToSearchKey, comparisonResult);
}
}
}
// Check that sort order of List.Keys is correct
var keys = list.Keys.ToArray();
BinarySearchAll("list.Keys", keys, list.Comparer);
CheckCorrectSortOrder("list.Keys", keys, list.Comparer);
// Check that sort order of Array.Sort(List.Keys) is correct
var arraySortedKeys = CopySortSearchAndCheck("Array.Sort(List.Keys)", keys, list.Comparer);
// Check that sort order of the Array.Sort(input words) is correct
var sortedInput = CopySortSearchAndCheck("Array.Sort(input words)", words, list.Comparer);
Console.ReadLine();
}
static string[] CopySortSearchAndCheck(string arrayDesc, string[] input, IComparer<string> comparer)
{
// copy input
var sortedInput = new string[input.Length];
Array.Copy(input, sortedInput, sortedInput.Length);
// sort it
Array.Sort(sortedInput, comparer);
// check that we can actually find the keys in the array using bin. search
BinarySearchAll(arrayDesc, sortedInput, comparer);
// check that sort order is correct
CheckCorrectSortOrder(arrayDesc, sortedInput, comparer);
return sortedInput;
}
static void BinarySearchAll(string arrayDesc, string[] sortedInput, IComparer<string> comparer)
{
// check that each key in the input can be found using bin. search
foreach (var word in sortedInput)
{
var ix = Array.BinarySearch(sortedInput, word, comparer);
if (ix < 0)
// and it appears it cannot!
Console.WriteLine("Error - {0} - Key not found: \"{1}\"", arrayDesc, word);
}
}
static void CheckCorrectSortOrder(string arrayDesc, string[] sortedKeys, IComparer<string> comparer)
{
for (int n = 0; n < sortedKeys.Length; n++)
{
for (int up = n + 1; up < sortedKeys.Length; up++)
{
var cmp = comparer.Compare(sortedKeys[n], sortedKeys[up]);
if (cmp >= 0)
{
Console.WriteLine(
"{0}[{1}] = \"{2}\" not < than {0}[{3}] = \"{4}\" - cmp = {5}",
arrayDesc, n, sortedKeys[n], up, sortedKeys[up], cmp);
}
}
for (int down = n - 1; down > 0; down--)
{
var cmp = comparer.Compare(sortedKeys[n], sortedKeys[down]);
if (cmp <= 0)
{
Console.WriteLine(
"{0}[{1}] = \"{2}\" not > than {0}[{3}] = \"{4}\" - cmp = {5}",
arrayDesc, n, sortedKeys[n], down, sortedKeys[down], cmp);
}
}
}
}
}
StringComparer.Ordinal
时(例如,在上述示例中将
fail
更改为
false
),问题消失了,这似乎指向一个比较问题,但是我不太明白为什么。
Array.BinarySearch
,它也会为使用
SortedList.ContainsKey
找不到的相同键返回负(未找到)值。因此,这表明键的排序顺序不正确。
Array.Sort
对其进行排序,则对于有问题的键,输出的排序顺序会有所不同。
a1 < a2
和
a2 < a3
能够使比较保持一致,那么下面的内容也适用:
a1 < a3
。
Comparer<string>.Default
和
StringComparer.InvariantCulture
而言)。
class Program
{
static void Main(string[] args)
{
var comparer = StringComparer.InvariantCulture;
var a1 = "A";
var a2 = "a\'";
var a3 = "\'a";
PrintComparison("a1", a1, "a2", a2, comparer);
PrintComparison("a2", a2, "a3", a3, comparer);
PrintComparison("a1", a1, "a3", a3, comparer);
Console.ReadLine();
}
public static void PrintComparison(string firstSymbol, string first, string secondSymbol, string second, IComparer<string> comparer)
{
var cmp = comparer.Compare(first, second);
var result = cmp == 0 ? "=" : cmp < 0 ? "<" : ">";
Console.WriteLine("{0} {1} {2} ({3} {1} {4})", firstSymbol, result, secondSymbol, first, second);
}
}
a1 < a2 (A < a')
a2 < a3 (a' < 'a)
a1 > a3 (A > 'a)
public class WorkAroundStringComparer : StringComparer
{
private static readonly Func<CompareInfo, string, CompareOptions, int> _getHashCodeOfString;
private readonly CompareInfo _compareInfo;
private readonly CompareOptions _compareOptions;
static WorkAroundStringComparer()
{
// Need this internal method to compute hashcode
// as an IEqualityComparer implementation.
_getHashCodeOfString = BuildGetHashCodeOfStringDelegate();
}
static Func<CompareInfo, string, CompareOptions, int> BuildGetHashCodeOfStringDelegate()
{
var compareInfoType = typeof(CompareInfo);
var argTypes = new[] { typeof(string), typeof(CompareOptions) };
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var methods = compareInfoType.GetMethods(flags).ToArray(); ;
var method = compareInfoType.GetMethod("GetHashCodeOfString", flags, null, argTypes, null);
var instance = Expression.Parameter(compareInfoType, "instance");
var stringArg = Expression.Parameter(typeof(string), "string");
var optionsArg = Expression.Parameter(typeof(CompareOptions), "options");
var methodCall = Expression.Call(instance, method, stringArg, optionsArg);
var expr = Expression.Lambda<Func<CompareInfo, string, CompareOptions, int>>(methodCall, instance, stringArg, optionsArg);
return expr.Compile();
}
public WorkAroundStringComparer()
: this(CultureInfo.InvariantCulture)
{
}
public WorkAroundStringComparer(CultureInfo cultureInfo, CompareOptions compareOptions = CompareOptions.None)
{
if (cultureInfo == null)
throw new ArgumentNullException("cultureInfo");
this._compareInfo = cultureInfo.CompareInfo;
this._compareOptions = compareOptions;
}
public override int Compare(string x, string y)
{
if (ReferenceEquals(x, y))
return 0;
if (ReferenceEquals(x, null))
return -1;
if (ReferenceEquals(y, null))
return 1;
var sortKeyFor_x = _compareInfo.GetSortKey(x, _compareOptions);
var sortKeyFor_y = _compareInfo.GetSortKey(y, _compareOptions);
return SortKey.Compare(sortKeyFor_x, sortKeyFor_y);
}
public override bool Equals(string x, string y)
{
return Compare(x, y) == 0;
}
public override int GetHashCode(string obj)
{
return _getHashCodeOfString(_compareInfo, obj, _compareOptions);
}
}
StringComparer.InvariantCulture
。
StringComparer.InvariantCulture : 00:00:15.3120013
WorkAroundStringComparer : 00:01:35.8322409
StringComparer.Ordinal
退回。
最佳答案
可能与.Net Framework 4/4.5有关吗?我已经为.Net 3.5修改了您的示例,如下所示:
var words = ReadFile("hamlet.txt");
//...
private static string[] ReadFile(string path)
{
List<string> lines = new List<string>();
using (StreamReader sr = new StreamReader(path))
{
string text = sr.ReadToEnd();
lines.Add(text);
}
return lines.SelectMany(l => l.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).Select(w => w.Trim()))
.Where(w => !(w.ToCharArray().All(c => c == ' ')))
.ToArray();
}
关于C#SortedList <string,TValue> .ContainsKey成功添加键返回false,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17599084/
我有一个 Enumerable> .我想创建一个 bool TryGetValue(TKey, out TValue)它的扩展方法就像它在 Dictionary 中可用一样. 我试过了 public
我已经在谷歌上搜索了 Range 参数的作用的解释,但一无所获,所以我相信你会解释使用 It.IsInRange(TValue from, TValue to, Range rangeKind) 时设
//ModelFor(person =>person.Name); public void ModelFor( Expression> expression) { //Result s
作为MSDN says ConcurrentDictionary Class 表示一个线程安全的键值对集合,可以被多个线程同时访问。 但据我所知,System.Collections.Concurre
我有以下字典: IDictionary> myDictionary 我想将字典中的所有值作为 IList 获取.... 只是为了添加一些关于我是如何陷入这种情况的背景...... 我有一个方法可以获取
这似乎是这个 question 的副本,它会问“SortedList 和 SortedDictionary 有什么区别?”不幸的是,答案只是引用了 MSDN 文档(其中明确指出两者之间存在性能和内存使
我正在尝试创建自定义 ReadOnlyDictionary对于.NET 4.0。方法是保持私有(private) Dictionary对象以及标志以确定是否允许添加/删除和项目分配。 这很好用,但我想
我想这并不重要,我只是好奇。 如果字典和查找之间的区别是一个是一对一的,另一个是一对多的,那么不会通过另一个更具体/派生的版本来字典吗? 查找是键/值对的集合,其中键可以重复。字典是键/值对的集合,其
我想在给定 Dictionary 的情况下获取 TKey 和 TValue 的类型类型。 例如。如果类型是 Dictionary我想知道如何获得keyType = typeof(Int32) 和val
我想在给定 Dictionary 的情况下获取 TKey 和 TValue 的类型类型。 例如。如果类型是 Dictionary我想知道如何获得keyType = typeof(Int32) 和val
好的 - 所以我知道构建一个提供功能的工厂方法很简单;但鉴于 Dictionary是 IEnumerable> ,它不应该有一个等价于例如List 的Ctor吗?的ctor(IEnumerable r
我有一个返回 IDictionary > 的函数. 我有另一个函数需要 IDictionary > . 我需要将第一个函数的返回传递给第二个函数。 编译器不想将第一个隐式转换为第二个。那么如何在 O(
我有一个字典如下: public enum Role { Role1, Role2, Role3, } public enum Action { Action1, Action2, Action3,
我认为转换 IDictionary> 相当简单反对 IDictionary> , 但是 var val = (IDictionary>)Value; 抛出 System.InvalidCastExce
我正在努力处理一段简单的代码,即使它很简单,我也找不到解决方案。在一部分上,我有一个名为这样的事件 OnReadMessageParameter(Self, aName, aTypeInfo, poi
我知道当我转换数据类型时,Variants 的速度很慢,即使我只是简单地添加两个保存整数的变体值。我还发现了另一个类似的东西,看起来像变体,称为 TValue。 他们有什么区别? 我正在考虑使用 TS
我在调试时无法检查 TValue 变量的字符串值。悬停菜单不显示字符串值,甚至将其添加到监视列表似乎也很棘手。 给定一个非常基本的示例控制台应用程序,例如 program Project1; uses
我是 C# 的新手,有 Ruby 背景。我还有很多东西要学,这就是为什么我要问以下问题: 目标:1)我想创建一个以字符串为键和我想要的任何对象类型作为值的字典。像这样: Dictionary 2) 但
我需要使用 RTTI 遍历一个具有复杂结构的类。该类有几个我也想迭代的记录成员。 TRTTIHelpers = class public class function DoGetValu
我遇到了一个奇怪的问题,涉及重命名 TValue 数组类型,例如; TValueArray1 := TArray ; TValueArray2 := Array of TValue; 我的意思是说他们
我是一名优秀的程序员,十分优秀!