
오늘 또 눈 때문에 병원을 가야 돼서 중간에 한 시간 수업을 못들었다. 과제 시간 만큼은 제대로 듣고 싶었는데 좀 슬펐다…
그리고 오늘 두번째 RBF를 했는데,
오늘 준비한 내용이 혼자 볼때는 잘 이해가 되지 않았지만 팀원분들이 그 부분에 설명을 추가해줘서 공부에 도움이 많이 됐다! Iterator를 사용하는 이유랑 for-each문과의 차이점이 잘 와닿지 않았었는데 이번에 제대로 정리해보니 머리에 더 잘 들어오는 느낌
매번 느끼지만 다들 정말 아는게 많으시다… 나는 암것도 몰라서 입을 움직일 수 가 없다..🥺아! 그리고 야구 개막해서 넘 행복하다🧡 🦅
OOP답게 클래스를 분리해서 개발하는게 좋다.
VO 와 DTO가 하는일은 비슷하다
Entity → 값을 담아오기 때문에 VO와 비슷한 일을 하긴 하지만 값을 옮겨주는 것도 가능 (하지만 분리해서 사용해도 된다.)
1단계 끝 - CRUD
ChatGPT보다 직접 서치하면서 공부하는게 실력향상에 도움이 많이 됨
열화판 파라미터
→ 똑같은 파리미터를 여러개 받을 수 있음
→ web에서는 변수를 중복해서 넣으면 list 형식으로 저장
startsWith() → 문자열이 어떤 문자로 시작하는지 확인하는 메서드split() → 매개변수에 두번째로 정수를 넣어주면 limit을 둠병원 갔다 오고 완전히 놓쳤다,, 올려주신 코드 보고 혼자 다시 해 봐야 될듯
Q1.
# a5 데이터베이스 삭제/생성/선택
DROP DATABASE a5;
CREATE DATABASE a5;
USE a5;
# 부서(dept) 테이블 생성 및 홍보부서 기획부서 추가
CREATE TABLE dept(
id INT(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
dept VARCHAR(10) NOT NULL UNIQUE
);
INSERT INTO dept (dept) VALUES
('홍보부서'),
('기획부서');
# 사원(emp) 테이블 생성 및 홍길동사원(홍보부서), 홍길순사원(홍보부서), 임꺽정사원(기획부서) 추가
CREATE TABLE emp (
id INT(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) NOT NULL,
dept_name VARCHAR(20) NOT NULL
);
INSERT INTO emp (name, dept_name) VALUES
('홍길동', '홍보부서'),
('홍길순', '홍보부서'),
('임꺽정', '기획부서');
# 홍보를 마케팅으로 변경
UPDATE emp
SET dept_name = '마케팅부서'
WHERE dept_name = '홍보부서';
# 마케팅을 홍보로 변경
UPDATE emp
SET dept_name = '홍보부서'
WHERE dept_name = '마케팅부서';
# 홍보를 마케팅으로 변경
UPDATE emp
SET dept_name = '마케팅부서'
WHERE dept_name = '홍보부서';
# 구조를 변경하기로 결정(사원 테이블에서, 이제는 부서를 이름이 아닌 번호로 기억)
ALTER TABLE emp DROP COLUMN dept_name;
ALTER TABLE emp ADD COLUMN dept_id INT(5) UNSIGNED NOT NULL;
UPDATE emp SET dept_id = (SELECT id FROM dept WHERE dept = '홍보부서') WHERE name IN ('홍길동', '홍길순');
UPDATE emp SET dept_id = (SELECT id FROM dept WHERE dept = '기획부서') WHERE name = '임꺽정';
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id);
# 사장님께 드릴 인명록을 생성
SELECT id AS 사원번호, name AS 이름, dept_id AS 부서번호 FROM emp;
# 사장님께서 부서번호가 아니라 부서명을 알고 싶어하신다.
# 그래서 dept 테이블 조회법을 알려드리고 혼이 났다.
SELECT * FROM dept;
# 사장님께 드릴 인명록을 생성(v2, 부서명 포함, ON 없이)
# 이상한 데이터가 생성되어서 혼남
SELECT e.id AS 사원번호, e.name AS 이름,
(SELECT dept FROM dept WHERE id = e.dept_id) AS 부서명
FROM emp e;
# 사장님께 드릴 인명록을 생성(v3, 부서명 포함, 올바른 조인 룰(ON) 적용)
# 보고용으로 좀 더 편하게 보여지도록 고쳐야 한다고 지적받음
SELECT e.id AS 사원번호, e.name AS 이름, d.dept AS 부서명
FROM emp e
JOIN dept d ON e.dept_id = d.id;
# 사장님께 드릴 인명록을 생성(v4, 사장님께서 보시기에 편한 칼럼명(AS))
SELECT
e.id AS '사원 번호',
e.name AS '이름',
d.dept AS '부서명'
FROM emp e
JOIN dept d ON e.dept_id = d.id;
→ 상당하다. 상당히 어렵다.
Q2. (시간 될 때마다.. 수정)
# a6 DB 삭제/생성/선택
# 부서(홍보, 기획, IT)
# 사원(홍길동/홍보/5000만원, 홍길순/홍보/6000만원, 임꺽정/기획/4000만원)
## IT부서는 아직 사원이 없음
# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력
# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력
## IT부서는 [IT, NULL, NULL] 으로 출력
# 전 사원에 대하여, [부서명, 사원번호, 사원명] 양식으로 출력
## IT부서는 [IT, 0, -] 으로 출력
# 모든 부서별, 최고연봉, IT부서는 0원으로 표시
# 모든 부서별, 최저연봉, IT부서는 0원으로 표시
# 모든 부서별, 평균연봉, IT부서는 0원으로 표시
| 메서드 | 기능 |
|---|---|
| hasNext() | - 다음 요소가 있으면 true, 없으면 false 반환 |
| next() | - 다음 요소를 반환 |
| - next()를 호출하기 전에 hasNext()를 통해 읽어올 다음 요소가 있는지 먼저 확인하는게 좋음 | |
| remove() | - next()를 통해 읽어온 객체를 삭제 |
| - next()를 호출한 다음에 remove()를 사용해야함 |
요소 접근
- Iterator는 요소에 접근할 때 next()를 사용하고, for-each는 콜론(:)으로 자동 접근
요소 삭제
- Iterator는 remove()로 요소 삭제 가능, for-each는 요소 삭제가 불가능
- 그렇기 때문에 컬렉션을 탐색하면서 요소를 삭제해야 될 때는 Iterator를 사용하는 것이 안전함
→ for-each문에서 요소를 삭제하면 ConcurrentModificationException이 발생할 수 있음
유연성
- Iterator는 Collection을 직접 수정하면서 순회가 가능, for-each는 Collection 수정이 불가능
1️⃣ Collection을 탐색하면서 요소를 삭제해야 할 때
2️⃣ Collection이 List가 아니라 Set이나 Map일 때
3️⃣ 동기화된 환경에서 컬렉션을 안전하게 순회할 때
→ 사실 3번은 잘 이해가 되지 않음..
Iterator와 Iterable은 역할과 사용하는 방식에 차이가 있음
Iterable은 Collection이 Iterator를 제공할 수 있도록 하는 인터페이스 → for-each문에서 사용이 가능하도록 함
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
public interface Iterable<T> {
Iterator<T> iterator();
}
3️⃣ 동기화된 환경에서 컬렉션을 안전하게 순회할 때
→ 사실 3번은 잘 이해가 되지 않음..
이후에 팀원분들이 3번에 대해 적어주신 내용
public E next() {
checkForComodification(); // 비 이터레이터에서 수정시 예외발생
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException(); //존재의미??
cursor = i + 1;
return (E) elementData[lastRet = i];
}
-----------------
// 위에 next() 메서드 설명 추가
public E next() {
checkForComodification(); // ✅ 리스트가 변경되었는지 검사 (동시 수정 예외 발생 가능)
int i = cursor; // ✅ 현재 위치(cursor)를 가져옴
if (i >= size) // ✅ 만약 cursor가 리스트 크기를 넘어서면 예외 발생
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData; // ✅ 내부 배열 참조
if (i >= elementData.length) // ✅ 리스트가 변경되어 길이가 줄어들었을 경우
throw new ConcurrentModificationException(); // ❌ 동시 수정 예외 발생!
cursor = i + 1; // ✅ 다음 요소로 이동
return (E) elementData[lastRet = i]; // ✅ 현재 요소 반환
}
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
new Thread(() -> {
try {
Thread.sleep(500); // 잠시 대기 후 6 추가
list.add(6);
// list.remove(list.size() - 1);
System.out.println("다른 스레드에서 요소 변경");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
try {
Thread.sleep(1000); // 순회 중 일부러 딜레이 줌
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
동기화된 컬렉션을 메서드 호출할 때는 상관없지만, 리스트를 순회하는 것은 명시적으로 동기화 블록(Synchronized) 사용해야함 -> Iterator자체는 Thread - Safe하지않다.
**ConcurrentModificationException 발생 ->** 컬렉션 구조적 변경이 발생함을 의미