c#的JSON、栈和队列、自定义创建一个链表类、LinkedList

64 阅读10分钟

一、使用JSON进行数据传输

1.什么是JSON

JSON官网www.json.org/json-zh.htm… JSON (JavaScript Object Notation) 的轻量级数据交换格式能够替代XML进行数据传输,首先,它是一个字符串,一个有规则的字符串。然后它的表现形式是键值对的,有点像我们前面学的字典Dictionary,以下是JSON的格式,我们可以看到是一组组键值对,在做web开发的时候我们 就是通过JSON来实现接口数据的传输,我们可以看到JSON数据是非常直观的,更易于我们理解和查看,而且JSON也是支持c#的。

2.JSON的语法规则

image.png 根节点可以是对象也可以是数组

{
  "person": {
    "name": "pig",
    "age": "18",
    "sex": "man",
    "hometown": {
      "province": "江西省",
      "city": "抚州市",
      "county": "崇仁县"
    }
  }
}

也可以是数组的形式

[   {    "name": "pig",    "age": "18",    "sex": "man"    },       {    "name": "cate",    "age": "12",    "sex": "woman"    }  ]
复制代码

重写类中的ToString()方法,这样我们去打印对象的时候会打印出对应字段或属性 image.png

3.书写JSON文本

image.png

4.引入JSON库文件(LitJSON)

1.两种引入LitJSON的方式

(1) 在项目依赖中引入LitJSON.dll文件

image.png (2) 在项目依赖的NuGet中搜索LitJSON进行添加 image.png

image.png

2.使用泛型去解析JSON(转成对象数组或者list列表)

注意开头要:using:LitJSON;

转化成对象数组的形式 image.png 也可以转成list列表

image.png

5.将对象转成JSON格式的字符串

image.png

6.将数组写入txt文件中

            int[] intArray = { 1, 2, 3, 4, 5, 6 };
            string json = JsonMapper.ToJson(intArray);
            File.WriteAllText("./1.txt", json);

7.京东联系题: 需求:获取京东首页面菜单栏的数据,并且有层次关系,数组里面包含对象,对象中可以包含数组,数组包含菜单元素

             public class Menu
        //使用一个静态方法帮我们创建对象
        {
            //无参的构造方法
            public Menu() 
            {
                this.firstMenu = FirstMenu;

            }
            //定义一个静态类集合和非静态类集合
            public List<string> firstMenu;
            private static List<string> FirstMenu;


            public static Menu getMenu(List<string> MenuList)
            {
                FirstMenu = MenuList;
                return new Menu();
            }

        }
        
            //main方法的开始-----------------
            //案例:获取京东页面的htnl,将京东菜单栏数据写入txt文件中
            string files = File.ReadAllText("./1.html");

            //因为换行影响我们提取需要的元素,所以我们利用换行来提取我们需要的。
            //干脆把</li>后面加上换行,每次去匹配的内容是没有换行的。
            //利用这个特点,我们匹配谁就给谁加上换行符
            List<Menu> MyMenu = new List<Menu>();
            //将获取到的html内容的换行去掉
            files = files.Replace("\n", "");
            //string pattern1 = "<li\\s+class=\"cate_menu_item\".+>.+</li>"; //?匹配的时候最小子串
            string pattern1 = "<li\\s+class=\"cate_menu_item\".+?</li>"; //.+? 开启贪婪模式   匹配的是最小子串  
            string pattern2 ="<a.+class=\"cate_menu_lk\".+>(.+)</a>";
            MatchCollection mc = Regex.Matches(files, pattern1);
            foreach(Match m in mc)
            {
                if (m.Success)
                {
                    Console.WriteLine(m);
                    List<string> MenuList = new List<string>();
                    //匹配每组a当中的值
                    string file = Convert.ToString(m).Replace("</a>", "</a>\n");
                    MatchCollection mc2 = Regex.Matches(file, pattern2);
                    
                    foreach (Match j in mc2)
                    {
                        MenuList.Add(j.Groups[1].ToString());
                    }
                    //将列表赋值给对象的一个字段,将对象放入列表中
                    MyMenu.Add(Menu.getMenu(MenuList));
                }
            }
            //将列表转化成json写入
            string json = JsonMapper.ToJson(MyMenu);
            File.WriteAllText("./jingdong.txt",json);
            
            

相关链接:

Json校验格式化工具:  www.bejson.com/

Josn编辑器:  www.bejson.com/jsoneditoro…

二、其他几种数据集合

1.栈Stack

Stack是c#为我们封装好的一个类,本质是Object[]数组,只是封装了特殊的存储规则,Stack是栈存储容器,栈是一种先进后出的数据结构,先存入的数据后获取,后存入的数据先获取

image.png

1.创建一个栈对象

需要using:System.collections; image.png

2.栈的相关操作

  1. 压(入)栈(可以是基本数据类型和引用数据类型)

image.png

  1. 弹栈,刚才最后入栈的是new Test(),所以等下出栈第一个应该是一个对象(后进先出)

image.png

  1. 查询栈中的值
  • 查看栈顶的值 image.png
  • 查看元素是否在栈中 image.png
  1. 栈中改变元素只能是压栈和弹栈还有清空

image.png

  1. 获取栈中的长度 stack.Count
  2. 使用foreach遍历栈

image.png

  1. 循环弹栈

image.png 栈的代码汇总:

            //创建一个栈
            Stack s = new Stack();
            Person p = new Person("heihei",12);
            //入栈,数据类型可以不相同
            s.Push(1);
            s.Push("helloworld");
            s.Push('d');
            s.Push(p);

            //查看栈中的值 只能读到栈顶的值 
            //Console.WriteLine(s.Peek); //打印的是一个对象
            //需要读取里面所有的值
            int length = s.Count; //放在for循环的话长度每次都会发生变化
            while (s.Count > 0)
            {
                //获取栈顶的值
                Object j = s.Peek();
                Console.WriteLine(j);
                s.Pop(); //出栈
            }

栈的相关练习题

编写一个方法计算任意一个十进制数的二进制数,使用栈的结构方式存储,之后打印出来

image.png

分析:在进行整数的进制转化中,我们从下往上取余数刚好满足栈先进后出的原理,这时候我们可以把每次得出的余数存入栈中,并且本身的整数也要进行除2的改变,完成入栈操作后,我们就可以进行出栈while循环取值了,就可以完成上述案例。

2.队列Queue

Queue是一种先进先出的数据结构,就像我们排队一样,先排在前面的人先出去 image.png

1.队列的相关操作

  1. 入队列(Enqueue)

image.png

  1. 出队列(Dequeue)

image.png

  1. 查询队列(与栈类似,先是获取最顶上的元素)

image.png

image.png

  1. 删除队列

image.png

  1. 遍历队列

image.png

            //队列:先进先出
            Queue q = new Queue();
            //入队列
            q.Enqueue("hello");
            q.Enqueue(10);
            q.Enqueue('d');

            //出队列
            //q.Dequeue(); //hello出队列了
            //获取的是第一个队列顶的值
            //Console.WriteLine(q.Peek()); // 10

            //循环遍历队列
             while(q.Count > 0)
             {
                 Console.WriteLine(q.Peek());
                 q.Dequeue();
             }
            //Console.WriteLine(q.Contains("hello")); //出队列了表示就没有这个元素了

            //清空队列
            //q.Clear();
            //Console.WriteLine(q); //System.Collections.Queue

队列练习题

使用队列存储消息,一次性存10条消息,每隔一秒打印一条消息,控制台打印消息时要有明显的停顿感,每隔一秒使用Thread.Sleep(1000)

image.png

            Queue q2 = new Queue();
            for(int i = 0; i < 10; i++)
            {
                string str = "第" + (i+1) + "条消息";
                q2.Enqueue(str);
            }
            //遍历队列
            while(q2.Count > 0)
            {
                
                Console.WriteLine(q2.Peek());
                q2.Dequeue();
                Thread.Sleep(1000);
                
            }

3.哈希表

一、什么是Hashtable

Hashtable,中文叫哈希表。它里面的英文Hash,音译是哈希。但Hash还有另一个翻译,就是“散列”,所以也叫散列表。

Hashtable是一个集合。集合是数据结构的一种,数据结构有集合、线性、树形、图形等。

Hashtable可用于存储键值对。

二、C#中如何声明一个Hashtable

您直接new一个Hashtable对象即可,如下所示:

    Hashtable ht = new Hashtable();

三、如何添加和删除

通过Add和Remove方法可以给Hashtable添加或者删除项。

    Hashtable ht = new Hashtable();
    ht.Add("name", "蔡徐坤");
    ht.Add("id", 123);
    //移除元素
    ht.Remove("id");

四、如何遍历

Hashtable的每个项是DictionaryEntry对象,如下:

            //1.遍历哈希表
            foreach (DictionaryEntry i in ht)
            {
                Console.WriteLine(i.Key);
                Console.WriteLine(i.Value);
            }
            Console.WriteLine("------------------------------");
            //2.遍历键
            foreach (var i in ht.Keys)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("------------------------------");
            //遍历值
            foreach (var i in ht.Values)
            {
                Console.WriteLine(i);
            }

//遍历哈希表的时候有点类似字典的遍历,在这里我们回顾一下遍历字典的操作:

image.png

五、判断指定元素是否存在

            Console.WriteLine(ht.Contains("id"));
            Console.WriteLine(ht.ContainsKey("id"));
            Console.WriteLine(ht.ContainsValue("123"));

完整代码如下:

     //哈希表
            Hashtable ht = new Hashtable();
            ht.Add("name", "蔡徐坤");
            ht.Add("id", 123);
            //哈希表里面键名都是唯一的,不能重复添加相同的键名,但是可以覆盖
            //Console.WriteLine(ht["name"]);

            //1.遍历哈希表
            foreach (DictionaryEntry i in ht)
            {
                Console.WriteLine(i.Key);
                Console.WriteLine(i.Value);
            }
            Console.WriteLine("------------------------------");
            //2.遍历键
            foreach (var i in ht.Keys)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("------------------------------");
            //遍历值
            foreach (var i in ht.Values)
            {
                Console.WriteLine(i);
            }

            //移除元素
            ht.Remove("id");

            //判断元素是否存在
            Console.WriteLine(ht.Contains("id"));
            Console.WriteLine(ht.ContainsKey("id"));
            Console.WriteLine(ht.ContainsValue("123"));

运行一下,代码输出结果如下图:

image.png

4.综合练习-

//创建一个简单的链表类--实现增删查功能

    namespace _1026链表
{
    //创建一个简单的链表类
    public class LinkList<T>
    {
        public LinkNode<T> head;//存放的头结点
        public LinkNode<T> last;//存放的尾结点

        //添加的方法,模仿列表的方法
        public void Add(T value)
        {
            //新建一个节点
            LinkNode<T> node = new LinkNode<T>();
            //给新建的节点赋值,值来源你调用得到传入的value
            node.value = value;
            //链表有可能是空的,[一开始是空的,后面添加之后就不为空了]
            if (head == null)
            {
                head = node;
                last = node;
            }
            else //插入到尾结点的后面
            {
                //将插入的节点作为尾结点,last.nextNode不在指向为null,原来的尾结点存放的是新插入节点(新尾结点的)的地址
                last.nextNode = node;
                //将新插入的值作为尾结点的值
                last = node;
            }
        }

        //查询的方法 输入一个值 帮我查询返回该节点
        public LinkNode<T> Find(T value) //head.value.Equals(value) 判断value是不是对象中的value字段的值
        {
            //每次从头结点开始遍历,将头结点赋值给一个新的节点接收【如果没有提前插入元素的话,这里查询会报错,head头结点为空】
            LinkNode<T> node = head;
            //直到一个节点为空,也就是尾结点的下一个节点为空,这时候就跳出循环
            while (node != null)
            {
                //每次循环判断节点中的字段value的值是不是我传进来的值
                if (node.value.Equals(value))
                {
                    //如果是就返回对应的节点
                    return node;
                }
                //每次循环改变node的值,移到下一个节点进行下一轮的判断
                node = node.nextNode;
            }
            return null; //没有查询到返回null
        }

        //删除操作
        public void Remove(T value) //移除某个值的节点  先找到节点,有就删除,没有就不用删除-啥都不做
        {
            //没有节点,从头结点开始遍历
            LinkNode<T> node = head;
            while (node != null)
            {
                //1.只有一个节点的情况
                if (head.nextNode == null)
                {
                    head = null;
                    last = null;
                    //删除头节点之后跳出循环
                    return;
                }
                else
                {
                    //2.多个节点的情况移除头结点 只需要将head = head.nextNode
                    if (head.value.Equals(value))
                    {
                        //原来的头结点被删除之后,下一个节点作为新的头结点,head.nextNode存放的就是原来头结点下一个地址
                        //把下一个节点的地址重新赋值给头节点,作为新的头节点
                        head = head.nextNode;
                    }
                }
                //每次循环判断节点中的字段value的值是不是我传进来的值
                if (node.nextNode.value.Equals(value))
                {
                    Console.WriteLine(222);
                    //3.如果有多个节点,移除最后的节点 last等于前一个节点 前一个节点的nextNode等于空 
                    if (last.value.Equals(value))
                    {
                        Console.WriteLine(111);
                        node.nextNode = null;
                        last = node;

                    }else
                    {
                        //4.移除中间的节点 该节点的前一个的节点指向下下个节点
                        node.nextNode = node.nextNode.nextNode;
                        Console.WriteLine(333);
                    }
                    
                    return;
                }
                //每次循环改变node的值,移到下一个节点进行下一轮的判断
                node = node.nextNode;
            }
            return; //没有该节点的值
        }
    }

    //链表节点类
    public class LinkNode<T>
    {
        public T value;//数据域
        public LinkNode<T> nextNode; //后指针域
        public LinkNode<T> previous; //前指针域

    }
    internal class Program
    {
        static void Main(string[] args)
        {
            //new一个LinkList
            LinkList<string> list = new LinkList<string>();
            list.Add("afsfsf");
            Console.WriteLine(list.Find("afsfsf").value);
            list.Add("hello");
            Console.WriteLine(list.Find("hello").value);
            list.Add("hi");
            Console.WriteLine(list.Find("hi").value);
            list.Remove("hi");
            //成功移除掉
            Console.WriteLine(list.Find("hi") == null);
        }
    }
}

这个创建链表实现增删查很经典,需要多加回顾。

5.LinkedList

C# LinkedList 类使用链表的概念。它允许我们快速插入和删除元素。它可以有重复的元素。它位于 System.Collections.Generic 命名空间中。

它允许我们在之前或最后一个索引处添加和删除元素。

一、创建一个LinkedList对象

        LinkedList<string> list = new LinkedList<string>();

二、往链表里面添加元素

        list.AddFirst("头节点");
        list.AddLast("尾节点");

三、获取头结点和尾节点

            Console.WriteLine(list.First.Value);//list.First表示找到头结点
            Console.WriteLine(list.Last.Value);

四、表示在某个节点的前面或后面插入一个节点

            list.AddBefore(list.First, "新头"); //表示在某个节点的前面插入一个新结点作为头结点
            Console.WriteLine(list.First.Value);
            list.AddAfter(list.Last,"新的尾"); //表示在某一个节点的后面插入一个新的节点值作为新的尾
            Console.WriteLine(list.Last.Value);

五、寻找指定的节点

            LinkedListNode<string> node = list.Find("尾节点");
            if (node != null)
            {
                Console.WriteLine(node.Value);
            }

六、移除指定的节点

            //移除节点
            list.Remove("新的尾");
            list.Remove("新头");
            Console.WriteLine(list.Last.Value); //移除后头结点作为第一个
            Console.WriteLine(list.First.Value); //移除后尾结点作最后一个

七、查询该节点是否在链表中,返回bool值

     Console.WriteLine(list.Contains("头节点"));

八、遍历链表

    foreach(string s in list)
            {
                Console.WriteLine(s);
            }
九、完整代码如下:
    using System.Collections;
namespace demo1028
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //集合类  链表类
            LinkedList<string> list = new LinkedList<string>();
            //添加
            list.AddFirst("头节点"); //插入头结点的前面,传入插入节点的值

            //拿到头节点:
            Console.WriteLine(list.First.Value);//list.First表示找到头结点
            list.AddLast("尾节点");
            Console.WriteLine(list.Last.Value); //list.First表示找到尾结点

            list.AddBefore(list.First, "新头"); //表示在某个节点的前面插入一个新结点作为头结点
            Console.WriteLine(list.First.Value);
            list.AddAfter(list.Last,"新的尾"); //表示在某一个节点的后面插入一个新的节点值作为新的尾
            Console.WriteLine(list.Last.Value);

            //找到指定的节点 没有找到返回空
            LinkedListNode<string> node = list.Find("尾节点");
            if (node != null)
            {
                Console.WriteLine(node.Value);
            }
            Console.WriteLine("--------------------------------------");
            //移除节点
            list.Remove("新的尾");
            list.Remove("新头");
            Console.WriteLine(list.Last.Value); //移除后头结点作为第一个
            Console.WriteLine(list.First.Value); //移除后尾结点作最后一个

            //查询该节点是否在链表中,返回bool值
            Console.WriteLine(list.Contains("头节点"));
            Console.WriteLine("--------------------------------------");

            //遍历链表
            foreach(string s in list)
            {
                Console.WriteLine(s);
            }
            Console.WriteLine("--------------------------------------");
            Console.WriteLine(list.Last.Previous.Value);

        }
    }
}
运行结果如下:

image.png