【问题标题】:Is c# List's array being shifted when i extract an element?当我提取元素时,c#List 的数组是否被移动?
【发布时间】:2019-04-05 14:48:54
【问题描述】:

我为 Unity 中的任何游戏对象实现了一个通用池系统。 当我从 List 中删除一个 GameObject 时,我总是取最后一个,以避免在 List 的实现中使用的数组发生移位。在互联网上没有找到任何有用的东西,所以试图自己弄清楚。 那有用还是我只是在做无用的代码?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolSystem : MonoBehaviour
{

    public static Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();

    #region cached
    private static GameObject lastReturned;
    #endregion

    public static GameObject GetElementFromPool(GameObject g)
    {
        if (!pool.ContainsKey(g.name))
        {
            lastReturned = Instantiate(g) as GameObject;
            pool.Add(g.name, new List<GameObject>());
            lastReturned.name = g.name;
            return lastReturned;
        }


        if (pool[g.name].Count == 0)
        {
            lastReturned = Instantiate(g) as GameObject;
            lastReturned.name = g.name;
        }
        else
        {
            lastReturned = pool[g.name][pool[g.name].Count - 1];
            pool[g.name].RemoveAt(pool[g.name].Count - 1);
        }

        return lastReturned;
    }

    public static void AddToPool(GameObject g)
    {

        if (!pool.ContainsKey(g.name))
        {
            pool.Add(g.name, new List<GameObject>());
        }
        pool[g.name].Add(g);

    }

}

我在答案和cmets中添加了所有建议,感谢大家的帮助。在下面添加更改的代码,也许有人会发现它有用

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolSystem : MonoBehaviour
{
    public static Dictionary<string, Stack<GameObject>> pool = new Dictionary<string, Stack<GameObject>>();

    #region cached
    private static GameObject lastReturned;
    private static Stack<GameObject> lastUsedStack;
    #endregion

    public static GameObject GetElementFromPool(GameObject g)
    {
        if (pool.TryGetValue(g.name, out lastUsedStack))
        {
            if (pool[g.name].Count == 0)
            {
                lastReturned = Instantiate(g) as GameObject;
                lastReturned.name = g.name;
            }
            else
            {
                lastReturned = pool[g.name].Pop();
            }
        }
        else
        {
            lastReturned = Instantiate(g) as GameObject;
            pool.Add(g.name, new Stack<GameObject>());
            lastReturned.name = g.name;
            return lastReturned;
        }

        return lastReturned;
    }

    public static void AddToPool(GameObject g)
    {
        if (!pool.ContainsKey(g.name))
        {
            pool.Add(g.name, new Stack<GameObject>());
        }
        pool[g.name].Push(g);
    }

}

【问题讨论】:

  • @Neil .NET 列表是基于一个数组的,它是实际上是在移除元素时移动的。双向链表会有可怕的随机访问时间。
  • 作为一个附带问题,不要像那样使用ContainsKey。请改用TryGetValue,以避免进行两次哈希查找。

标签: c# .net list unity3d mono


【解决方案1】:

所有索引集合都具有此属性。如果您在任何时候删除一个元素,则以下所有元素都会向上移动。这是您找不到ConcurrentList[T] 的一大原因。制作一个只会让你先进入索引竞争条件。如果不考虑使用代码,就不可能有一个 List 并发。

Keyd Collections (Dictionary[int, GameObject]) 没有这个缺点。请注意,某些操作有特殊的集合。队列(先进先出)和堆栈(先进后出)。这些可能更适合您的场景。

【讨论】:

  • 堆栈完美地与 O(1) 进行推送和弹出(以及 O(n) 推送成本与完整堆栈),非常感谢!
  • @victordabija:对于那些多任务场景,它甚至还有一个并发变体。
【解决方案2】:

阅读the documentation

当您调用 RemoveAt 删除一个项目时,列表中的剩余项目将重新编号以替换已删除的项目。例如,如果您删除索引 3 处的项目,则索引 4 处的项目将移动到 3 位置。此外,列表中的项目数(由 Count 属性表示)减少了 1。

此方法是一个 O(n) 操作,其中 n 是 (Count - index)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多