程序中的映射关系与链表详解

映射关系在程序中的表示主要有以下三种方式:

一对一  键值对的方式,可以用Dictionary<key,value>

一对多  可以用字典套集合 Dictionary<key,List<object>>

多对多  可以用字典套链表 Dictionary<key,Node>

 

链表

我们知道链表是节点连接组成的,而节点是由数据域和指针域构成的,顾名思义,数据域data就是存数据的,当然数据类型得我们自己看需求定义,指针域就是存node的引用,一般用next表示。

 

下面我通过一个多对多的映射关系,演示一下

1. 链表中的节点;

2. 多对多关系的实现;

3. 链表中元素的添加,移除。

那么下面我定义一个类表示节点:

public class EventNode

{

    /// <summary>

    /// 当前数据

    /// </summary>

    public MonoBase data;

 

    /// <summary>

    /// 下一个节点

    /// </summary>

    public EventNode next;

 

    /// <summary>

    /// 构造函数

    /// </summary>

    public EventNode(MonoBase data)

    {

        this.data = data;

        this.next = null;

    }

}

 

 

我定义了很多条消息,每个消息可以有多个脚本监听,每个脚本中可以添加很多条消息。如何实现如下:

我可以先写一个消息类MsgBase

如下:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class MsgBase  {

 

    public ushort msgId;

 

    /// <summary>

    /// 得到Manager

    /// </summary>

    /// <returns></returns>

    public ManagerID GetManager()

    {

        int managerId = (int)msgId / FrameTools.MsgPan;

        return (ManagerID)( FrameTools.MsgPan * managerId);

    }

 

    /// <summary>

    /// 构造函数

    /// </summary>

    /// <param name="msgId">msgId消息Id</param>

    public MsgBase(ushort msgId)

    {

        this.msgId = msgId;

    }

}

我定义了每个消息类,我每个消息有自己独特的id,所以我们可以定义一个数据结构,来实现多条消息对应多个脚本。

 /// <summary>

    /// 存储注册消息 msgId 值 脚本链表

    /// </summary>

private Dictionary<ushort, EventNode> eventTree = new Dictionary<ushort, EventNode>();

 

由于我们采用的是字典键值对的方式,所以我们可以定义四个方法,

1. 注册消息----脚本

2. 注册脚本----消息

3. 移除消息----脚本

4. 移除脚本----消息

 

   具体方法如下:

 /// <summary>

    /// 往一个脚本里注册若干消息

    /// </summary>

    /// <param name="mono">要注册的脚本</param>

    /// <param name="msgs">一个脚本可以注册多条消息</param>

    public void RegisterMsg(MonoBase mono,params ushort[] msgs )

    {

       

        for (int i = 0; i < msgs.Length; i++)

        {

            EventNode node = new EventNode(mono);

 

            RegisterMsg(msgs[i], node);

        }

    }

   链表的增加,每次把需要增加的节点,跟在链表的最后一个节点后面。

    /// <summary>

    /// 根据msgId注册一个消息链表

    /// </summary>

    /// <param name="msgId">根据msgId</param>

    /// <param name="data">node链表</param>

    public void RegisterMsg(ushort msgId, EventNode node)

    {

        if (!eventTree.ContainsKey(msgId))

        {

            eventTree.Add(msgId, node);

        }

        else

        {

            EventNode temp = eventTree[msgId];

            //如果当前键对应的空间不止一个节点

            //那么遍历节点,找到最后为空的那个节点,

 

            while (temp.next != null)

            {

              temp=temp.next;

            }

            //把node添加在最后

            temp.next = node;

        }

    }

 

    /// <summary>

    /// 去掉一个脚本的若干个消息

    /// </summary>

    /// <param name="mono">mono脚本</param>

    /// <param name="msgs">消息id数组</param>

    public void UnRegisterMsg(MonoBase mono, params ushort[] msgs)

    {

        for (int i = 0; i < msgs.Length; i++)

        {

            UnRegisterMsg(msgs[i], mono);

        }

    }

 

链表节点的移除,我们可以先去,注意我们要移除的节点有下列三种情况:

1. 在链表的头结点(在链表的头结点也分两种情况,1.头结点后面没有其他节点2.链表只有一个节点,这个节点就是头结点)

2. 在链表的中间

3. 在链表的尾部

    /// <summary>

    /// 去掉一个消息链表

    /// </summary>

    /// <param name="msgId">消息id</param>

    /// <param name="node">脚本</param>

    public void UnRegisterMsg(ushort msgId, MonoBase node)

    {

        if (!eventTree.ContainsKey(msgId))

        {

            Debug.LogWarning("not contail id==" + msgId);

            return;

        }

        else

        {

            EventNode temp = eventTree[msgId];

            //去掉头部,包含两种情况

            if (temp.data == node)

            {

                EventNode header = temp;

                //后面多个节点

                if (header.next != null)

                {

                    eventTree[msgId] = header.next;

                    header.next = null;

                }

                else //只有一个节点的情况

                {

                    eventTree.Remove(msgId);

                }

            }

            else //去掉尾部和中间的节点

            {

                while (temp.next != null && temp.next.data != node) //只要下一个不是空,或者下一个node存的不是我想要找的,那么我就一直往下找

                {

                    temp = temp.next;

                } //表示已经找到该节点

                //没有引用 ,自动释放

                if (temp.next.next != null)  //去掉中间

                {

                    EventNode currentNode = temp.next;

                    temp.next = currentNode.next;

                    currentNode.next = null;

                }

                else //去掉尾部

                {

                    temp.next = null;

                }

            }

        }

    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值