(C#으로 데이터 구조 구현) LinkedList 생성(제네릭 타입)

C#에서 LinkedList 만들기

목록 유형을 일반화하는 LinkedList를 만들어 보겠습니다.

앞선 글에서 LinkedList는 C++를 템플릿으로 만들어서 모든 종류의 데이터를 관리할 수 있는데, 아래 링크는 참고용이다.

https://suldenlion.90

C++의 템플릿과 C#의 제네릭은 기본적으로 동일합니다.

그러나 C++, C# 및 Java의 템플릿/제네릭 간에는 몇 가지 차이점이 있습니다.

웹 검색을 참고하여 일부 내용이 잘 이해되지 않고 요약되어 있어 내용상 오류가 있을 수 있습니다.

C++이 컴파일되면 나중에 동일한 기계어 코드를 실행합니다(모두 합쳐서 하나의 컴파일). 하지만 C#의 경우 한 번 컴파일한 후 IL 코드(가상 머신이 실행될 때 바이트코드로 변환되는 스택 기반 어셈블리 언어)로 컴파일됩니다. 이 컴파일된 출력은 CLR(프로그램 코드의 실행 환경을 정의하는 Microsoft의 공통 언어 기반 표준 기능입니다.), CLR이 실행되는 환경에 따라 머신 코드로 다시 컴파일됩니다(총 2회 컴파일).

템플릿을 실제로 사용하지 않을 때는 코드가 전혀 생성되지 않으며, C# Generic을 사용하지 않더라도 관련 정보를 저장하기 위해 메타데이터를 생성한다. (실제 사용시 이 메타데이터에서 관련 코드 생성)

Java는 제네릭과 관련된 메타데이터를 바이트코드에 저장하고 실행 시 바이트코드를 기계어 코드로 컴파일한다는 점에서 C#과 유사합니다.

위의 정보를 종합하면 C++ 템플릿은 단일 컴파일로 최종 결과를 생성합니다. → 현재 생성시 해당 코드가 유효한지 확인 가능

반면 제네릭은 IL이나 바이트 코드로 컴파일되더라도 최종 결과물이 아니며 2차 컴파일 과정에서 구현된다. 첫 번째 컴파일에서 감지할 수 없음

이것이 템플릿과 제네릭의 차이점이며 C#의 제네릭과 Java의 제네릭 간의 차이점도 살펴보겠습니다.

C#에 제네릭이 도입되면서 컴파일 시간 오류를 감지하고 불필요한 캐스팅을 피할 수 있으므로 제네릭을 사용하지 않는 것에 비해 성능이 향상되었습니다.

Java는 제네릭을 사용할 때 컴파일 타임 오류를 감지할 수 있지만 실제 실행되는 코드는 제네릭을 사용하지 않은 것과 동일하므로 성능 향상이 없습니다.

요약하면 C#은 이전 버전과의 호환성을 일부 포기하면서 성능과 형식 안전성을 유지하는 반면 Java는 호환성과 형식 안전성을 유지하고 성능을 일부 포기합니다.

그리고 Java의 제네릭은 일단 컴파일된 바이트코드에는 유형 정보가 없다고 말합니다. 따라서 Java는 일반 목록에 포함된 객체의 유형을 유추할 수 없습니다.

반면 C#의 CLR은 클래스가 로드될 때 유형 정보를 포함하는 클래스를 동적으로 생성합니다. 이 때문에 C#은 형식을 유추할 수 있습니다.

위와 같은 이유가 정수형 LinkedList가 있기 때문이라면 C#에서는 LinkedList라는 변수형이 있는 것 같다. 수정되지 않았으며 Java는 LinkedList 래퍼 개체 유형을 사용합니다. 내부

이제 제네릭을 포함하는 LinkedList 코드를 살펴보겠습니다.

using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSLinkedList
{
    class Node<T>
    {
        T data;
        Node<T> next;
        public Node()
        {
            data = default(T);
            next = null;
        }
        public Node(T data)
        {
            this.data = data;
            this.next = null;
        }
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        public Node<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    }
    class LinkedList<T>
    {
        Node<T> header;
        int count;
        public LinkedList()
        {
            header = null;
            count = 0;
        }
        public void addFirst(T data)
        {
            Node<T> newNode = new Node<T>(data);
            newNode.Next = header;
            header = newNode;
            count++;
        }
        public void add(T x)
        {
            Node<T> tmp = header;
            Node<T> newNode = new Node<T>(x);

            if (count == 0)
            {
                addFirst(x);
                return;
            }
            while (tmp.Next != null)
            {
                tmp = tmp.Next;
            }
            count++;
            tmp.Next = newNode;
        }
        public void remove(T x)
        {
            Node<T> tmp;
            Node<T> follow;

            tmp = header;
            follow = tmp;
            if (count == 0)
            {
                Console.WriteLine("List is empty");
                return;
            }
            if (header.Data.Equals(x))
            {
                header = header.Next;
                count--;
                return;
            }
            while (tmp != null)
            {
                if (tmp.Data.Equals(x))
                {
                    follow.Next = tmp.Next;
                    count--;
                    return;
                }
                follow = tmp;
                tmp = tmp.Next;
            }
            Console.WriteLine("Cannot find element");
        }
        public void addAt(int index, T x)
        {
            Node<T> tmp = header;
            Node<T> newNode = new Node<T>(x);

            if (index == 0)
            {
                addFirst(x);
                return;
            }
            if (index > count)
            {
                Console.WriteLine("Incorrect index");
                return;
            }
            for (int i = 0; i < index-1; i++)
            {
                tmp = tmp.Next;
            }
            newNode.Next = tmp.Next;
            tmp.Next = newNode;
            count++;
        }
        public T this(int index)  //indexer
        {
            get
            {
                Node<T> tmp = header;
                T value;

                if (count == 0)
                {
                    Console.WriteLine("List is empty");
                    Environment.Exit(-1);
                }
                if (index == 0)
                {
                    value = header.Data;
                    return value;
                }
                if (index > count - 1)
                {
                    Console.WriteLine("Incorrect index");
                    Environment.Exit(-1);
                }
                for (int i = 0; i < index; i++)
                {
                    tmp = tmp.Next;
                }
                value = tmp.Data;
                return value;
            }
            set
            {
                Node<T> tmp = header;

                if (count == 0)
                {
                    Console.WriteLine("List is empty");
                    return;
                }
                if (index > count - 1)
                {
                    Console.WriteLine("Incorrect index");
                    return;
                }
                for (int i = 0; i < index; i++)
                {
                    tmp = tmp.Next;
                }
                tmp.Data = value; // value = property
            }
        }
        public T get(int index)
        {
            Node<T> tmp = header;
            T value;

            if (count == 0)
            {
                Console.WriteLine("List is empty");
                Environment.Exit(-1);
            }
            if (index == 0)
            {
                value = header.Data;
                return value;
            }
            if (index > count-1) 
            {
                Console.WriteLine("Incorrect index");
                Environment.Exit(-1);
            }
            for (int i = 0; i < index; i++)
            {
                tmp = tmp.Next;
            }
            value = tmp.Data;
            return value;
        }
        public void set(int index, T x)
        {
            Node<T> tmp = header;

            if (count == 0)
            {
                Console.WriteLine("List is empty");
                return;
            }
            if (index > count - 1)
            {
                Console.WriteLine("Incorrect index");
                return;
            }
            for (int i = 0; i < index; i++)
            {
                tmp = tmp.Next;
            }
            tmp.Data = x;
        }
        public override string ToString()
        {
            String s;
            Node<T> tmp = header;

            s = "(";
            while (tmp != null)
            {
                s = s + tmp.Data;
                if (tmp.Next != null)
                {
                    s += ", ";
                }
                tmp = tmp.Next;
            }
            s = s + ")";

            return s;
        }

    }

    class Student
    {
        public String name;
        public int grade;
        public Student()
        {
            name = null;
            grade = 0;
        }
        public Student(String name, int grade)
        {
            this.name = name;
            this.grade = grade;
        }
        public override string ToString()
        {
            String s = name + ", " + grade + " ";
            return s;
        }
    }

    class Program
    {
        static void Main(string() args)
        {
            LinkedList<Student> xList = new LinkedList<Student>();
            xList.add(new Student("Kim", 20));
            xList.addFirst(new Student("Lee", 30));
            xList.addFirst(new Student("Park", 50));
            Console.WriteLine(xList);

            LinkedList<String> sList = new LinkedList<string>();
            sList.addFirst("Kim");
            sList.addFirst("Lee");
            sList.addFirst("Park");
            sList.addFirst("Kwon");
            Console.WriteLine(sList);

            LinkedList<int> list = new LinkedList<int>();
            list.addFirst(10);
            list.addFirst(20);
            list.addFirst(30);
            list.add(100);
            list.remove(30);
            list.addAt(3, 200);
            int p = list(2);
            Console.WriteLine(p);
            Console.WriteLine(list.get(3));
            Console.WriteLine(list);
            //list.set(3, 1000);
            list(2) = 1000;
            Console.WriteLine(list);
        }
    }
}

단순히 add(), remove(), get(), set() 등을 수행하는 LinkedList입니다. 기본 기능에서 Student 개체를 LinkedList에 넣고 테스트하고 String 및 내부도 테스트합니다.

학생 개체는 데이터 멤버로 문자열 이름과 int 클래스를 가지며 생성자와 toString을 가집니다. toString()은 Object 클래스에서 상속받았으므로 오버라이드할 수 있도록 오버라이드를 작성합니다.

코드 상단에서 보면 Node 클래스는 제네릭 타입, 데이터 변수는 Type, Node 다음 노드를 가리키는 next는 노드를 형성합니다.

노드의 빈 생성자에서


데이터는 기본(유형)으로 주어지지만 값이 없다고 말하기(또는 생략하기)가 모호해 보입니다. 데이터 유형을 알 수 없으므로 NULL 또는 0이 될 수 있음을 의미하는 구문으로 보입니다.


노드 데이터와 next는 private로 정의되어 있으므로 getter와 setter가 생성됩니다.

다음으로 LinkedList를 보면 add(), remove(), get(), set() 등은 제네릭 타입을 잘 처리하기 위해 T를 잘 추가해서 정의하고 마지막으로 dem과 관련된 부분을 인덱서에 해당하는 것을 보자. .


메인 파일의 마지막 부분을 보면 int 타입의 LinkedList가 생성되어 사용된다.

인덱스 3에 30, 20, 10, 100을 삽입하고 30을 빼고 200을 삽입하면 목록에 (20, 10, 100, 200)이 포함됩니다.

나는 정수 p를 만들고 list(2)와 일치하도록 100을 설정합니다. C#은 인덱서를 통해 개체의 데이터에 액세스할 수 있습니다. Java의 반복자와 같습니다.



인덱서의 get은 ‘int p = list(2);’입니다. 이렇게 하는 부분과 같고 set은 ‘list(2) = 1000;’ 하는 부분과 같습니다.

노드를 순회할 때 콘텐츠는 이제 원하는 위치의 값을 가져오거나 위치의 값을 변경합니다.

예외가 발생하면 Environment.Exit(-1)을 실행합니다. 프로그램을 종료합니다.