Редактирование словаря оценивает в цикле foreach

Определениями от ссылок до этих двух исключений выше является IllegalArgumentException: Брошенный, чтобы указать, что метод был передан недопустимый или несоответствующий аргумент. NullPointerException: Брошенный, когда приложение пытается использовать пустой указатель в случае, где объект требуется.

большой разницей здесь является IllegalArgumentException, как, предполагается, используется при проверке, что аргумент методу допустим. NullPointerException, как предполагается, используется каждый раз, когда объект, "используемый", когда это является пустым.

я надеюсь, что это помогает поместить два в перспективу.

180
задан sǝɯɐſ 23 July 2015 в 05:35
поделиться

8 ответов

Setting a value in a dictionary updates its internal "version number" - which invalidates the iterator, and any iterator associated with the keys or values collection.

I do see your point, but at the same time it would be odd if the values collection could change mid-iteration - and for simplicity there's only one version number.

The normal way of fixing this sort of thing is to either copy the collection of keys beforehand and iterate over the copy, or iterate over the original collection but maintain a collection of changes which you'll apply after you've finished iterating.

For example:

Copying keys first

List<string> keys = new List<string>(colStates.Keys);
foreach(string key in keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        colStates[key] = 0;
    }
}

Or...

Creating a list of modifications

List<string> keysToNuke = new List<string>();
foreach(string key in colStates.Keys)
{
    double percent = colStates[key] / TotalCount;    
    if (percent < 0.05)
    {
        OtherCount += colStates[key];
        keysToNuke.Add(key);
    }
}
foreach (string key in keysToNuke)
{
    colStates[key] = 0;
}
248
ответ дан 23 November 2019 в 06:13
поделиться

Как насчет того, чтобы просто выполнить несколько запросов linq к вашему словарю, а затем привязать ваш график к их результатам? ...

var under = colStates.Where(c => (decimal)c.Value / (decimal)totalCount < .05M);
var over = colStates.Where(c => (decimal)c.Value / (decimal)totalCount >= .05M);
var newColStates = over.Union(new Dictionary<string, int>() { { "Other", under.Sum(c => c.Value) } });

foreach (var item in newColStates)
{
    Console.WriteLine("{0}:{1}", item.Key, item.Value);
}
3
ответ дан 23 November 2019 в 06:13
поделиться

Если вы Чувствуете себя творчески, вы могли бы сделать что-то подобное. Прокрутите словарь в обратном направлении, чтобы внести изменения.

Dictionary<string, int> collection = new Dictionary<string, int>();
collection.Add("value1", 9);
collection.Add("value2", 7);
collection.Add("value3", 5);
collection.Add("value4", 3);
collection.Add("value5", 1);

for (int i = collection.Keys.Count; i-- > 0; ) {
    if (collection.Values.ElementAt(i) < 5) {
        collection.Remove(collection.Keys.ElementAt(i)); ;
    }

}

Конечно, не идентичны, но в любом случае вам может быть интересно ...

3
ответ дан 23 November 2019 в 06:13
поделиться
​​

Вам необходимо создать новый Словарь из старого, а не изменять его на месте. Что-то вроде (также перебирайте KeyValuePair <,> вместо использования поиска по ключу:

int otherCount = 0;
int totalCounts = colStates.Values.Sum();
var newDict = new Dictionary<string,int>();
foreach (var kv in colStates) {
  if (kv.Value/(double)totalCounts < 0.05) {
    otherCount += kv.Value;
  } else {
    newDict.Add(kv.Key, kv.Value);
  }
}
if (otherCount > 0) {
  newDict.Add("Other", otherCount);
}

colStates = newDict;
2
ответ дан 23 November 2019 в 06:13
поделиться

You are modifying the collection in this line:

colStates[key] = 0;

By doing so, you are essentially deleting and reinserting something at that point (as far as IEnumerable is concerned anyways.

If you edit a member of the value you are storing, that would be OK, but you are editing the value itself and IEnumberable doesn't like that.

The solution I've used is to eliminate the foreach loop and just use a for loop. Простой цикл for не будет проверять изменения, которые, как вы знаете, не повлияют на коллекцию.

Вот как это можно сделать:

List<string> keys = new List<string>(colStates.Keys);
for(int i = 0; i < keys.Count; i++)
{
    string key = keys[i];
    double  Percent = colStates[key] / TotalCount;
    if (Percent < 0.05)    
    {        
        OtherCount += colStates[key];
        colStates[key] = 0;    
    }
}
19
ответ дан 23 November 2019 в 06:13
поделиться

Вы не можете изменять ключи или значения непосредственно в ForEach, но вы можете изменять их члены. Например, это должно работать:

public class State {
    public int Value;
}

...

Dictionary<string, State> colStates = new Dictionary<string,State>();

int OtherCount = 0;
foreach(string key in colStates.Keys)
{
    double  Percent = colStates[key].Value / TotalCount;

    if (Percent < 0.05)
    {
        OtherCount += colStates[key].Value;
        colStates[key].Value = 0;
    }
}

colStates.Add("Other", new State { Value =  OtherCount } );
6
ответ дан 23 November 2019 в 06:13
поделиться

Отказ от ответственности: я не очень много делаю C #

Вы пытаетесь изменить DictionaryEntry объект, который хранится в HashTable. Hashtable хранит только один объект - ваш экземпляр DictionaryEntry. Изменения ключа или значения достаточно, чтобы изменить HashTable и сделать перечислитель недействительным.

Вы можете сделать это вне цикл:

if(hashtable.Contains(key))
{
    hashtable[key] = value;
}

путем создания сначала списка всех ключей значений, которые вы хотите изменить, и вместо этого перебирать этот список.

0
ответ дан 23 November 2019 в 06:13
поделиться

You can't modify the collection, not even the values. You could save these cases and remove them later. It would end up like this:

        Dictionary<string, int> colStates = new Dictionary<string, int>();
        // ...
        // Some code to populate colStates dictionary
        // ...

        int OtherCount = 0;
        List<string> notRelevantKeys = new List<string>();

        foreach (string key in colStates.Keys)
        {

            double Percent = colStates[key] / colStates.Count;

            if (Percent < 0.05)
            {
                OtherCount += colStates[key];
                notRelevantKeys.Add(key);
            }
        }

        foreach (string key in notRelevantKeys)
        {
            colStates[key] = 0;
        }

        colStates.Add("Other", OtherCount);
1
ответ дан 23 November 2019 в 06:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: