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

Я хочу использовать List в качестве очереди. Это связано с тем, что List более гибок при получении/вставке данных.

Мой вопрос: когда я использую операции Linq (DistinctBy, Take), как показано ниже, получаю ли я элементы в том порядке, в котором они были добавлены в список?

private List<IMessage> _queue = new List<IMessage>();

// add many
 _queue.Add(tagMessage);


 toConsume = _queue
     .Where(t => !_snLockList.Contains(t.Id))
     .DistinctBy(t => t.Id)
     .Take(freeTasks).ToList();

Имеет ли название вопроса какое-либо отношение к тому, что задается внутри? По сути, вы спрашиваете о поведении операторов LINQ Where, DistinctBy, Take и ToList, сохраняют ли они порядок последовательности source или нет. Не имеет значения, является ли source списком, очередью или какой-либо другой коллекцией.

Theodor Zoulias 27.05.2024 08:20

Если вы добавляете элементы только в конец списка (используя .Add() или .AddRange()), добавленные элементы (за исключением тех, которые были позже удалены) останутся в том порядке, в котором они были добавлены. Если вы вставляете какие-либо элементы по определенному индексу, используя .Insert() или .InsertRange(), они останется в том же положении относительно элементов до и после. Имейте в виду, что List<> реализован с использованием массива под обложками, поэтому операции вставки и удаления, отличные от конца списка, могут быть дорогостоящими, поскольку они включают в себя перемещение элементов. в массиве.

T N 27.05.2024 08:22

Без order by или skip LINQ будет использовать отдельные элементы, которых нет в списке _snLockList. Я не понимаю, почему бы вам просто не использовать Console/Debug.Writeline() и не протестировать несколько сценариев?

Jeremy Thompson 27.05.2024 08:23

Я выполнил много многопоточных операций, и список работает как очередь. Просто хочу посмотреть, будет ли это поведение нарушено при стрессе или каких-либо особых случаях при использовании операторов Where, DistinctBy, Take.

Patola 27.05.2024 08:30

@mamen да, очень интересно, но по DistinctBy нет однозначного вывода. Я согласен с порядком «найден первым», но не с непредсказуемым порядком.

Patola 27.05.2024 08:44

Список не является потокобезопасным (!) - он не требует стресса или особых случаев, чтобы ваша реализация испарилась. Прежде чем беспокоиться о конкретных операциях LINQ, ознакомьтесь с потокобезопасными реализациями IList.

Sancho Panza 27.05.2024 08:49

LINQ рассматривает все как IEnumerable<T> результаты будут зависеть от реализации соответствующего перечислителя. На практике это почти всегда приводит к выводу элементов в том же порядке, в котором они вводятся, если операция специально не требует переупорядочения. Тем не менее, если это специально не задокументировано, это всего лишь результат удобства, а не необходимости, и нет никакой гарантии, что это не изменится в будущем. Опять же, однако, это вряд ли когда-либо изменится на практике. Тем не менее, если вам конкретно нужен заказ, вам следует реализовать его самостоятельно.

jmcilhinney 27.05.2024 08:52

Обратите внимание, что многие методы LINQ имеют перегрузки, которые принимают элемент и его индекс в качестве аргументов. Вы можете начать с вызова Select и создания анонимных объектов, включающих элемент и его индекс, а затем закончить OrderBy, который упорядочивает по индексу, а затем Select, который извлекает элемент.

jmcilhinney 27.05.2024 08:54

На самом деле, последнее предложение, вероятно, не сработает. Индексы, предоставляемые LINQ, вероятно, просто следуют порядку перечисления, независимо от того, соответствует ли он порядку исходного списка или нет. Если все IEnumerable<T>, нет никакой гарантии, что существует какой-либо способ получить исходный индекс или даже если он есть, например. в Dictionary. Вы могли бы проверить5 это, создав собственный тип коллекции, который намеренно перечисляется не по порядку, но я не думаю, что буду тратить на это время.

jmcilhinney 27.05.2024 09:30

@Patola This is because List is more flexible when getting/inserting data. угадай, что List и Queue используют для хранения: T[]. У них одинаковые ограничения на потоки. ConcurrentQueue и ConcurrentStack потокобезопасны и используют очень мало (если вообще используют) блокировок. Нет ConcurrentList

Panagiotis Kanavos 27.05.2024 13:12

Кажется, это можно очень легко попробовать? У вас уже есть код и, предположительно, есть входные данные. Почему вы не запустили этот код самостоятельно, чтобы проверить, будет ли ответ «да» или «нет»?

TylerH 27.05.2024 16:42

Имя toConsume указывает на то, что вы, возможно, захотите удалить некоторые элементы из списка, а не просто выбрать некоторые элементы. По крайней мере, именно так обычно работают системы производитель-потребитель. Если вы хотите эффективно удалить несколько элементов из списка и контролировать порядок удаления элементов, вам может показаться этот вопрос интересным (отправная точка для пользовательской RemoveMany реализации).

Theodor Zoulias 28.05.2024 01:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
12
114
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ответ: «Да, но…».

Да, все операции LINQ, которые перебирают коллекции с использованием шаблона «foreach, yield return», возвращают результаты в порядке IEnumerable базового объекта (ваших List<T>) (см. MS Doc - выход, также перейдите по ссылке в этой статье, чтобы iterators) - если/пока другая операция не изменит порядок (Sort()).

Но вы не даете достаточного понимания того, «что, как и почему» того, что вы делаете... (1) Списки можно использовать для реализации довольно эффективных (кольцевых) очередей («кольцевых буферов» - для которых вы, вероятно, будете придется кодировать итератор/ы). (2) Если вы собираетесь вставлять и удалять из середины очереди, то вы должны использовать (O(n), т.е. довольно дорогую) логику перемешивания вверх и вниз (за кулисами Insert(), RemoveAt()... и ваша очередь не является FIFO, то есть на самом деле это не очередь).

Вы рассматривали возможность использования PriorityQueue<T> или LinkedList<T>?

Я использую _queue.Add(tagMessage);, _queue.Insert(index, tagMessage); а затем описанная выше операция linq для выбора нескольких элементов, а затем я удаляю их _queue.RemoveAll(t => toConsume.Any(x => x.QueueId == t.QueueId));. Это операции, которые я использую. Я хочу, чтобы список сохранял порядок индекса. Это не строгая очередь. Все операции выполняются под блокировкой из-за многопоточного доступа.

Patola 28.05.2024 02:19

Я вижу, что вопрос закрыт (дублирование в аспекте упорядочивания запросов LINQ). Если очередь может стать большой и/или важна производительность (и/или вы, как и я, ОКР в отношении дизайна ;-), то, судя по вашему описанию, я бы серьезно рассмотрел LinkedList<T> с политикой «добавляй в хвосте и сначала потребляй, доступный из голова/перед». Преимущество заключается в том, что операции вставки и удаления O(1) требуют (вероятно, гораздо меньших) затрат на некоторую фрагментацию памяти (управление памятью в .NET превосходно). (Возможно, также придется отказаться от некоторой аккуратности и краткости LINQ.)

AlanK 28.05.2024 08:44

Другие вопросы по теме