Я написал библиотеку, которая работает с большим Excel и отображает записи в объекты. Но в какой-то момент он становится очень медленным из-за трех вложенных циклов foreach. Я видел некоторые решения со словарем, но проблема была немного другой, чем моя.
var house = new PostHouse();
house.FK_STRID = long.Parse(fields[2]);
if (!fields[3].Equals("")){
house.HouseNumber = long.Parse(fields[3]);
}
foreach (var canton in cantons)
{
foreach(var city in canton.Cities)
{
if (city.Streets == null)
city.Streets = new List<PostStreet>();
foreach(var street in city.Streets)
{
if (street.STRID == house.FK_STRID)
{
if (street.Houses == null)
street.Houses = new List<PostHouse>();
street.Houses.Add(house);
}
}
}
}
Чтобы перебрать все в цикле, требуется около 10 минут.
Любая помощь?
заранее спасибо
Я так понимаю, что проблема не в цикле foreach. Проблема заключается в чтении из файла xls. Вы можете прочитать это как файл csv?
По крайней мере, вы можете извлечь house.FK_STRID в локальную переменную вне циклов. Наверное, это не сильно поможет, но это стоит сделать.
Чтобы проделать эти последние 10 минут, понадобится вся Швейцария. Нет простых уловок для слишком большого количества данных. На самом деле вы можете сделать только одно: сделать это один раз и поместить в хорошо проиндексированную базу данных. Если вы не можете сделать это только один раз, сосредоточьтесь на чтении только измененных данных. Если и этого не получается, убедитесь, что этого никто не ждет. Все спят в 3 часа ночи :)
Откуда взялся cantons?
Вы использовали профилировщик? Где именно узкое место?
Попробуй распараллелить код.





Трудно сказать, потому что мы не знаем, что это за тип cantons и его дочерние элементы и откуда они берутся, но основная проблема здесь в том, что у вас есть структура данных, которая не подходит для этой задачи. У вас есть иерархический список улиц внутри городов внутри кантонов, тогда как вам нужен список улиц проиндексирован по их STRID.
Если вы можете выполнить первоначальную обработку того, как хранятся ваши улицы, вы можете сделать это:
var streetIndex = new Dictionary<string, PostStreet>();
foreach (var canton in cantons)
foreach (var city in canton.Cities)
foreach (var street in city.Streets)
{
streetIndex.Add(street.STRID, street);
}
тогда вы можете мгновенно искать улицу по ее STRID, когда вы перебираете дома за O (1) раз.
foreach (var house in houses)
{
if (streetIndex.ContainsKey(house.FK_STRID))
{
streetIndex[house.FK_STRID].Add(house);
}
}
К сожалению, в настоящее время это невозможно, потому что я читаю одну строку файла, а затем собираюсь добавить ее в объект. Итак, предварительная обработка здесь не работает ..
@ DominicJärmann Если вы читаете файл по одной строке за раз, то, скорее всего, проблема в скорости, читайте его по частям (или, если он маленький, все сразу)
Сделайте это в своих конструкторах или автоматически инициализируйте их
Streets = new List<PostStreet>();
...
Houses = new List<PostHouse>()
Тогда Linq
var streets = cantons.SelectMany(x => x.Cities)
.SelectMany(x => x.Streets)
.Where(x => x.STRID == house.FK_STRI);
foreach (var street in streets)
street.Houses.Add(house);
выполнение цикла: цикл For - лучший вариант, если вы не можете использовать цикл For, вы можете использовать foreach с локальной переменной. (var cantons = кантоны и foreach (var canton в кантонах)