HOMEWORK

DAY 11 - HOMEWORK - SungJukDTO / SungJuk (2024.07.17)

summ.n 2024. 8. 12. 19:03

 

 

 

[문제] 성적처리

 

1. 필드로 번호(no), 이름(name), 국어(kor), 영어(eng), 수학(math), 총점(tot), 평균(avg) 잡는다

 

2. 번호 입력할 때 중복해서 넣지 않는다.

 

3. 1인분의 클래스로 SungJukDTO.java 사용한다.

 

4. SungJukDTO.java 에서 toString() Override 한다.

toString()에서 평균은 소수이하 2째자리까지 한다.

생성자

setter / getter 메소드

calc() - 총점과 평균을 계산한다.

 

5. 입력, 출력, 수정, 삭제, 소트하는 클래스는 반드시 interface를 상속받는다.

 

6. menu() 작성한다.

만약에 1 ~ 6번 외의 숫자가 들어오면 "1~6중에 선택하세요" 메세지를 출력 후 다시 입력받는다.

1. 입력

2. 출력

3. 수정

4. 삭제

5. 정렬

6. 끝

 

7. SungJukInsert.java

- 번호, 이름, 국어, 영어, 수학를 입력하여 총점과 평균을 계산한다.

 

번호 입력 :

이름 입력 :

국어 입력 :

영어 입력 :

수학 입력 :

 

입력되었습니다

 

8. SungJukPrint.java

- ArrayList에 저장된 모든 데이터를 출력한다.

번호 이름 국어 영어 수학 총점 평균

 

9. SungJukUpdate.java

- 없는 번호가 입력되면 "잘못된 번호 입니다." 라고 출력한다.

- 있는 번호가 입력되면 번호에 해당하는 데이터를 출력 후 수정한다.

수정한 후에는 총점과 평균을 재계산해야 한다.

 

번호 입력 :

잘못된 번호 입니다.

 

또는

 

번호 이름 국어 영어 수학 총점 평균

 

수정 할 이름 입력 :

수정 할 국어 입력 :

수정 할 영어 입력 :

수정 할 수학 입력 :

 

수정하였습니다.

 

10. SungJukDelete.java

- 이름을 입력하여 없는 이름이면 "회원의 정보가 없습니다" 출력하시오

- 똑같은 이름이 있으면 모두 삭제한다.

 

삭제할 이름 입력 : 천사

회원의 정보가 없습니다

 

또는

 

x건의 항목을 삭제하였습니다.

 

11. SungJukSort.java

- menu() 작성한다.

 

********************

1. 총점으로 내림차순

2. 이름으로 오름차순

3. 이전 메뉴

********************

번호 :

 

SungJukDTO.java

package sungJuk;

public class SungJukDTO implements Comparable<SungJukDTO>{
	private int no, kor, eng, math, tot;
	private String name;
	private double avg;

	public SungJukDTO() {

	}
	
	public SungJukDTO(int no, int kor, int eng, int math, int tot) {
		this.no = no;
		this.kor = kor;
		this.eng = eng;
		this.math = math;
		this.tot = tot;
	}
	
	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public int getKor() {
		return kor;
	}

	public void setKor(int kor) {
		this.kor = kor;
	}


	public int getEng() {
		return eng;
	}

	public void setEng(int eng) {
		this.eng = eng;
	}

	public int getMath() {
		return math;
	}

	public void setMath(int math) {
		this.math = math;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getTot() {
		return tot;
	}
	
	public void setTot(int tot) {
		this.tot = tot;
	}
	
	public double getAvg() {
		return avg;
	}
	
	public void setAvg(double avg) {
		this.avg = avg;
	}
	
	public void calc() {
		tot = kor + eng + math;
		avg = tot / 3.0;
	}
	
	@Override
	public String toString() {
		return no + " " 
				+ name + "\t" 
				+ kor + "\t" 
				+ eng + "\t" 
				+ math + "\t" 
				+ tot + "\t" 
				+ String.format("%.2f", avg);
	}
	
	@Override
	public int compareTo(SungJukDTO sungJukDTO) {
		//총점으로 내림차순이므로 숫자를 바꾼경우 1 → -1
		if(this.tot > sungJukDTO.tot)
			return -1; 
		else if(this.tot < sungJukDTO.tot)
			return 1;
		else
			return 0;
	}
}
 

SungJukMain.java

package sungJuk;

public class SungJukMain {

	public static void main(String[] args) {
		SungJukService sungjukService = new SungJukService();
		sungjukService.menu();
		System.out.println("프로그램을 종료합니다.");
	}
}
 

SungJukService.java

package sungJuk;

import java.util.ArrayList;
import java.util.Scanner;

public class SungJukService {
	private ArrayList<SungJukDTO> list = new ArrayList<SungJukDTO>();
	
	public void menu() {
		Scanner scan = new Scanner(System.in);
		int num=0;
		SungJuk sungJuk = null;
		
		while(true) {
			System.out.println("1. 입력");
			System.out.println("2. 출력");
			System.out.println("3. 수정");
			System.out.println("4. 삭제");
			System.out.println("5. 정렬");
			System.out.println("6. 끝");
			System.out.print("번호 : ");
			num = scan.nextInt();
			System.out.println();
			
			if(num == 6)
				break;
			else if(num == 1)
				sungJuk = new SungJukInsert();
			else if(num == 2) 
				sungJuk = new SungJukPrint();				
			else if(num == 3) 
				sungJuk = new SungJukUpdate();				
			else if(num == 4) 
				sungJuk = new SungJukDelete();	
			else if(num == 5)
				sungJuk = new SungJukSort();	
			else {
				System.out.println("1~6중에 선택하세요");
				System.out.println();
				continue;
			}
			sungJuk.execute(list);
		}
	}
}
 

SungJuk.java

package sungJuk;

import java.util.ArrayList;

public interface SungJuk {
	public void execute(ArrayList<SungJukDTO> list);
}
 

SungJukInsert.java

package sungJuk;

import java.util.ArrayList;
import java.util.Scanner;

public class SungJukInsert implements SungJuk{
	
	public void execute(ArrayList<SungJukDTO> list) {
		Scanner scan = new Scanner(System.in);
		
		SungJukDTO sungJukDTO = new SungJukDTO();
		System.out.print("번호 입력 : ");
		sungJukDTO.setNo(scan.nextInt());
		
		System.out.print("이름 입력 : ");
		sungJukDTO.setName(scan.next());
		
		System.out.print("국어 입력 : ");
		sungJukDTO.setKor(scan.nextInt());
		
		System.out.print("영어 입력 : ");
		sungJukDTO.setEng(scan.nextInt());
		
		System.out.print("수학 입력 : ");	
		sungJukDTO.setMath(scan.nextInt());
		
		sungJukDTO.calc();
		
		list.add(sungJukDTO);
		System.out.println("입력되었습니다.");
		System.out.println();
	}
}
 

SungJukPrint.java

package sungJuk;

import java.util.ArrayList;

public class SungJukPrint implements SungJuk{

	@Override
	public void execute(ArrayList<SungJukDTO> list) {
		System.out.println("번호\t이름\t국어\t영어\t수학\t총점\t평균");
		for(SungJukDTO sungJukdto : list) {
			System.out.println(sungJukdto);
		}
		System.out.println();
	}
}
 

SungJukUpdate.java

package sungJuk;

import java.util.ArrayList;
import java.util.Scanner;

public class SungJukUpdate implements SungJuk{

	@Override
	public void execute(ArrayList<SungJukDTO> list) {
		Scanner scan = new Scanner(System.in);
		
		System.out.print("번호 입력 : ");
		int no = scan.nextInt();
		
		boolean sw = false; //★★★★★
		for(SungJukDTO sungJukDTO : list) {
			if(sungJukDTO.getNo() == no) {
				sw = true;
				
				System.out.println("번호\t이름\t국어\t영어\t수학\t총점\t평균");
				System.out.println(sungJukDTO);
				
				System.out.println("수정 할 이름 입력 : ");
				sungJukDTO.setName(scan.next());
				System.out.println("수정 할 국어 입력 : ");
				sungJukDTO.setKor(scan.nextInt());
				System.out.println("수정 할 영어 입력 : ");
				sungJukDTO.setEng(scan.nextInt());
				System.out.println("수정 할 수학 입력 : ");
				sungJukDTO.setMath(scan.nextInt());
				sungJukDTO.calc();
				System.out.println("수정하였습니다.");
				System.out.println();
				break;
			}
		}
	
 
if(!sw) { // sw == false 이렇게 하지말기.
			System.out.println("잘못된 번호입니다.");
			System.out.println();
		}
	}
}
 

여기서 boolean 값을 이용해야되는 이유

위에서 그냥 if-else문을 써버리면 찾는 no가 없으면 "잘못된 번호입니다."가 출력되고 또 찾았을 때 없으면 "잘못된 번호입니다."가 출력되고 그리고 나서야 찾으면 수정하는 코드가 나오게 된다.

그러므로 boolean 값을 이용해서해야 내가 찾는 번호를 찾았는지 안 찾았는지를 확인해서 올바르게 코드를 출력할 수 있다.

 


SungJukDelete.java

package sungJuk;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;

public class SungJukDelete implements SungJuk{

	@Override
	public void execute(ArrayList<SungJukDTO> list) {
		Scanner scan = new Scanner(System.in);
		String name;
		int count = 0;

		System.out.print("삭제할 이름 입력 : ");
		name = scan.next();

		/*
		for(int i=list.size()-1; i<=0; i--) {
			if(list.get(i).getName().equals(name)) {
				list.remove(i);
				count++;
			}
		}
		*/
 

위에 for문처럼 해서 거꾸로 찾는 이름이 있는지 확인하면 그나마 오류가 발생하는 것을 고칠 수 있는 것 같지만 위의 코드에도 약간의 문제가 있다.

그러므로 밑의 코드처럼 해야한다.

//★★★★★
		Iterator<SungJukDTO> it = list.iterator();
		while(it.hasNext()) { //현재 위치에 항목이 있습니까? true / false
			//항목을 꺼내고 임시공간에 보관하고, 다음으로 이동한다.
			SungJukDTO sungJukDTO = it.next();
			
			if(sungJukDTO.getName().equals(name)) {
				it.remove(); //it.next()가 반환한 항목을 제거한다.
				count++;
			}
		}
		
		if(count == 0) {
			System.out.println("회원의 정보가 없습니다");
			System.out.println();
		}
		else {
			System.out.println(count + "건의 항목을 삭제했습니다.");
			System.out.println();
		}
	}

}
 

위의 Iterator같은 경우에는 현재 위치에 항목이 있는지 확인을 하고 있으면 임시공간 즉, 버퍼에 저장을 한다. 그리고 그 다음 항목으로 넘어가게된다.

넘어가고 나서야 버퍼에 있는 이름과 내가 찾는 이름이 같은지를 비교를 하게된다.

비교를 했을 때 같으면 그 항목을 제거하게 된다.

 

[설명]

자바에서 ArrayList와 같은 컬렉션에서 요소를 삭제할 때,

for문 대신 Iterator를 사용해야 하는 이유는 ConcurrentModificationException 예외를 피하기 위해서이다.

 

 

  • for문을 사용한 삭제
for (int i = list.size() - 1; i >= 0; i--) {
    if (list.get(i).getName().equals(name)) {
        list.remove(i);
        count++;
    }
}
 

이 코드에서는 리스트의 크기와 인덱스를 직접 조작하면서 요소를 삭제하기 때문에 문제가 발생하지 않는다.

하지만 만약 인덱스를 0부터 시작하거나 리스트 크기가 동적으로 변경되는 상황에서는 ConcurrentModificationException이 발생할 수 있다.

 

 

  • Iterator를 사용한 삭제
Iterator<SungJukDTO> it = list.iterator();
while (it.hasNext()) {
    SungJukDTO sungJukDTO = it.next();
    if (sungJukDTO.getName().equals(name)) {
        it.remove();
        count++;
    }
}
 

위 코드에서 Iterator를 사용하여 요소를 삭제하면 ConcurrentModificationException이 발생하지 않는다.

그 이유는 Iterator의 remove() 메서드가 Iterator가 순회하고 있는 컬렉션에 안전하게 접근하여 요소를 삭제할 수 있기 때문이다.

 

 

[요약]

  • for문을 사용한 삭제: 요소를 삭제할 때 리스트의 크기와 인덱스를 직접 조작하므로 위험할 수 있다. 잘못된 접근으로 인해 ConcurrentModificationException이 발생할 수 있다.
  • Iterator를 사용한 삭제: 컬렉션을 순회하면서 안전하게 요소를 삭제할 수 있다. Iterator는 컬렉션의 구조가 변경되었을 때 이를 감지하고 적절히 처리다.

 

 

따라서, 컬렉션의 요소를 삭제할 때는 Iterator를 사용하는 것이 더 안전하고 권장되는 방법이이다.


SungJukSort.java

package sungJuk;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;

import collection.PersonDTO;

public class SungJukSort implements SungJuk{

	@Override
	public void execute(ArrayList<SungJukDTO> list) {
		Scanner scan = new Scanner(System.in);

		int num;

		while(true) {
			System.out.println("********************");
			System.out.println(" 1. 총점으로 내림차순");
			System.out.println(" 2. 이름으로 오름차순");
			System.out.println(" 3. 이전 메뉴");
			System.out.println("********************");
			System.out.println("   번호 :");
			num = scan.nextInt();
 
            if(num == 1) {
				Collections.sort(list); //원본을 바꿔줌 - 원본 유지 x
			}else if(num == 2) {
				//익명 inner class
				Comparator<SungJukDTO> com = new Comparator<SungJukDTO>() {
					@Override
					public int compare(SungJukDTO p1, SungJukDTO p2) {
						return p1.getName().compareTo(p2.getName());
					}
				};
				Collections.sort(list, com);
			}else if(num == 3) 
				//밑에처럼 하면 안 되는 이유: 클래스 새롭게 또 만들어짐. (초기화)
				//SungJukService sungjukService = new SungJukService();
				//sungjukService.menu();
				break;//나가서 제자리로 가야됨.

			System.out.println("번호\t이름\t국어\t영어\t수학\t총점\t평균");
			for(SungJukDTO sungJukdto : list) {
				System.out.println(sungJukdto);
			}
		}
	}
}
 

num == 1인 경우:

  • Collections.sort(list)를 사용하여 리스트를 기본 정렬 순서에 따라 정렬한다.
  • 기본 정렬 순서는 SungJukDTO 클래스의 compareTo 메서드에 정의되어 있으며, 총점(tot)을 기준으로 내림차순 정렬된다.

 

 

num == 2인 경우:

  • 익명 내부 클래스를 사용하여 Comparator<SungJukDTO>를 구현한다.
  • Comparator는 compare 메서드에서 두 SungJukDTO 객체의 name 필드를 비교하여 사전 순으로 정렬한다.
  • Collections.sort(list, com)를 사용하여 이 사용자 정의 정렬 순서를 적용한다.

 

이미 SungJukDTO에서 총점으로 내림차순하는 것으로 기준이 되어있기 때문에 이름으로 오름차순하기 위해서는 Comparator를 사용하여 구현해야한다.