幞ããªããšã«ããããã®æäœã®ã³ã¹ãã¯OïŒnïŒã§ãã ãããã倧èŠæš¡ãªã³ã¬ã¯ã·ã§ã³ã®å Žåãæ¯èŒèªäœã®æå¹æ§ã¯ç§ãã¡ã«ãšã£ãŠéèŠã§ãã æååãããŒãšããŠéžæãããŠããå Žåãã©ã®æ¯èŒå®è£ ãããã©ã«ãã§äœ¿çšãããŸããããã®å®è£ ã¯æååã«é©ããŠããŸããïŒ
clients.Join(orders, c => c.Name, o => o.ClientName, (c, o) => CreateOrederDto(c, o));
ãŠãŒã¶ãŒãæ瀺çã«æå®ããªãå Žåãã³ã³ãã¬ãŒã¿ãŒã®å®è£ ã¯ã©ã®ããã«éžæãããŸããïŒ
Joinã¡ãœããã®ãœãŒã¹ã³ãŒãã§æ¬¡ã®åäœã確èªã§ããŸãã
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector) { if (outer == null) throw Error.ArgumentNull("outer"); if (inner == null) throw Error.ArgumentNull("inner"); if (outerKeySelector == null) throw Error.ArgumentNull("outerKeySelector"); if (innerKeySelector == null) throw Error.ArgumentNull("innerKeySelector"); if (resultSelector == null) throw Error.ArgumentNull("resultSelector"); return JoinIterator<TOuter, TInner, TKey, TResult>(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null); } static IEnumerable<TResult> JoinIterator<TOuter, TInner, TKey, TResult>(IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer) { Lookup<TKey, TInner> lookup = Lookup<TKey, TInner>.CreateForJoin(inner, innerKeySelector, comparer); foreach (TOuter item in outer) { Lookup<TKey, TInner>.Grouping g = lookup.GetGrouping(outerKeySelector(item), false); if (g != null) { for (int i = 0; i < g.count; i++) { yield return resultSelector(item, g.elements[i]); } } } }
ãŸããnullã¯JoinIteratorã¡ãœããã«æž¡ãããå éšã§ãã§ãã¯ã¯è¡ãããŸããããŸããCreateForJoinã¡ãœããã«Lookupãäœæãããšãã«ããã©ã¡ãŒã¿ãŒãšããŠnullãæž¡ãããŸãã
ãŸãã«Lookupã䜿çšããããšã¯ãæ瀺çã«èŠã€ããããšãã§ããŸãã ãã®ã¯ã©ã¹ã¯ãããŒã«ããèŠçŽ ãžã®ã¢ã¯ã»ã¹ãåããã³ã¬ã¯ã·ã§ã³ã§ãããããŒããšã«ããã€ãã®èŠçŽ ãä¿åã§ããŸããååšããªãããŒã«ããã¢ã¯ã»ã¹è©Šè¡ã®å Žåã 空ã®ã³ã¬ã¯ã·ã§ã³ãåã«è¿ãããŸãã
CreateForJoinã¡ãœããã«èå³ããããŸãïŒ
internal static Lookup<TKey, TElement> CreateForJoin(IEnumerable<TElement> source, Func<TElement, TKey> keySelector, IEqualityComparer<TKey> comparer) { Lookup<TKey, TElement> lookup = new Lookup<TKey, TElement>(comparer); ... } Lookup(IEqualityComparer<TKey> comparer) { if (comparer == null) comparer = EqualityComparer<TKey>.Default; this.comparer = comparer; groupings = new Grouping[7]; // 7 - , 7 }
EqualityComparer
ãžã§ããªãã¯ã€ã³ã¿ãŒãã§ã€ã¹IEqualityComparer <T>ã«ã¯ãæœè±¡ã¯ã©ã¹EqualityComparer <T>ã®åœ¢åŒã®åºæ¬çãªå®è£ ããããŸãããã®ã¯ã©ã¹ã«ã¯ãç¹å®ã®ã¯ã©ã¹Tã®æ¢å®ã®ã³ã³ãã¬ãŒã¿ãéžæããéçãªDefaultããããã£ããããŸããã€ãŸããTãã¯ã©ã¹ãæ§é ããŸãã¯åçŽåãã®ã¿ã€ãã®ãªããžã§ã¯ãã®çŸåšã®ã³ã³ãã¬ãŒã¿ãŒãéžæãããŸãã
å®å šãªéžæã³ãŒã ïŒã¿ã€ãã«ãã£ãŠç°ãªããŸã ïŒã¯ããªãè¯ããã§ãããJoinãšGroupByã®äœæ¥ãç解ãããšãã芳ç¹ããã¯èå³æ·±ããã®ã§ãã
- ãã€ãã®å Žåãç¹å¥ãªByteEqualityComparerãéžæãããŸãã ãã®ã¯ã©ã¹ã¯ããã€ãé åã®IndexOfå®è£ ãå«ãããããã€ãé åãæ¯èŒãããšãã®ããã©ãŒãã³ã¹ãæ¹åããããã«èšèšãããŠããŸãã
- TãIEquatable <T>ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ããå ŽåãIEquatable <T> .EqualsïŒTïŒã¡ãœããã®å®è£ ã®åŒã³åºãã«åºã¥ããŠãªããžã§ã¯ããæ¯èŒããGenericEqualityComparer <T>ãéžæãããŸãã ããã«ãåŒã³åºãåã«ãäž¡æ¹ã®ãã©ã¡ãŒã¿ãŒãnullã®äžçåŒããã§ãã¯ããŸãã
- TãIEquatable <U>ãå®è£ ããNullable <U>ã§ããå Žåã以åã®ãã®ãšåæ§ã§ãè¿œå ã®HasValueãã§ãã¯ãå«ãNullableEqualityComparer <T>ã¯ã©ã¹ã䜿çšãããŸãã
- åæåã®å Žåãåºæ¬åã«å¿ããŠãEnumEqualityComparer <T>å®è£ ã®1ã€ãéžæãããŸãïŒlongåã®å Žåãç¬èªã®ç¹å¥ãªå®è£ ããããŸãïŒãããã¯ãåæå€ãæ°å€ã«å€æããæ¹æ³ã®JITæé©åã®ã¿ãç°ãªããŸãã
- ãã以å€ã®å Žåã¯ãã¹ãŠãObject.Equalsã«åºã¥ããŠãªããžã§ã¯ããæ¯èŒããObjectEqualityComparer <T>ã䜿çšãããŸã ã ããã§ã¯ããã¹ãŠãéåžžã©ããã§ã-åç §åã®å Žåããªã³ã¯ã®ç䟡æ§ãéèŠãªåã®å Žå-ãªããžã§ã¯ãã®åã®äžèŽïŒObjectEqualityComparer <T>ã®å Žåã¯åžžã«trueïŒããã³ãªããžã§ã¯ãã®ãã¹ãŠã®ãã£ãŒã«ãã®å€ã®äžèŽã
Stringã¯ã©ã¹ã¯IEquatable <T>ã€ã³ã¿ãŒãã§ã€ã¹ãå®è£ ãããããæååãæ¯èŒãããšãã«ãã®ã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£ ãåŒã³åºãããŸãã
.NET Coreã®å Žåãã³ã³ãã¬ãŒã¿ãŒãéžæããããã©ã«ãã®å®è£ ã¯ç°ãªããŸã -æååã®å Žåã EqualityComparerForStringãéžæãããæ¯èŒã«ç䟡==æŒç®åã®ã¿ã䜿çšãããŸãã
æååæ¯èŒ
String.EqualsïŒæååå€ïŒã¡ãœããã¯ãæåååç §ã®ç䟡æ§ãæååã®é·ãã®ç䟡æ§ããã§ãã¯ãããããã®ããããã£ã«åºã¥ããŠç䟡æ§ãèšç®ã§ããªãå Žåãæååãããã¡ã® ïŒã»ãŒïŒ ãã€ãæ¯èŒãåŒã³åºããŸã ã
[System.Security.SecuritySafeCritical] // auto-generated [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private unsafe static bool EqualsHelper(String strA, String strB) { int length = strA.Length; fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar) { char* a = ap; char* b = bp; // #if AMD64 // AMD64 12 3 qword . while (length >= 12) { if (*(long*)a != *(long*)b) return false; if (*(long*)(a+4) != *(long*)(b+4)) return false; if (*(long*)(a+8) != *(long*)(b+8)) return false; a += 12; b += 12; length -= 12; } #else while (length >= 10) { if (*(int*)a != *(int*)b) return false; if (*(int*)(a+2) != *(int*)(b+2)) return false; if (*(int*)(a+4) != *(int*)(b+4)) return false; if (*(int*)(a+6) != *(int*)(b+6)) return false; if (*(int*)(a+8) != *(int*)(b+8)) return false; a += 10; b += 10; length -= 10; } #endif // \0 while (length > 0) { if (*(int*)a != *(int*)b) break; a += 2; b += 2; length -= 2; } return (length <= 0); } }
çç£æ§ãé«ããããã«ãMicrosoft ã¯ã«ãŒãã解ãã»ãããŸãã ã
ãã®ãããããã©ã«ãã§ã¯ããããã®è¡ã®å 容ã®ãã€ãæ¯èŒã䜿çšãããŸãã å¹æçã«ïŒ ããããã ãããŠãä»ã«ã©ã®ããã«æååãæ¯èŒã§ããŸããïŒ
StringComparer
Stringã¯ã©ã¹ã®å Žåã.netã«ã¯StringComparerã³ã³ãã¬ãŒã¿ã®ç¬èªã®æœè±¡å®è£ ãšãããããç¶æ¿ãããããã€ãã®ã¯ã©ã¹ãããããã®ã¯ã©ã¹ã®éçããããã£ãä»ããŠã€ã³ã¹ã¿ã³ã¹ã«ã¢ã¯ã»ã¹ã§ããŸãã 3ã€ã®ã¿ã€ãã®æååæ¯èŒã«ã¯ããããã倧æåãšå°æåã®åºå¥ã®æç¡ã«ããããã6ã€ã®å®è£ ããããŸãã
- CurrentCulture / CurrentCultureIgnoreCase-çŸåšã®æåãšèšèªïŒçŸåšã®ã¹ã¬ããã®æåïŒã®ã«ãŒã«ãèæ ®ãããåèªã«ããæ¯èŒ
- InvariantCulture / InvariantCultureIgnoreCase-èšèªãšæåããã³èšèªã®èŠåãèæ ®ããã«åèªã«åŸã£ãŠæ¯èŒããïŒCultureInfo.InvariantCultureã䜿çšïŒ
- Ordinal / OrdinalIgnoreCase-ãã€ãæ¯èŒ
StringComparerã¯IEqualityComparer <string>ãå®è£ ããããããã®åå«ã¯ãã¹ãŠIEqualityComparer <string>ãæåŸ ããããã©ã¡ãŒã¿ãŒãšããŠæå®ã§ããŸãã
ãããã®æ¯èŒæ¹æ³ã®èª¬æã«äœåºŠãåºäŒã£ãããšããããŸããããçŸåšã®æåãèæ ®ããèšèã«ããæ¯èŒããäœãæå³ããã®ããç§ã¯æ¬åœã«çåã«æããŸããã§ããã
ãã€ãæ¯èŒã¯ãEqualityComparer <String> .DefaultãéžæãããŠããå ŽåããŸãã¯Equalsã¡ãœãããæ瀺çã«åŒã³åºãããå Žåãšåãã§ããïŒ ãã®å Žåã OrdinalComparerã¯ã©ã¹ãæ¯èŒãæ åœããŸãã
public override bool Equals(string x, string y) { if (Object.ReferenceEquals(x ,y)) return true; if (x == null || y == null) return false; if( _ignoreCase) { if( x.Length != y.Length) { return false; } return (String.Compare(x, y, StringComparison.OrdinalIgnoreCase) == 0); } return x.Equals(y); }
倧æåãšå°æåãåºå¥ããå Žåãéãã¯ãªããžã§ã¯ãåç §ã®ç䟡æ§ã®éè€ãã§ãã¯ãšnullã®ãã§ãã¯ã«ãããŸãã ããã¯ããã»ã©å€ããããŸããããMSILã®èŠ³ç¹ããã¯ãæ°åã®åœä»€ã§ãã
IL_0000: nop IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: call bool [mscorlib]System.Object::ReferenceEquals(object, object) IL_0008: ldc.i4.0 IL_0009: ceq IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: brtrue.s IL_0013 IL_000f: ldc.i4.1 IL_0010: stloc.0 IL_0011: br.s IL_003e IL_0013: ldarg.0 IL_0014: brfalse.s IL_001f IL_0016: ldarg.1 IL_0017: ldnull IL_0018: ceq IL_001a: ldc.i4.0 IL_001b: ceq IL_001d: br.s IL_0020 IL_001f: ldc.i4.0 IL_0020: nop IL_0021: stloc.1 IL_0022: ldloc.1 IL_0023: brtrue.s IL_0029 IL_0025: ldc.i4.0 IL_0026: stloc.0 IL_0027: br.s IL_003e
倧æåãšå°æåãåºå¥ããªãä¿åã¯ã©ãã§ããïŒ ççŽã«èšã£ãŠããã®æäœã¯æåã«äŸåããããã ToLowerã®ãããªãã®ã¯æåŸ ããŠããŸããã§ããã ããããçµæã¯ãŸã æåŸ ãè¶ ããŠããŸããã String.CompareïŒxãyãStringComparison.OrdinalIgnoreCaseïŒãåŒã³åºãã«ã¯ã次ã®ã³ãŒããã©ã³ããå®è¡ãããŸãã
case StringComparison.OrdinalIgnoreCase: if (this.Length != value.Length) return false; // ASCII , . if (this.IsAscii() && value.IsAscii()) { return (CompareOrdinalIgnoreCaseHelper(this, value) == 0); } #if FEATURE_COREFX_GLOBALIZATION return CompareInfo.CompareOrdinalIgnoreCase(strA, 0, strA.Length, strB, 0, strB.Length); #else // return TextInfo.CompareOrdinalIgnoreCase(strA, strB); #endif
IsAsciiãã¹ããæ£åœåããããã«ãã©ãã»ã©æªãå€æãäžãå¿ èŠãããã®ã§ããããã
ASCIIæååã®å Žåãæ€èšŒã¯å®éã«æåããšã«å®è¡ãããåæåã®å€§æåãšå°æåããã§ãã¯ãããå¿ èŠã«å¿ããŠ0x20ã®åçŽãªæžç®ã«ãã£ãŠå€§æåã«å€æãããŸãã
private unsafe static int CompareOrdinalIgnoreCaseHelper(String strA, String strB) { int length = Math.Min(strA.Length, strB.Length); fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar) { char* a = ap; char* b = bp; while (length != 0) { int charA = *a; int charB = *b; Contract.Assert((charA | charB) <= 0x7F, "strings have to be ASCII"); // if ((uint)(charA - 'a') <= (uint)('z' - 'a')) charA -= 0x20; if ((uint)(charB - 'a') <= (uint)('z' - 'a')) charB -= 0x20; if (charA != charB) return charA - charB; // a++; b++; length--; } return strA.Length - strB.Length; } }
éASCIIæååã®å Žåã ãã€ãã£ã C ++ ã³ãŒããåŒã³åºãããæ¡ä»¶ã«å¿ããŠãWindowsã«ãŒãã«ããã®æååãæ¯èŒããããã®ã¡ãœãããåŒã³åºãããšãã§ããŸãïŒã³ã¡ã³ãã«ãã£ãŠå€æãããšãããã¯Windows XPã§ã®ã¿å¯èœã§ãïŒåãæåããšã®æ¯èŒãå®è¡ããåæåãäžäœã«å€æããã¡ãœãããªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã®æåããŒãã«ã®ã±ãŒã¹ããŒã¹ã®ã¬ãžã¹ã¿ã
åãã³ã³ãã¬ãŒã¿ã®ããã©ãŒãã³ã¹ã¯ãçž®éã®å Žåãè¡ã®1æåãå€æŽããããšã«ãããå ¥åããŒã¿ã«å¿ããŠç°ãªãå Žåããããããããã¯å®éã«ããã©ãŒãã³ã¹ã®åé¡ã«é¢å¿ãåŒãèµ·ãããŸãã
äœç©ã¯ã©ãã§ããïŒ CultureAwareComparerã¯ã©ã¹ã¯ã«ã«ãã£ãå ¥åãšããŠåãå ¥ããããã«åºã¥ããŠæååãšã±ãŒã¹ãç¡èŠããããã©ããã瀺ããã©ã°ãæ¯èŒããŸãã ã«ã«ãã£ã«é¢ããæ å ±ã«ã¯CompareInfoããããã£ãå«ãŸãããã®ãªããžã§ã¯ãã«ã¯CultureAwareComparerã§äœ¿çšããããã®ã«ã«ãã£ãèæ ®ããŠæååãæ¯èŒããã¡ãœãããå«ãŸããŸãã
return (_compareInfo.Compare(x, y, _ignoreCase? CompareOptions.IgnoreCase : CompareOptions.None) == 0);
æ®å¿µãªãããå®éã«ã¯ãã€ãã£ãã³ãŒããåŒã³åºãããåã³ã«ãŒãã«ã«ã¯ããŒã«ãããŠæååãœãŒãé¢æ°ãåŒã³åºããããããå éšã«ã¯èå³æ·±ããã®ã¯ãããŸããã ã³ãŒãã®äžè¶³ãè£ãããã«ãcoreclrã®æååé¢æ°ã§ç¹°ãè¿ãèŠãããæç²ã次ã«ç€ºããŸãã
// TODO: Remove this workaround after Vista SP2 &/or turkic CompareStringEx() gets fixed on Vista. // If its Vista and we want a turkik sort, then call CompareStringW not CompareStringEx LPCWSTR pLingLocaleName = AvoidVistaTurkishBug ? GetLingusticLocaleName((LPWSTR)lpLocaleName, dwCmpFlags) : lpLocaleName; // TODO: End of workaround for turkish CompareStringEx() on Vista/Win2K8
ã€ãŸãã æåãšèšèªã«é¢ãã.netæååã®æ¯èŒã¯ã åžžã«ãªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã®ã«ãŒãã«ã¬ãã«ã§è¡ãããŸã ã
æ§èœè©Šéš
ãã®ãããªæ ã®åŸãç§ã¯ããã©ãŒãã³ã¹ã®æ¬åœã®éãã«èå³ãæã€ããã«ãªããŸããã åœåãç§ã®ã·ããªãªã¯ãå€æ°ã®æ¯èŒã®çµæãšããŠãã·ãŒã±ã³ã¹ãçµåããããšã§ããã ã¯ãªãŒã³ãã¹ãã®ããã«ãè¿œå ã®æäœãè¡ããã«æååæ¯èŒã®ã¿ãæ®ããŸããã GitHubã§ãã¹ãã®ãœãŒã¹ã³ãŒãã衚瀺ãŸãã¯ååŸã§ããŸãã çµæã¯å°ãæµ®ããŠããŸãããã1,000,000åã®10,000åã®å埩ã§ãä¿¡é Œåºéããªããã°ååã§ãããšå€æããŸããã
ã¹ã¯ãªãã | ããªç§/ 1,000,000æäœ | çžå¯Ÿå·® |
---|---|---|
string.Equals | 25.8 | 1å |
EqualityComparer <string> .Default | 33.5 | 1.3å |
StringComparer.Ordinal | 29.8 | 1.16x |
StringComparer.OrdinalIgnoreCase | 50.3 | 1.95x |
StringComparer.OrdinalIgnoreCase ASCIIä»¥å€ | 82.2 | 3.19x |
StringComparer.CurrentCulture | 136 | 5.27x |
StringComparer.CurrentCultureéASCII | 174.3 | 6.76x |
StringComparer.CurrentCultureIgnoreCase | 134.5 | 5.21x |
StringComparer.CurrentCultureIgnoreCaseéASCII | 172.1 | 6.67x |
StringComparer.InvariantCulture | 132.2 | 5.12x |
StringComparer.InvariantCultureéASCII | 189.5 | 7.34x |
StringComparer.InvariantCultureIgnoreCase | 134.1 | 5.2å |
StringComparer.InvariantCultureIgnoreCaseéASCII | 188 | 7.29x |
ä»ã®æååæäœã®æ¯èŒ
ããã©ã«ãã§ã¯ãæéãã€æãç°¡åãªæ¯èŒã䜿çšãããããã«æãããŸãããããã°ã©ãã¯å¿é ããå¿ èŠã¯ãããŸããã å®éãããã¯å®å šã«çå®ã§ã¯ãããŸããã æååæäœã¯å€æ°ãããŸãã ããšãã°ãæååãç¹å®ã®éšåæååã§å§ãŸããã©ãããå€æããã«ã¯ ïŒ
public Boolean StartsWith(String value) { return StartsWith(value, StringComparison.CurrentCulture); }
åæã«ã éšåæåå ïŒåãããã«èŠããïŒ ã®çºçã®ç¢ºèªãããã€ãããšã«å®è¡ãããŸãã
public bool Contains( string value ) { return ( IndexOf(value, StringComparison.Ordinal) >=0 ); }
ãã®éšåæååã®å é ã®ã€ã³ããã¯ã¹ãè¿ãéšåæååã®åºçŸã®ãã§ãã¯ã¯ãçŸåšã®ã«ã«ãã£ã§ãè¡ãããŸãã
public int IndexOf(String value) { return IndexOf(value, StringComparison.CurrentCulture); }
éåžžãLastIndexOfã¯ãã€ãã£ãã³ãŒããåŒã³åºããŸãã ããã§äœãèµ·ãã£ãŠããŸããïŒ ãµãã£ã€ã ããç¥ã£ãŠããŸãã
çµè«
ããããã¹ãŠã®æ å ±ãã©ãããŸããïŒ
- ãŸããå€æ°ã®æ¯èŒã«ãããstring.Equalsã¡ãœããã«æ¢ã«ååšãããã§ãã¯ãè€è£œããªãç¬èªã®æååã³ã³ãã¬ãŒã¿ãŒãå®è£ ããããšã«ããããããã®æäœã®ããã©ãŒãã³ã¹ãçŽ10ïŒ åäžãããããšãã§ããŸãã
- 次ã«ãæååãããŒã§ããå Žåãã»ãšãã©ã®æ¯èŒã§çŽ20ïŒ ã®ã²ã€ã³ãåŸãããAsciiæåã®ã¿ã䜿çšããããšã決å®ã§ããŸãã
- 第äžã«ãæåãèæ ®ããŠæ¯èŒãé©çšãããå Žåãç解ããå¶éããå¿ èŠããããŸãã èè ã¯ãã®æ¯èŒãæãäžè¬çã§ãããšèãããããå€ãã®å Žåãã³ãŒãã§StringComparer.InvariantCultureãªãã·ã§ã³ãšæ£ç¢ºã«äžèŽããŸããã ãã ããã»ãšãã©ã®å ŽåãStringComparer.OrdinalããŸãã¯ç¹å¥ãªå Žåã«ã¯StringComparer.OrdinalIgnoreCaseã䜿çšããã ãã§ååã§ãã
- Stringã¯ã©ã¹ã®ããŸããŸãªã¡ãœããã䜿çšããŠããœãŒã¹ã§ã®åäœãå確èªããããšããå§ãããŸãã ããããããã¹ãããTestStringããšãAnotherTestStringãã§ãã¹ããããåŸãã³ãŒããå®çšŒåã«å ¥ããšé©ãã§ãããã