当我提取元素时,c#List 的数组是不是被移动?

Posted

技术标签:

【中文标题】当我提取元素时,c#List 的数组是不是被移动?【英文标题】: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,以避免进行两次哈希查找。 【参考方案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)。

【讨论】:

以上是关于当我提取元素时,c#List 的数组是不是被移动?的主要内容,如果未能解决你的问题,请参考以下文章

Python中的数组索引

无法从 SQLite 表中显示 ListView 元素

pop_front()函数⽤法

C# - 更改多维数组元素类型

C#Windows窗体 - 调整窗体大小时移动元素

从指向结构数组的指针中提取元素