제네릭, 연결리스트

brave_chicken·2024년 5월 27일

잇(IT)생 챌린지

목록 보기
56/90

3. 제네릭

  • 여러타입이 올 수 있는 부분을 일반적인 기호로 정의하고 클래스를 사용하느 시점에 타입을 정해서 사용할 수 있도록 처리
  • 타입에 대한 검증은 컴파일러가 한다.
  • 제네릭을 적용하는 경우 대체문자열을 이용해서 정의
<T> : 타입
<E> : 요소(자료구조 내부에 저장되는 요소의 타입)
<K> : key
<V> : value

generic 실습

  • 제네릭을 사용함으로써 사용이 유연해짐
  • 타입이 여러 개라 타입에 따라 여러 클래스를 만들던 걸 하나로 만들수있음


타입에 제약 걸기 (extends Material)

GenericPrinterExtends

T는 Material 하위클래스로 특정해서 지정하는 방법

public class GenericPrinterExtends<T extends Material> {
	private T material;

	public T getMaterial() {
		return material;
	}

	public void setMaterial(T material) {
		this.material = material;
	}
	
}

GenericPrinterExtendsTest

public class GenericPrinterExtendsTest {
	public static void main(String[] args) {
		Aluminum al = new Aluminum();
		GenericPrinterExtends<Aluminum> printer1 = new GenericPrinterExtends<>();
		printer1.setMaterial(al);
		System.out.println(printer1.getMaterial());
		Aluminum p = printer1.getMaterial();
		System.out.println(p);
		
		System.out.println("=================");
		Plastic pl = new Plastic();
		GenericPrinterExtends<Plastic> printer2 = new GenericPrinterExtends<>();
		printer2.setMaterial(pl);
		System.out.println(printer2.getMaterial());
		Plastic p2 = printer2.getMaterial();
		System.out.println(p2);
		
		System.out.println("=================");
		p2.print();
		//GenericPrinterExtends<Coffee> printer3 = new GenericPrinterExtends<>();
		
	}
}


사각형 면적 구하기

Point

public class Point<T,V> {
	T x; //x좌표 - left,right로 표현(실수나 정수가 올 수 있다)
	V y; //y좌표 - top,bottom을 표현
	public Point(T x, V y) {
		super();
		this.x = x;
		this.y = y;
	}
	public T getX() {
		return x;
	}
	public void setX(T x) {
		this.x = x;
	}
	public V getY() {
		return y;
	}
	public void setY(V y) {
		this.y = y;
	}
	
}

GenericMethodTest

public class GenericMethodTest {
	//사각형의 면적을 계산해서 리턴하는 메소드
	//메소드 안에서 사용할 제네릭의 기호를 리턴타입 옆에 정의
	public static<T,V> double genericTest(Point<T, V> p1, Point<T, V> p2) {
		double result = 0.0;
		double left = ((Number) p1.getX()).doubleValue();
		double top = ((Number) p1.getY()).doubleValue();
		
		double right = ((Number) p2.getX()).doubleValue();
		double bottom = ((Number) p2.getY()).doubleValue();
		
		double width = right - left;
		double height = bottom - top;
		result = width * height;
		return result;
	}
	
	public static<T1,V1,T2,V2> double genericTest2(Point<T1, V1> p1, Point<T2, V2> p2) {
		double result = 0.0;
		double left = ((Number) p1.getX()).doubleValue();
		double top = ((Number) p1.getY()).doubleValue();
		
		double right = ((Number) p2.getX()).doubleValue();
		double bottom = ((Number) p2.getY()).doubleValue();
		
		double width = right - left;
		double height = bottom - top;
		result = width * height;
		return result;
	}
	
	
	public static void main(String[] args) {
		Point<Integer,Double> point1 = new Point<Integer, Double>(10, 10.5);
		Point<Integer,Double> point2 = new Point<Integer, Double>(25, 30.8);
		
		Point<Integer,Double> point3 = new Point<Integer, Double>(10, 10.5);
		Point<Float,Long> point4 = new Point<Float,Long>(30.8F, 25L);
		
		double rectArea = genericTest(point1, point2);
		double rectArea2 = genericTest2(point3, point4);
		System.out.println("면적은 "+rectArea+"이다.");
		System.out.println("면적은 "+rectArea2+"이다.");
	}

}


미션

generic 연습

  • MyNum클래스 정의하세요.
    MyNum클래스는 num1과 num2의 변수가 정의되어 있고 타입은 제네릭으로 표현하세요
  • Opr클래스를 정의하고 MyNum클래스를 매개변수로 받아 +, -, *, / 연산을 하는 메소드를 각각 만들어주세요.
    모두 double연산으로 처리합니다.
  • GenericExamTest클래스를 정의하고 int,int와 double,double을 받아 연산하는 작업을 처리해보세요

MyNum

연산할 숫자를 가지고 있는 클래스

public class MyNum<T> {
	T num1;
	T num2;
	
	public MyNum(T num1, T num2) {
		super();
		this.num1 = num1;
		this.num2 = num2;
	}
	
	public T getNum1() {
		return num1;
	}
	public void setNum1(T num1) {
		this.num1 = num1;
	}
	public T getNum2() {
		return num2;
	}
	public void setNum2(T num2) {
		this.num2 = num2;
	}
	
}

Opr

public class Opr {
	public <T> double add(MyNum<T> numObj) {
		double num1 = ((Number) numObj.getNum1()).doubleValue();
		double num2 = ((Number) numObj.getNum2()).doubleValue();
		
		return num1+num2;
	}
	public <T> double sub(MyNum<T> numObj) {
		double num1 = ((Number) numObj.getNum1()).doubleValue();
		double num2 = ((Number) numObj.getNum2()).doubleValue();
		
		return num1-num2;
	}
	public <T> double mul(MyNum<T> numObj) {
		double num1 = ((Number) numObj.getNum1()).doubleValue();
		double num2 = ((Number) numObj.getNum2()).doubleValue();
		
		return num1*num2;
	}
	public <T> double div(MyNum<T> numObj) {
		double num1 = ((Number) numObj.getNum1()).doubleValue();
		double num2 = ((Number) numObj.getNum2()).doubleValue();
		
		return num1/num2;
	}
}

GenericExamTest

public class GenericExamTest {
	public static void main(String[] args) {
		MyNum<Integer> numObj1 = new MyNum<Integer>(100, 50);
		MyNum<Double> numObj2 = new MyNum<Double>(50.9, 20.8);
		Opr opr = new Opr();
		System.out.println(opr.add(numObj1));
		System.out.println(opr.mul(numObj1));
		System.out.println("----------------");
		System.out.println(opr.add(numObj2));
		System.out.println(opr.sub(numObj2));
	}
}


compare.test 실습

CompareToTest

//CompareTo메소드
public class CompareToTest {
	public static void main(String[] args) {
		System.out.println((int)'A');
		//작은따옴표로 하는 이유(문자 하나 char)
		//"" : String  , '' : char
		
		
		Integer i = 10;
		Integer j = 20;
		Integer k = 5;
		Integer num = 10;
		
		System.out.println(i.compareTo(j));//-1 : i가 비교대상의 값보다 작은 경우
		System.out.println(i.compareTo(k));//1 : i가 비교대상의 값보다 큰 경우
		System.out.println(i.compareTo(num));//0 : i가 비교대상의 값과 같은 경우
		
		String str0 = "B";
		String str1 = "A";//65
		String str2 = "B";//66 - 기준값
		String str3 = "C";//67
		String str4 = "D";//68
		String str5 = "E";//69
		String mystr = "test2";
		System.out.println("===============문자열비교================");
		System.out.println(str2.compareTo(str0));//0 - 같은 문자열
		//System.out.println(mystr.compareTo("test2"));//0 - 같은 문자열
		System.out.println(str2.compareTo(str1));//str2가 B(66)이고 str1이 A(65) => 1
		System.out.println(str2.compareTo(str3));//66-67 = -1 
		System.out.println(str2.compareTo(str4));//66-68 = -2
		System.out.println(str2.compareTo(str5));//66-69 = -3
		System.out.println("E".compareTo("A"));//69-65 = 4
	}
}

datastructure.liked 실습

MyNode

  • 연결리스트에 저장될 데이터정보와 다음 연결될 노드의 정보를 갖고 있는 객체
  • 연결리스트에서 하나의 데이터 정보를 담고있는 객체를 모델링
  • 298p
public class MyNode<E> {
	private E data;//저장될데이터
	MyNode<E> nextNode;//참조하게될 다음 노드 정보 - 참조할 노드가 없는 경우에는 null이 할당
	public MyNode() {
		data = null;
		nextNode = null;
	}
	public MyNode(E data) {
		this.data = data;
		this.nextNode = null;
	}
	public MyNode(E data, MyNode<E> nextNode) {
		super();
		this.data = data;
		this.nextNode = nextNode;
	}
	public E getData() {
		return data;
	}
	public void setData(E data) {
		this.data = data;
	}
	
}

MyLinkedList

public class MyLinkedList<E> {
	private MyNode<E> head;//첫번째 노드를 헤드라고 부른다.(헤드에 노드를 직접 저장)
	int size;//링크드리스트에 몇개가 저장되어있는지를 표현할 변수
	
	public MyLinkedList() {
		head = null;
		size = 0;
	}
	
	//맨마지막에 노드를 삽입하는 작업 처리
	//맨 마지막까지 쭉 찾아가서 (순회) 요소를 저장하는데 맨 마지막 요소는 참조하는 노드가 null이다.
	public MyNode<E> add(E data) {
		//추가될 노드
		MyNode<E> newNode = null;
		//head가 널인지 체크. 널이라는 것은 첫번째 노드라는 의미이므로 head에 추가하는 작업부터 시작
		if(head==null) {
			//노드를 만들고
			newNode = new MyNode<>(data);
			//헤드에 셋팅
			head = newNode;
		}else {
			//head가 널이 아니라는 것은 이미 head에 노드가 셋팅됐다는 의미이므로 노드들을 순회해서 마지막까지 찾아가서 
			//마지막 노드에 추가
			newNode = new MyNode<>(data);
			//맨 마지막 노드를 찾기 위해서 노드를 순회해야 한다.
			//노드를 찾아가기 위해서 노드가 있어야 하므로 head부터 찾아가면 된다.
			
			//템프노드는 중간에 순회하는 노드를 넣으려고 임시로 선언한 노드
			//else블럭에 넣고 while문을 돌리면서 순회하는대로 찾는 노드를 넣을것
			MyNode<E> tempNode = head;
			while(tempNode.nextNode!=null) {
				tempNode = tempNode.nextNode;
			}
			//tempNode는 nextNode가 널인상태 => 즉 마지막 노드라는 의미
			tempNode.nextNode = newNode;
		}
		size++;
		return newNode;
	}
	
	public void printAll() {
		//모든 연결된 노드를 순회하며 data를 출력하기
		//data -> data -> ...
		MyNode<E> tempNode = head;
		while(tempNode!=null) {
			System.out.print(tempNode.getData());
			tempNode = tempNode.nextNode;//그다음 노드를 순회하기 위해 넘겨주기
			if(tempNode!=null) {//마지막 노드가 아니면
				System.out.print("->");
			}
		}
		System.out.println();
	}
	public boolean isEmpty() {
		if(head==null) {
			return true;
		}else {
			return false;
		}
	}
	public int getSize() {
		return size;
	}
	public void removeAll() {
		head = null;
		size = 0;
	}
	public MyNode<E> insert(int position, E data) {
		//첫번째 노드부터 순회하며 삽입될 위치로 찾아가야 함
		MyNode<E> tempNode = head;
		MyNode<E> newNode = new MyNode<>(data);
		if(position==0) {
			newNode.nextNode = head;//원래 헤드에 있던 노드 뉴노드의 다음 노드에 삽입
			head = newNode;//새로운 노드 헤드에 삽입
		}else {
			MyNode<E> preNode = null;
			//삽입될 위치의 이전 노드의 포지션을 찾아 작업하기
			for(int i=0;i<position;i++) {
				preNode = tempNode;
				//노드를 순회하면서 다음 참조하는 노드를 tempNode로 지정 - 계속 찾아나가기 위해서 작업
				tempNode = tempNode.nextNode;
			}
			//새 노드의 nextNode를 현재 preNode의 nextNode로 설정
			newNode.nextNode = preNode.nextNode;
			//preNode의 nextNode를 새 노드로 설정
			preNode.nextNode = newNode;
		}
		size++;
		return newNode;
	}
	public MyNode<E> remove(int position){
		MyNode<E> tempNode = head;
		//맨앞의 노드를 삭제하는 경우
		if(position==0) {
			head = tempNode.nextNode;
		}else {
			MyNode<E> preNode = null;
			for(int i=0;i<position;i++) {
				preNode = tempNode;
				tempNode = tempNode.nextNode;
			}
			preNode.nextNode = tempNode.nextNode;
		}
		size--;
		System.out.println(position+"항목이 삭제됐습니다.");
		return tempNode;
	}
}

MyLinkedListTest

public class MyLinkedListTest {
	public static void main(String[] args) {
		MyLinkedList<String> list = new MyLinkedList<>();
		list.add("사과");
		list.add("배");
		list.add("바나나");
		list.printAll();//전체 LinkedList의 모든 데이터를 출력
		System.out.println("-------------");
		list.insert(2,"수박");
		list.printAll();//사과->배->수박->바나나
		System.out.println("-------------");
		list.remove(1);
		list.printAll();//사과->수박->바나나
	}
}

미션 리스트

  • printAll완성하기(printAll의 실행결과를 캡쳐해주세요)

  • insert메소드를 구현해서 중간에 원하는 노드가 삽입될 수 있도록 구현하세요

  • remove완성하기

본 포스팅은 멀티캠퍼스의 멀티잇 백엔드 개발(Java)의 교육을 수강하고 작성되었습니다.

0개의 댓글