Подтвердить что ты не робот

Почему я не могу получить элемент из HashSet без перечисления?

Я ищу информацию о руководителях дизайнеров HashSet. Насколько мне известно, мой вопрос относится как к Java, так и к С# HashSets, заставляя меня думать, что для этого должна быть какая-то веская причина, хотя я и не могу думать о себе.

После того, как я вставил элемент в HashSet, почему невозможно получить этот элемент без перечисления, вряд ли эффективная операция? Тем более, что HashSet явно построен таким образом, который поддерживает эффективное извлечение.

Мне было бы полезно иметь Remove (x) и Contains (x) возвращать фактический элемент, который удаляется или содержится. Это не обязательно элемент, который я передаю в функцию Remove (x) или Contains (x). Конечно, я думаю, что я мог бы добиться такого же эффекта через HashMap, но зачем тратить все это пространство и усилия, когда это вполне возможно сделать с помощью набора?

Я могу оценить, что могут быть некоторые проблемы с дизайном, что добавление этой функции позволит использовать HashSet, которые не согласуются с их ролью или будущей ролью в структуре, но если это так, то каковы эти проблемы дизайна?

Edit

Чтобы ответить на еще несколько вопросов, более подробная информация:

Я использую неизменяемый ссылочный тип с переопределенным hashcode, equals и т.д., чтобы эмулировать тип значения в С#. Пусть говорят, что тип имеет члены A, B и C. Hashcode, equals и т.д. Зависят только от A и B. Учитывая, что некоторые A и BI хотят получить этот эквивалентный элемент из hashset и получить его C. Я выиграл ' я могу использовать HashSet для этого, но мне хотелось бы узнать, есть ли для этого веские причины. Ниже следует псевдокод:

public sealed class X{
 object A;
 object B;
 object extra;

 public int HashCode(){
  return A.hashCode() + B.hashCode();
 }

 public bool Equals(X obj){
  return obj.A == A && obj.B == B;
 }
}

hashset.insert(new X(1,2, extra1));
hashset.contains(new X(1,2)); //returns true, but I can't retrieve extra
4b9b3361

Ответ 1

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

Установки, как понятие, используются для проверки включения, то есть независимо от того, находится ли этот элемент в хэш-наборе данных. Если вы хотите получить значение из источника данных с использованием значения ключа или индекса, я бы предложил изучить либо Map, либо a List.

РЕДАКТИРОВАТЬ: Дополнительный ответ на основе Редактирования исходного вопроса

Soonil, основываясь на вашей новой информации, похоже, что вам может быть интересно реализовать ваши данные как Java Enum, что-то похожее на это:

 public enum SoonilsDataType {
      A, B, C;

      // Just an example of what possible
      public static SoonilsDataType getCompositeValue(SoonilsDataType item1,
           SoonilsDataType item2) {
           if (item1.equals(A) && 
                     item2.equals(B)) {
                return C;
           }
      }
 }

Enum автоматически наследует значения(), который возвращает список всех значений в enum "set", который вы можете использовать для тестирования включения так же, как и Set. Кроме того, поскольку его полный класс, вы можете определить новые статические методы для выполнения сложной логики (например, я пытался ссылаться на код примера). Единственное, что связано с Enum, это то, что вы не можете добавлять новые экземпляры во время выполнения, что может и не быть тем, что вы хотите (хотя, если размер заданных данных не будет расти во время выполнения, Enum - это то, что вы хотите).

Ответ 2

В .Net, то, что вы, вероятно, ищете, - KeyedCollection http://msdn.microsoft.com/en-us/library/ms132438.aspx

Вы можете обойти гадость повторного внедрения этого абстрактного класса каждый раз с некоторой "общей" умностью. (См. IKeyedObject`1.)

Примечание. Любой объект передачи данных, который реализует IKeyedObject`1, должен иметь переопределенный метод GetHashCode, просто возвращающий this.Key.GetHashCode(); и то же самое для равных...

Моя базовая библиотека классов обычно заканчивается чем-то вроде этого:

public class KeyedCollection<TItem> : System.Collections.ObjectModel.KeyedCollection<TItem, TItem>
    where TItem : class
{
    public KeyedCollection() : base()
    {
    }

    public KeyedCollection(IEqualityComparer<TItem> comparer) : base(comparer)
    {
    }

    protected override TItem GetKeyForItem(TItem item)
    {
        return item;
    }
}

public class KeyedObjectCollection<TKey, TItem> : System.Collections.ObjectModel.KeyedCollection<TKey, TItem>
    where TItem : class, IKeyedObject<TKey>
    where TKey : struct
{
    public KeyedCollection() : base()
    {
    }

    protected override TItem GetKeyForItem(TItem item)
    {
        return item.Key;
    }
}

///<summary>
/// I almost always implement this explicitly so the only
/// classes that have access without some rigmarole
/// are generic collections built to be aware that an object
/// is keyed.
///</summary>
public interface IKeyedObject<TKey>
{
    TKey Key { get; }
}

Ответ 3

Если вы измените объект после его установки, его хэш может измениться (это особенно вероятно, если hashCode() был переопределен). Если хеш изменяется, поиск его в наборе не будет выполнен, так как вы будете пытаться искать объект, который хэшируется в другом месте, чем он хранится.

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

Обратите внимание, что это все для Java - я предполагаю, что у С# есть что-то похожее, но, поскольку прошло несколько лет с тех пор, как я использовал С#, я позволю другим говорить с ним о возможностях.

Ответ 4

Я предполагаю, что дизайнеры интерфейса Set и класс HashSet хотели убедиться, что метод remove(Object), определенный в интерфейсе Collection, также применим к Set; этот метод возвращает логическое значение, указывающее, был ли объект успешно удален. Если дизайнеры хотели предоставить функциональность, при которой remove (Object) вернул бы "равный" объект уже в Set, это означало бы другую сигнатуру метода.

Кроме того, учитывая, что удаляемый объект логически равен объекту, переданному для удаления (Object), он может спорить о добавленной стоимости при возврате содержащегося объекта. Тем не менее, у меня была эта проблема раньше и использовала карту для решения проблемы.

Обратите внимание, что в Java a HashSet использует HashMap внутренне, и поэтому вместо использования HashMap нет дополнительных затрат на хранение.

Ответ 5

Почему бы просто не использовать HashMap<X,X>? Это делает именно то, что вы хотите. Просто делайте .put(x,x) каждый раз, а затем вы можете просто сохранить сохраненный элемент равным x с помощью .get(x).

Ответ 6

Похоже на то, что вы действительно ищете Map<X,Y>, где Y - тип extra1.


(rant ниже)

Методы equals и hashCode определяют значимое равномерность объекта. Класс HashSet предполагает, что если два объекта равны, как определено Object.equals(Object), между этими двумя объектами нет разницы.

Я бы сказал, что если object extra имеет смысл, ваш дизайн не идеален.

Ответ 7

решаемые. Желание найти элемент кажется мне совершенно верным, потому что представитель, используемый для поиска, может отличаться от найденного элемента. Это особенно актуально, если элементы содержат информацию о ключах и значениях, а пользовательский сопоставитель сравнений сравнивает только ключевую часть. См. Пример кода. Код содержит средство сравнения, которое реализует пользовательский поиск и захватывает найденный элемент. Для этого требуется экземпляр компаратора. Очистите ссылку на найденный элемент. Выполните поиск с помощью Содержит. Доступ к найденному элементу. Помните о многопоточных проблемах при совместном использовании экземпляра сравнения.

using System;
using System.Collections.Generic;

namespace ConsoleApplication1 {

class Box
{
    public int Id;
    public string Name;
    public Box(int id, string name)
    {
        Id = id;
        Name = name;
    }
}

class BoxEq: IEqualityComparer<Box>
{
    public Box Element;

    public bool Equals(Box element, Box representative)
    {
        bool found = element.Id == representative.Id;
        if (found)
        {
            Element = element;
        }
        return found;
    }

    public int GetHashCode(Box box)
    {
        return box.Id.GetHashCode();
    }
}

class Program
{
    static void Main()
    {
        var boxEq = new BoxEq();
        var hashSet = new HashSet<Box>(boxEq);
        hashSet.Add(new Box(3, "Element 3"));
        var box5 = new Box(5, "Element 5");
        hashSet.Add(box5);
        var representative = new Box(5, "Representative 5");
        boxEq.Element = null;
        Console.WriteLine("Contains {0}: {1}", representative.Id, hashSet.Contains(representative));
        Console.WriteLine("Found id: {0}, name: {1}", boxEq.Element.Id, boxEq.Element.Name);
        Console.WriteLine("Press enter");
        Console.ReadLine();
    }
}

} // namespace

Ответ 8

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

И на самом деле, если вы делаете a (X) на множестве и ожидаете получить другой объект Y, это означает, что X и Y равны (т.е. X.equals(Y) = > true), но несколько другой, который кажется неправильным.

Ответ 9

Мне было дано интересное предложение о способе использования Карты, поскольку мои собственные объекты определяют себя как KeyValuePairs. Хотя хорошая концепция, к сожалению, KeyValuePair не является интерфейсом (почему бы и нет?) И является структурой, которая снимает этот план из эфира. В конце я откажу свой собственный Set, поскольку мои ограничения позволяют мне эту опцию.

Ответ 10

Короткий ответ; потому что предметы не могут быть неизменными.

Я столкнулся с конкретной проблемой, которую вы описываете, где HashCode основан на фиксированных полях внутри класса-члена, но класс содержит дополнительную информацию, которая может быть обновлена ​​без изменения хэша.

Моим решением было реализовать общий MyHashSet <T> на основе ICollection <T> , но обернутый вокруг словаря < int, List < T → , чтобы обеспечить требуемую эффективность поиска, где ключ int является HashCode T. Однако это показывает, что если HashCode объектов-членов может измениться, то поиск словаря, за которым следует сравнение сравнений элементов в списке, никогда не найдет измененные элементы. Нет механизма принуждения членов к неизменному, поэтому единственным решением является перечисление партии.

Ответ 11

После того, как вы задаетесь вопросом о том же, и точно сможете посмотреть исходный код:

источник: http://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs

Набор представляет собой набор уникальных элементов (объектов или значений). В реализации .net элемент совпадает с другим элементом (не уникальным), если метод Equals сравнения возвращает true для двух элементов. Нет, если два элемента имеют один и тот же хэш-код. поэтому проверка существования элемента является двухэтапным процессом. сначала используя hashset, чтобы свести к минимуму количество элементов для компиляции, а затем само сжатие.

Если вы хотите получить элемент, вы должны иметь возможность предоставить функцию получения уникального идентификатора. вы можете знать хэш-код требуемого элемента. Но этого недостаточно. поскольку более одного элемента может иметь тот же самый хеш. вам также нужно будет предоставить сам элемент, чтобы можно было вызвать метод Equal. и ясно, если у вас есть предмет, нет причин для его получения.

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

*, учитывая, что метод GetHash тот же.