Skip to content

[API Proposal]: New method HashSet<T>.AddOrUpdate that replaces an existing element if found #101003

@theodorzoulias

Description

@theodorzoulias

Background and motivation

When adding an existing key to a Dictionary<TKey,TValue> collection, there is an option to either preserve or replace the existing value.

dictionary.TryAdd(key, value); // Preserve
dictionary[key] = value; // Replace

There is no such option for the HashSet<T> collection. There is only an Add method with preserving behavior.
So this proposal is to introduce a new AddOrUpdate method with replacing behavior:

API Proposal

namespace System.Collections.Generic;

public class HashSet<T>
{
    /// <summary>Adds the specified element to a set, replacing an existing equal element if found.</summary>
    /// <param name="item">The element to add to the set.</param>
    public void AddOrUpdate(T item);
}

API Usage

Recently I wrote a custom ICollection<T> collection intended for JSON serialization, that has the functionality of the KeyedCollection<TKey,TItem>, but instead of a List+Dictionary combo is backed by a HashSet<T>. Practically it is a dictionary that is serialized in JSON as a list. I wanted to replicate the existing deserialization behavior of the Dictionary<TKey,TValue> collection regarding duplicate keys, which is to preserve the last key found in the JSON document and silently discard the earlier duplicates. So I implemented the ICollection<TItem>.Add method like this:

abstract class JsonKeyedCollectionBase<TKey, TItem> : ICollection<TItem>
{
    private readonly HashSet<TItem> _set;

    /* ... */

    void ICollection<TItem>.Add(TItem item)
    {
        if (_set.Add(item)) return;
        bool removed = _set.Remove(item);
        bool added = _set.Add(item);
        Debug.Assert(removed);
        Debug.Assert(added);
    }
}

The whole implementation can be found here. If the proposed API was available, I could make the above implementation simpler and (presumably) more efficient like this:

    void ICollection<TItem>.Add(TItem item) => _set.AddOrUpdate(item);

Alternative Designs

No response

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions