/// <summary>
        /// Adds <paramref name="element"/> under the specified <paramref name="key"/>. <paramref name="key"/> does not need to exist.
        /// </summary>
        /// <param name="key">The key to add <paramref name="element"/> under.</param>
        /// <param name="element">The element to add.</param>
        public void Add(TKey key, TElement element)
        {
            ObservableGrouping <TKey, TElement> grouping;

            if (key == null)
            {
                grouping = nullGrouping;
                if (grouping.Count == 0)
                {
                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
                }
            }
            else if (!this.groupings.TryGetValue(key, out grouping))
            {
                if (!ReuseGroups || !this.oldGroups.TryRemove(key, out grouping))
                {
                    grouping = new ObservableGrouping <TKey, TElement> (key);
                }

                this.groupings.Add(key, grouping);
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count - 1));
            }

            grouping.Add(element);
        }
        public void Add(TKey key, IEnumerable <TElement> elements)
        {
            if (elements == null)
            {
                throw new ArgumentNullException(nameof(elements));
            }

            ObservableGrouping <TKey, TElement> grouping;

            if (key == null)
            {
                bool wasEmpty = this.nullGrouping.Count == 0;
                grouping = nullGrouping;
                grouping.AddRange(elements);
                if (wasEmpty && this.nullGrouping.Count > 0)
                {
                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
                }
            }
            else if (!this.groupings.TryGetValue(key, out grouping))
            {
                if (!ReuseGroups || !this.oldGroups.TryRemove(key, out grouping))
                {
                    grouping = new ObservableGrouping <TKey, TElement> (key);
                }

                grouping.AddRange(elements);
                if (grouping.Count == 0)
                {
                    if (ReuseGroups)
                    {
                        this.oldGroups.Add(grouping.Key, grouping);
                    }

                    return;
                }

                this.groupings.Add(key, grouping);
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count - 1));
            }
        }
        public void Add(IGrouping <TKey, TElement> grouping)
        {
            if (grouping == null)
            {
                throw new ArgumentNullException(nameof(grouping));
            }

            if (grouping.Key == null)
            {
                bool wasEmpty = this.nullGrouping.Count == 0;
                this.nullGrouping.AddRange(grouping);
                if (wasEmpty && this.nullGrouping.Count > 0)
                {
                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)grouping, this.groupings.Count));
                }

                return;
            }

            ObservableGrouping <TKey, TElement> og;

            if (!ReuseGroups || !this.oldGroups.TryRemove(grouping.Key, out og))
            {
                og = new ObservableGrouping <TKey, TElement> (grouping.Key);
            }

            og.AddRange(grouping);
            if (og.Count == 0)
            {
                if (ReuseGroups)
                {
                    this.oldGroups.Add(grouping.Key, og);
                }

                return;
            }

            this.groupings.Add(grouping.Key, og);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)og, this.groupings.Count - 1));
        }
        public void Insert(int index, IGrouping <TKey, TElement> grouping)
        {
            ObservableGrouping <TKey, TElement> og;

            if (!ReuseGroups || !this.oldGroups.TryRemove(grouping.Key, out og))
            {
                og = new ObservableGrouping <TKey, TElement> (grouping.Key);
            }

            og.AddRange(grouping);
            if (og.Count == 0)
            {
                if (ReuseGroups)
                {
                    this.oldGroups.Add(grouping.Key, og);
                }

                return;
            }

            this.groupings.Insert(index, og.Key, og);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, (object)og, index));
        }