2008-08-07 23 views
134

Tôi đang sử dụng Dictionary<string, int> trong đó int là số của khóa.Truy cập Khóa từ điển.Keys thông qua chỉ số bằng số

Bây giờ, tôi cần truy cập Khóa được chèn lần cuối trong Từ điển, nhưng tôi không biết tên của nó. Nỗ lực rõ ràng:

int LastCount = mydict[mydict.keys[mydict.keys.Count]]; 

không hoạt động, vì Dictionary.Keys không thực hiện [] -indexer.

Tôi tự hỏi liệu có bất kỳ lớp học tương tự nào không? Tôi đã nghĩ đến việc sử dụng Stack, nhưng chỉ lưu trữ một chuỗi. Bây giờ tôi có thể tạo cấu trúc của riêng tôi và sau đó sử dụng một Stack<MyStruct>, nhưng tôi tự hỏi nếu có một thay thế, về cơ bản một từ điển thực hiện một [] -indexer trên các phím?

+1

Điều gì xảy ra nếu bạn đặt biến đó? –

Trả lời

203

Như @Falanwe chỉ ra trong một chú thích, làm một cái gì đó như thế này là không chính xác :

int LastCount = mydict.Keys.ElementAt(mydict.Count -1); 

Bạn không nên phụ thuộc vào thứ tự của các phím trong một từ điển. Nếu bạn cần đặt hàng, bạn nên sử dụng một số OrderedDictionary, như được đề xuất trong số answer này. Các câu trả lời khác trên trang này cũng thú vị.

+1

dường như không hoạt động với 'HashTable' System.Collections.ICollection 'không chứa định nghĩa cho' ElementAt 'và không có phương thức mở rộng' ElementAt 'chấp nhận đối số đầu tiên của loại' System.Collections.ICollection 'có thể được tìm thấy –

+0

Bạn có thể sử dụng phiên bản 'ElementAtOrDefault' để làm việc với phiên bản không có ngoại lệ. –

+19

Thật đáng sợ khi thấy một câu trả lời sai lầm trắng trợn như vậy được chấp nhận và bỏ phiếu nhiều. Đó là sai vì, vì tài liệu ['Dictionary '] (https://msdn.microsoft.com/en-us/library/yt2fy5zk (v = vs.110) .aspx) nói "Thứ tự của các khóa trong 'Từ điển .KeyCollection' không được chỉ định." Thứ tự không xác định, bạn không có cách nào để biết chắc chắn là ở vị trí cuối cùng ('mydict.Count -1') – Falanwe

6

Bạn luôn có thể làm điều này:

string[] temp = new string[mydict.count]; 
mydict.Keys.CopyTo(temp, 0) 
int LastCount = mydict[temp[mydict.count - 1]] 

Nhưng tôi sẽ không khuyên bạn nên nó. Không có gì đảm bảo rằng khóa được chèn cuối cùng sẽ nằm ở cuối mảng. Việc đặt hàng cho các Phím on MSDN không được chỉ định và có thể thay đổi. Trong bài kiểm tra rất ngắn gọn của tôi, nó dường như theo thứ tự chèn, nhưng bạn nên xây dựng trong sổ sách kế toán thích hợp như một chồng - như bạn đề xuất (mặc dù tôi không thấy sự cần thiết của một cấu trúc dựa trên của bạn các câu lệnh khác) - hoặc bộ nhớ cache biến duy nhất nếu bạn chỉ cần biết khóa mới nhất.

2

Tôi không biết điều này có hiệu quả không vì tôi chắc chắn rằng các khóa không được lưu trữ theo thứ tự chúng được thêm vào, nhưng bạn có thể đưa KeysCollection vào Danh sách rồi lấy khóa cuối cùng trong danh sách ... nhưng nó sẽ có giá trị có một cái nhìn.

Điều duy nhất tôi có thể nghĩ là lưu trữ các khóa trong danh sách tra cứu và thêm các phím vào danh sách trước khi bạn thêm chúng vào từ điển ... nó không đẹp.

+0

Tôi đã không kiểm tra mã, nhưng phương pháp được ghi trên [MSDN] [1] có thể là một phiên bản khác của khung công tác? [1]: http://msdn.microsoft.com/en-us/library/bb908406.aspx – Juan

+0

@Juan: không có phương pháp .Last() trên KeyCollection – lomaxx

+0

2 năm cuối nhưng nó có thể giúp ai đó ... xem trả lời của tôi cho bài đăng của Juan bên dưới. Last() là một phương thức mở rộng. – SuperOli

5

Tôi nghĩ rằng bạn có thể làm một cái gì đó như thế này, cú pháp có thể sai, havent sử dụng C# trong một thời gian Để có được mục cuối cùng

Dictionary<string, int>.KeyCollection keys = mydict.keys; 
string lastKey = keys.Last(); 

hoặc sử dụng Max thay vì cuối để lấy giá trị max, Tôi không biết cái nào phù hợp với mã của bạn tốt hơn.

+2

Tôi sẽ thêm rằng kể từ "Last()" là một phương pháp mở rộng, bạn sẽ cần .NET Framework 3.5 và thêm "using System.Linq" ở đầu tệp .cs của bạn. – SuperOli

+0

Hãy thử điều này cho lần cuối (khi sử dụng một Dist rõ ràng :-) KeyValuePair last = oAuthPairs.Last(); nếu (kvp.Key! = Last.Key) { _oauth_ParamString = _oauth_ParamString + "&"; } –

4

Tôi đồng ý với phần thứ hai của câu trả lời của Patrick. Ngay cả khi trong một số thử nghiệm có vẻ như giữ trật tự chèn, tài liệu (và hành vi bình thường cho từ điển và băm) nêu rõ thứ tự không được chỉ định.

Bạn chỉ đang gặp sự cố tùy thuộc vào thứ tự của các phím. Thêm sổ sách kế toán của riêng bạn (như Patrick đã nói, chỉ một biến duy nhất cho khóa được thêm cuối cùng) để chắc chắn. Ngoài ra, không bị cám dỗ bởi tất cả các phương pháp như Last và Max trên từ điển vì chúng có thể liên quan đến bộ so sánh chính (tôi không chắc chắn về điều đó).

3

Cách bạn diễn đạt câu hỏi khiến tôi tin rằng int trong Từ điển có chứa "vị trí" của mục trên Từ điển. Đánh giá từ việc xác nhận rằng các khóa không được lưu trữ theo thứ tự mà chúng được thêm vào, nếu điều này là chính xác, điều đó có nghĩa là keys.Count (hoặc .Count - 1, nếu bạn đang sử dụng không dựa trên) nên vẫn luôn là số của khóa được nhập cuối cùng?

Nếu đúng, có lý do nào bạn không thể sử dụng từ điển < int, chuỗi > để bạn có thể sử dụng mydict [mydict.Keys.Count]?

8

Tại sao bạn không chỉ mở rộng lớp từ điển để thêm vào thuộc tính được chèn khóa cuối cùng. Một cái gì đó như sau có thể?

public class ExtendedDictionary : Dictionary<string, int> 
{ 
    private int lastKeyInserted = -1; 

    public int LastKeyInserted 
    { 
     get { return lastKeyInserted; } 
     set { lastKeyInserted = value; } 
    } 

    public void AddNew(string s, int i) 
    { 
     lastKeyInserted = i; 

     base.Add(s, i); 
    } 
} 
+2

Bạn đang đặt lastKeyInserted cho giá trị cuối cùng được chèn vào. Hoặc bạn có nghĩa là để đặt nó vào khóa cuối cùng được chèn vào hoặc bạn cần tên tốt hơn cho biến và thuộc tính. – Fantius

+0

Eh? Không, tôi không (?) – Calanus

56

Bạn có thể sử dụng OrderedDictionary.

Trình bày tập hợp khóa/giá trị các cặp có thể truy cập bằng khóa hoặc chỉ mục.

+39

Erhm, sau 19 lần bỏ phiếu, không ai đề cập rằng OrderedDictionary vẫn không cho phép lấy khóa theo chỉ mục? – Lazlo

+0

Bạn có thể truy cập một giá trị có chỉ số nguyên bằng ** OrderedDictionary **, nhưng không phải với ** System.Collections.Generic.SortedDictionary ** nơi chỉ mục cần là TKey – Maxence

16

Từ điển là bảng băm, vì vậy bạn không có ý tưởng thứ tự chèn!

Nếu bạn muốn biết khóa được chèn cuối cùng, tôi khuyên bạn nên mở rộng từ điển để bao gồm giá trị LastKeyInserted.

ví dụ .:

public MyDictionary<K, T> : IDictionary<K, T> 
{ 
    private IDictionary<K, T> _InnerDictionary; 

    public K LastInsertedKey { get; set; } 

    public MyDictionary() 
    { 
     _InnerDictionary = new Dictionary<K, T>(); 
    } 

    #region Implementation of IDictionary 

    public void Add(KeyValuePair<K, T> item) 
    { 
     _InnerDictionary.Add(item); 
     LastInsertedKey = item.Key; 

    } 

    public void Add(K key, T value) 
    { 
     _InnerDictionary.Add(key, value); 
     LastInsertedKey = key; 
    } 

    .... rest of IDictionary methods 

    #endregion 

} 

Bạn sẽ chạy vào các vấn đề tuy nhiên khi bạn sử dụng .Remove() như vậy để khắc phục điều này bạn sẽ phải giữ một danh sách có thứ tự các phím chèn vào.

3

Trong trường hợp bạn quyết định sử dụng mã nguy hiểm có thể bị vỡ, chức năng mở rộng này sẽ lấy khóa từ số Dictionary<K,V> theo chỉ mục nội bộ của nó (cho Mono và .NET hiện tại có cùng thứ tự với bạn bằng cách liệt kê tài sản Keys).

Bạn nên sử dụng LINQ: dict.Keys.ElementAt(i), nhưng chức năng đó sẽ lặp lại O (N); sau đây là O (1) nhưng với một hình phạt hiệu suất phản ánh.

using System; 
using System.Collections.Generic; 
using System.Reflection; 

public static class Extensions 
{ 
    public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx) 
    { 
     Type type = typeof(Dictionary<TKey, TValue>); 
     FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance); 
     if (info != null) 
     { 
      // .NET 
      Object element = ((Array)info.GetValue(dict)).GetValue(idx); 
      return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element); 
     } 
     // Mono: 
     info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance); 
     return (TKey)((Array)info.GetValue(dict)).GetValue(idx); 
    } 
}; 
+0

Hmm, chỉnh sửa để cải thiện câu trả lời đã đạt được một lợi nhuận.Tôi không nói rõ ràng rằng đoạn mã (rõ ràng) ghê tởm, và nên được xem xét tương ứng? –

4

Một giải pháp thay thế sẽ là KeyedCollection nếu khóa được nhúng vào giá trị.

Chỉ cần tạo triển khai cơ bản trong lớp được niêm phong để sử dụng.

Vì vậy, để thay thế Dictionary<string, int> (đây không phải là một ví dụ rất hay vì không có khóa rõ ràng cho int).

private sealed class IntDictionary : KeyedCollection<string, int> 
{ 
    protected override string GetKeyForItem(int item) 
    { 
     // The example works better when the value contains the key. It falls down a bit for a dictionary of ints. 
     return item.ToString(); 
    } 
} 

KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary(); 

intCollection.Add(7); 

int valueByIndex = intCollection[0]; 
+0

Về ý kiến ​​của bạn trên phím, xem câu trả lời theo dõi của tôi cho điều này. – takrl

2

Mở rộng trên Daniels bài và ý kiến ​​của ông về chìa khóa, từ khóa được nhúng vào trong các giá trị dù sao, bạn có thể nghỉ mát để sử dụng một KeyValuePair<TKey, TValue> như giá trị. Lý do chính cho điều này là, nói chung, khóa không nhất thiết phải trực tiếp phái sinh từ giá trị.

Sau đó, nó sẽ giống như thế này:

public sealed class CustomDictionary<TKey, TValue> 
    : KeyedCollection<TKey, KeyValuePair<TKey, TValue>> 
{ 
    protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item) 
    { 
    return item.Key; 
    } 
} 

Để sử dụng điều này như trong ví dụ trước, bạn sẽ làm:

CustomDictionary<string, int> custDict = new CustomDictionary<string, int>(); 

custDict.Add(new KeyValuePair<string, int>("key", 7)); 

int valueByIndex = custDict[0].Value; 
int valueByKey = custDict["key"].Value; 
string keyByIndex = custDict[0].Key; 
2

Bạn cũng có thể sử dụng SortedList và người đồng cấp Generic của nó. Hai lớp này và trong Andrew Peters câu trả lời được đề cập OrderedDictionary là các lớp từ điển trong đó các mục có thể được truy cập bằng chỉ mục (vị trí) cũng như bằng khóa. Cách sử dụng các lớp học này bạn có thể tìm thấy: SortedList Class, SortedList Generic Class.

2

Một từ điển có thể không rất trực quan cho việc sử dụng chỉ số để tham khảo nhưng, bạn có thể có các hoạt động tương tự với một mảng của KeyValuePair:

cũ. KeyValuePair<string, string>[] filters;

1

Visual Studio's UserVoice cung cấp liên kết tới generic OrderedDictionary implementation bởi dotmore.

Nhưng nếu bạn chỉ cần nhận cặp khóa/giá trị theo chỉ mục và không cần lấy giá trị theo khóa, bạn có thể sử dụng một mẹo đơn giản. Khai báo một số lớp chung (tôi gọi nó ListArray) như sau:

class ListArray<T> : List<T[]> { } 

Bạn cũng có thể tuyên bố nó với nhà thầu:

class ListArray<T> : List<T[]> 
{ 
    public ListArray() : base() { } 
    public ListArray(int capacity) : base(capacity) { } 
} 

Ví dụ, bạn đọc một số cặp khóa/giá trị từ một tập tin và chỉ muốn lưu trữ chúng theo thứ tự chúng được đọc như vậy để có được chúng sau này bởi chỉ số:

ListArray<string> settingsRead = new ListArray<string>(); 
using (var sr = new StreamReader(myFile)) 
{ 
    string line; 
    while ((line = sr.ReadLine()) != null) 
    { 
     string[] keyValueStrings = line.Split(separator); 
     for (int i = 0; i < keyValueStrings.Length; i++) 
      keyValueStrings[i] = keyValueStrings[i].Trim(); 
     settingsRead.Add(keyValueStrings); 
    } 
} 
// Later you get your key/value strings simply by index 
string[] myKeyValueStrings = settingsRead[index]; 

Như bạn có thể nhận thấy, bạn có thể có không nhất thiết chỉ là cặp khóa/giá trị trong ListArray của bạn. Các mảng vật phẩm có thể có độ dài bất kỳ, giống như trong mảng răng cưa.

Các vấn đề liên quan