Почему я не могу получить элемент из 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
Ответы
Ответ 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 тот же.