【问题标题】:Most efficient way of storing objects with corresponding indices存储具有相应索引的对象的最有效方法
【发布时间】:2020-02-29 22:12:50
【问题描述】:

我想存储一个点集合,它们是Point 类的对象。 (Point 包含positionXpositionYelectricalPotential 等属性) 他们每个人都应该有一个索引i,但不需要以任何方式订购。 这就是为什么我首先使用字典Dictionary<int, Point> meshpoints

第一个问题:
如果我只想存储具有特定索引/键的对象,尤其是涉及到每个元素的添加、搜索和循环等性能问题时,字典是存储数据的最有效方式吗?

第二个问题:
如果我想添加一个新点,我如何获得“下一个”免费密钥?就像我有带有哈希键 01234 的字典,我如何获得下一个项目键的 5

选项 1: meshpoints.Keys.Max()meshpoints.Keys.Last() 在我的测试中花费了很多时间。

Dictionary<int, string> meshpoints = new Dictionary<int, string>();

meshpoints.Add(meshpoints.Keys.Max() + 1, "itemA");

meshpoints.Add(meshpoints.Keys.Max() + 1, "itemB");

选项 2: 在我的性能测试中,创建一个单独的变量 counter 非常快,但它真的是最优雅的方式吗?我的意思是,你总是有一个带有字典的单独整数值到所有方法等。...

Dictionary<int, string> meshpoints = new Dictionary<int, string>();
int counter = 0;

meshpoints.Add(counter, "itemA");
counter++;

meshpoints.Add(counter, "itemB");
counter++;

选项 3: meshpoints.Count() 不起作用,当我也随时删除项目时。

【问题讨论】:

  • 1) 是的。 2)创建一个单独的变量counter。或者,如果您不会从字典中删除项目,则使用 meshpoints.Count 来表示下一个项目。但是,如果您永远不会删除它们,只需使用 List 而不是 Dictionary 并将索引设为标识符。如果永远不会删除meshpoints[0],则meshpoints[3] 始终是同一个对象。如果您可以删除它们,请使用字典和计数器变量。
  • @EdPlunkett:除了“删除”操作之外,我们是否还应该考虑随机访问?如果您关心性能和随机访问,我认为您仍然应该选择在字典中存储点的类。
  • @vonludi List&lt;T&gt;'s index operator is a wrapper around an array indexing operation。这并不慢。你在考虑一个链表吗?
  • index 没有身份语义。如果删除了位于该项目之前的另一个项目,则该项目的索引可能会更改。也许你的意思是id 而不是index
  • 你能有多个坐标相同的点吗?

标签: c# list performance dictionary hashtable


【解决方案1】:

你不需要字典,因为你想做的事情可以用一个列表来完成。

因为您使用Count + 1 作为新 ID,您可以将自动生成的 ID 添加到 Point 类,这是一个更好的设计:

public class Point
{ 
  static private int NextID;

  int ID { get; }
  public Point()
  {
    ID = NextID++;
  }
}

List<Point> meshPoints = new List<Point>();

var meshPointA = new Point();
var meshPointB = new Point();
var meshPointC = new Point();

meshPoints.Add(meshPointA);
meshPoints.Add(meshPointB);
meshPoints.Add(meshPointC);

var meshPoint = meshPoints.Where(p => p.ID == 2).SingleOrDefault();

这样,在添加、插入和删除对象时,您将永远不会有重复的 ID 或冲突。

使用选项 1,Max 不提供速度性能。

使用选项 3,使用 Count 会导致这些问题:如果你添加 4 个点,然后删除两个,然后添加一个,最后一个的 ID 为 3,而剩下的两个 ID 为 3 和 4...所以选项 2更好。

如果您想要一个字典,您可以通过创建一个嵌入内部字典的类来做同样的事情,该类具有 NextID,并提供您想要提供的方法,例如 Add、Remove、ContainsX、ID (Items. Keys)、Points (Items.Values) 等等(所有你想要管理这个专门的集合):

public class PointsDictionary : IEnumerable<Point>
{
  private readonly Dictionary<int, Point> Items = new Dictionary<int, Point>();

  private int NextID;

  public Point this[int index]
  {
    get { return Items.ContainsKey(index) ? Items[index] : null; }
    set { ... }
  }

  public Dictionary<int, Point>.KeyCollection IDs
  {
    get { return Items.Keys; }
  }

  public Dictionary<int, Point>.ValueCollection Points
  {
    get { return Items.Values; }
  }

  public int Add(Point point)
  {
    int index = NextID++;
    Items.Add(index, point);
    return index;
  }

  ...

}

所以在这里,您将拥有一个强大而简洁的设计。

【讨论】:

    【解决方案2】:

    如果您不喜欢有两个单独的变量,那么创建一个新结构将它们绑定在一起:

    struct MeshPointContainer
    {
        private int _counter = 0;
        private List<Point> _pts = new List<Point>();
    
        public int Add(Point pt) {
            _pts.Add(pt);
            return ++counter;
        }
    }
    

    然后像这样使用它:

    var meshPoints = new MeshPointContainer();
    meshPoints.Add(new Point(-1f));
    

    如果您希望能够删除点,这将稍微复杂一些,但到目前为止您还没有表示您这样做。

    【讨论】:

    • 这也可以使用通用列表类型吗?所以我想要一个List&lt;T&gt;
    【解决方案3】:

    这个问题的变量太多,无法正确回答。如果您提到的数据必须存储

    具有一定的索引/键,

    访问者必须知道该访问键...否则为什么要使用以整数作为键的字典?

    如何获得“下一个”免费密钥?

    不保证Object.GetHashCode Method 的唯一性,应该由您自己覆盖以创建适当的散列,但可以做这样的事情

    var myDict = new Dictionary<int, PointIt>();
    
    var p1 = new PointIt() { PositionX=1, PositionY=2,  ElectricalPotential=999.99};
    var p2 = new PointIt() { PositionX=1, PositionY=2,  ElectricalPotential=999.99};
    
    myDict.Add(p1.GetHashCode(), p1);
    myDict.Add(p2.GetHashCode(), p2);
    
    myDict.ToList()
          .ForEach(itm => Console.WriteLine($"Key ({itm.Key}) Value {itm.Value}"));
    

    输出如

    Key (23832771) Value X:1 Y:2 Potential:999.99
    Key (44512918) Value X:1 Y:2 Potential:999.99
    

    public class PointIt
    {
        public int PositionX { get; set; }
        public int PositionY { get; set; }
        public double ElectricalPotential { get; set; }
    
        public override string ToString()
            => $"X:{PositionX} Y:{PositionY} Potential:{ElectricalPotential}";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 2022-07-15
      • 2015-01-29
      • 2017-07-19
      相关资源
      最近更新 更多