람다식

 

Map 함수형인터페이스로 값 출력하기

// Map
		Map<String, String> map = new HashMap<String, String>();
		map.put("1", "1");
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", "4");

		// iterator 방식
		Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();

		while (entries.hasNext()) {
			Map.Entry<String, String> entry = entries.next();
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}

		System.out.println();

		// 향상된 for문
		for (Map.Entry<String, String> entry : map.entrySet()) {
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}
		
		System.out.println();
		
		// 함수형 인터페이스
		map.forEach((k,v) -> System.out.println(k + "," + v));

이어서 람다식을 사용해본다.

 

java.util.function 패키지 예제

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class LambdaTest4 {

	public static void main(String[] args) {
		Supplier<Integer> s = () -> (int) (Math.random() * 100) + 1;
		Consumer<Integer> c = i -> System.out.print(i + ", ");
		Predicate<Integer> p = i -> i % 2 == 0;
		Function<Integer, Integer> f = i -> i / 10 * 10; // i의 일의 자리를 없앤다.

		List<Integer> list = new ArrayList<>();
		makeRandomList(s, list);
		System.out.println(list);
		printEvenNum(p, c, list);
		List<Integer> newList = dosomthing(f, list); 
		System.out.println(newList);

	}

	// 메소드의 매개변수를 제한할 떄 제네릭 타입 사용하기
	// list에 나온 수들의 1의 자리를 없앤 값 newList에 넣기
	static <T> List<T> dosomthing(Function<T, T> f, List<T> list) {
		// ArrayList는 사이즈를 지정하지 않아도 기본 크기가 10
		// 크기를 지정해두면 속도가 좋아진다
		List<T> newList = new ArrayList<T>(list.size());

		for (T i : list) {
			newList.add(f.apply(i));
		}

		return newList;
	}

	// list에서 짝수만 출력하기
	static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
		System.out.print("[");
		
		for (T i : list) {
			if (p.test(i)) {
				c.accept(i);
			}
		}
		System.out.println("]");
	}

	// 100 이하의 랜덤 수 10개를 list에 넣기
	static <T> void makeRandomList(Supplier<T> s, List<T> list) {
		for (int i = 0; i < 10; i++) {
			list.add(s.get());
		}
	}
}

 

람다식의 메소드 참조 방식 (매개변수 생략, ::)

import java.util.function.BiFunction;
import java.util.function.Function;

public class LambdaTest5 {

	public static void main(String[] args) {
		// 람다식의 메소드 참조 방식 (매개변수 생략, ::)
		// 람다식이 하나의 메소드만 호출하는 경우에는 메소드 참조라는 방식으로 호출할 수 있다.

		// 문자열을 정수로 바꿔준다
		// Function<String, Integer> f = s -> Integer.parseInt(s);
		Function<String, Integer> f = Integer::parseInt; // 메소드 참조
		int i = f.apply("100");
		System.out.println(i + 100);

		// BiFunction<String, String, Boolean> f2 = (s1, s2) -> s1.equals(s2); 
		// 문자열이 같은지 비교
		BiFunction<String, String, Boolean> f2 = String::equals; // 메소드 참조
		boolean b = f2.apply("hello", "hello"); // 문자열이 같다
		System.out.println(b); // 문자열이 같으므로 true

	}

}

 

 

System.out.println() 의 메소드 참조 방식 

 // BiFunction<String, String, Boolean> f2 = (s1, s2) -> s1.equals(s2); // 문자열이 같은지 비교
		BiFunction<String, String, Boolean> f2 = String::equals; // 메소드 참조
		boolean b = f2.apply("hello", "hello"); // 문자열이 같다
		System.out.println(b); // 문자열이 같으므로 true
		
		ArrayList<Integer>list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		
		//list.forEach(i -> System.out.println(i));
		list.forEach(System.out :: println);

많이 쓴다!!

위 코드들은 베이스 코드. 응용에 앞서 베이스코드를 혼자 짤 수 있어야 한다.

대표하는 베이스 코드는 머리 속에 넣어둬야 한다. → 그냥 외워

 

Stream

: 데이터를 추상화(표준화) 하여 사용하기 편하게 연산하는 방식을 통일한다

 

배열을 스트림화

package Stream0529;

import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamTest {
	
	// Stream
	// 객체형식으로 되어있는 데이터를 다루는 변수 구조 : 배열, 컬렉션
	// 데이터를 추상화(표준화) 하여 사용하기 편하게 연산하는 방식을 통일한다
	// 중간연산, 최종연산
	
	// 배열을 스트림화 하는 3가지 방식
	
	Stream<String> stream1 = Stream.of("a", "b", "c"); // 가변인자
	Stream<String> stream2 = Stream.of(new String[] {"a", "b", "c"}); // 직접입력
	Stream<String> stream3 = Arrays.stream(new String[] {"a", "b", "c"}); // Arrays 사용
	
	// 기본형에 IntStream 으로 스트림화 하기
//	IntStream IntStream.of(int ...value);
//	IntStream intStream.of(int[]);
//	IntStream Arrays.stream(int[]);
			

}

 

연산자 : map(), filter(), distinct(). forEach() 사용

package Stream0529;

import java.io.File;
import java.util.stream.Stream;

public class StreamTest2 {

	public static void main(String[] args) {
		File[] fileArr = { new File("Ex1.java"), new File("Ex1.bak"), new File("Ex2.java"), new File("Ex1"),
				new File("Ex1.txt") };

//		File f1 = fileArr[0];
//		System.out.println(f1.getName()); // Ex1.java

		Stream<File> fileStream = Stream.of(fileArr);

		Stream<String> filenameStream = fileStream.map(File::getName);
		filenameStream.forEach(System.out::println);

		// 스트림을 다시 생성해서 초기상태로 만들어야한다.
		fileStream = Stream.of(fileArr);

		// 연산자 : map(), filter(), distinct(). forEach() 사용
		fileStream.map(File::getName) // Stream<File> -> Stream<String>
				.filter(s -> s.indexOf('.') != -1) // 확장자가 없는 것은 제외
				.map(s -> s.substring(s.indexOf('.') + 1)) // 확장자만 유출
				.map(String::toUpperCase) // 모두 대문자로 변환
				.distinct() // 중복제거
				.forEach(System.out::print); // JAVABAKTXT

		System.out.println();

	}

}

 

기본형을 스트림화

package Stream0529;

import java.util.stream.IntStream;

public class StreamTest3 {
	public static void main(String[] args) {

		IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
		intStream.forEach(System.out::println);

		System.out.println("============");
		IntStream.range(1, 5).forEach(System.out::println); // 마지막은 포함하지 않는다

		System.out.println("============");
		IntStream.rangeClosed(1, 5).forEach(System.out::println); // 마지막까지 포함된다

		System.out.println("============");
		IntStream.rangeClosed(1, 5).map(n -> n * n).filter(n -> n % 2 == 0).forEach(System.out::println);

		// sum(), average(), max(), min()

		System.out.println("============");

		// 1부터 5까지의 합
		int sum = IntStream.rangeClosed(1, 5).sum();
		System.out.println(sum);

		// 1부터 5까지의 합의 평균 구하기
		// getAsDouble() 실수형으로 값을 바꿔준다
		double avg = IntStream.rangeClosed(1, 5).average().getAsDouble();
		System.out.println(avg);

		// 최대값 구하기
		int max = IntStream.rangeClosed(1, 5).max().getAsInt();
		System.out.println(max);

		// 최소값 구하기
		int min = IntStream.rangeClosed(1, 5).min().getAsInt();
		System.out.println(min);

		// anyMatch()
		// 스트림의 요소 중 어느 하나라도 주어진 술어를 만족하면 'true'를 반환
		boolean any = IntStream.rangeClosed(1, 5).anyMatch(n -> n == 7);
		System.out.println(any);

	}
}

 

 

스트림 정렬

기본정렬 방식에 대한 정의가 클래스 안에 되어있어야 한다!

String 안에 기본 정렬(오름차순)이 있었던 것처럼!

 

정렬에 대해 복습하며 제네릭으로 바꿔보기)

더보기

제네릭

 

제네릭 적용 전

package come.list;

import lombok.AllArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@ToString
public class Student implements Comparable {
	String name;
	int ban;
	int no;
	int kor, eng, math;

	int getTotal() {
		return kor + eng + math;
	}

	float getAverage() {
		return (int) ((getTotal() / 3f) * 10 + 0.5) / 10f;
	}

	// 학생의 이름에 따라 정렬
	@Override
	public int compareTo(Object o) { // Object o -> Student

		if (o instanceof Student) {
			Student other = (Student) o; // 값으로 가지고 와서 사용할 수 있다.

			// return this.no - other.no; // 오름차순
			// return other.no - this.no; // 내림차순
			return this.name.compareTo(other.name); // 오름차순
			// return this.name.compareTo(other.name) * -1; // 내림차순
		}
		return -1;
	}
}

 

 

제네릭 적용 후

public class Student implements Comparable<Student> {
	String name;
	int ban;
	int no;
	int kor, eng, math;

	int getTotal() {
		return kor + eng + math;
	}

	float getAverage() {
		return (int) ((getTotal() / 3f) * 10 + 0.5) / 10f;
	}

	// 학생의 이름에 따라 정렬
	@Override
	public int compareTo(Object o) { // Object o -> Student

			return this.name.compareTo(s.name); // 오름차순
			
		}
}

 

 

 

스트림으로 정렬해보기

package Stream0529;

public class Student implements Comparable<Student>{
	
	String name;
	int ban;
	int totalScore;

	public Student(String name, int ban, int totalScore) {
		this.name = name;
		this.ban = ban;
		this.totalScore = totalScore;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", ban=" + ban + ", totalScore=" + totalScore + "]";
	}
	
	String getName() {
		return name;
	}
	
	int getBan() {
		return ban;
	}
	
	int getTotalScore() {
		return totalScore;
	}
	
	@Override
	public int compareTo(Student s) {
		// TODO Auto-generated method stub
		return s.totalScore - this.totalScore; // 총점으로 내림차순
	}

}
package Stream0529;

import java.util.Comparator;
import java.util.stream.Stream;

public class StreamTest4 {

	public static void main(String[] args) {
		// 가변형 방식의 스트림
		
		Stream<Student> studentStream = Stream.of(
				new Student("남파이", 3, 142),
				new Student("강자바", 3, 298),
				new Student("최타임", 2, 90),
				new Student("코틀린", 5, 273),
				new Student("크립트", 7, 194),
				new Student("이루비", 2, 210),
				new Student("안쿼리", 1, 198)
				);
		
		//studentStream.sorted(Comparator.comparing((Student s) -> s.getBan()))
		studentStream.sorted(Comparator.comparing(Student::getBan))
		.forEach(System.out::println);

	}

}

 

결과 :

Student [name=안쿼리, ban=1, totalScore=198]
Student [name=최타임, ban=2, totalScore=90]
Student [name=이루비, ban=2, totalScore=210]
Student [name=남파이, ban=3, totalScore=142]
Student [name=강자바, ban=3, totalScore=298]
Student [name=코틀린, ban=5, totalScore=273]
Student [name=크립트, ban=7, totalScore=194]

반을 기준으로 오름차순 되어 나온다.

스레드

 

wait()와 notify()로 스레드 이해하기

→ 이 방법을 권장하고 있다.

→ resume()은 suspend()는 내부에 동기화가 되어있지 않아 교착상태가 발생함.

wait()와 notify()는 순서대로 작업하게 되어있다.

 

 

wait()와 notify()를 이용한 프로그램

public class ThreadWaitTest {

	public static void main(String[] args) throws InterruptedException {
		// wait()와 notify()
		// 고객 2명 스레드, 요리사 스레드 2개를 만들 예정.
		// 고객 2명이 하는 일 - 음식먹기 , 요리사 하는 일 - 한번에 최대 6개 음식 만들기
		// wait()는 따로 관리되는 wait pool이 있음, 그 곳에 들어가면 일시정지
		// wait 되어지는 스레드가 notify를 해줌. (고객이 음식을 다먹어서 먹을 음식이 없으면 요리사에게 음식이 없다고 알림)
		// table 클래스를 만들어 그 위에 음식 6개를 올려둘 수 있게 함.

		Table table = new Table();

		new Thread(new Cook(table), "COOK1").start(); // 0.001초 단위로 음식을 만든다
		new Thread(new Customer(table, "donut"), "CUST1").start(); // 0.01초 단위로 음식을 먹는다
		new Thread(new Customer(table, "burger"), "CUST2").start();

		Thread.sleep(100); // 0.1초 뒤에
		System.exit(0); // 프로그램 강제 종료

	}

}
import java.util.ArrayList;

public class Table {
	String[] dishNames = { "donut", "dount", "burger" };

	final int MAX_FOOD = 6;

	private ArrayList<String> dishes = new ArrayList<String>();

	// 음식을 만드는 기능
	public void add(String dish) {
		if (dishes.size() >= MAX_FOOD) {
			return;
		}

		dishes.add(dish);
		System.out.println("Dishes : " + dishes.toString());
	}

	// true는 먹을 음식이 있다, false는 먹을 음식이 없다는 뜻
	public boolean remove(String dishName) {
		for (int i = 0; i < dishes.size(); i++) {
			if (dishName.equals(dishes.get(i))) {
				dishes.remove(i);
				return true;
			}
		}
		return false;
	}
	
	public int dishNum() {
		return dishNames.length; // 배열의 크기
	}
}
public class Cook implements Runnable{
	// 요리사, 랜덤으로 음식을 만든다 
	
	private Table table;

	public Cook(Table table) {
		super();
		this.table = table;
	}

	@Override
	public void run() {
		while(true) {
			int idx = (int)(Math.random() * table.dishNum());
			table.add(table.dishNames[idx]);
			
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {

			}
		}
	}
	
}
public class Customer implements Runnable {

	private Table table;
	private String food;

	public Customer(Table table, String food) {
		super();
		this.table = table;
		this.food = food;
	}

	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {

			}
			
			String name = Thread.currentThread().getName();
			
			if (eatFood()) {
				System.out.println(name + " ate a " + food);
			} else {
				System.out.println(name + " failed to " + food);
			}
		}
	}
	
	boolean eatFood() {
		return table.remove(food);
	}

}

 

결과)

Exception in thread "COOK1" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
	at java.base/java.util.AbstractCollection.toString(AbstractCollection.java:456)
	at Table.add(Table.java:17)
	at Cook.run(Cook.java:16)
	at java.base/java.lang.Thread.run(Thread.java:840)

 

= 오류가 난 이유 : 동시에 작업이 일어났기 때문이다

Exception이 발생, 이는 요리사(Cook) 쓰레드가 테이블에 음식을 놓는 도중에, 손님(Customer) 쓰레드가 음식을 가져가려했기 때문에 발생하는 예외

이런 예외가 발생하는 이유는 여러 쓰레드가 테이블(자원)을 공유하는데 동기화를 하지 않았기 때문이다.

또한 스레드마다 화면에 출력되는 순서도 뒤죽박죽이다. 이렇게 자원을 공유하는 경우 반드시 동기화가 필요하다.

이 밖에도 고객1과 고객2가 동시에 먹으려 하면 오류가 생긴다.

 

= 없는 위치에서 데이터를 삭제하려했다

= indexoutofboundsexception 오류

 

private ArrayList<String> dishes = new ArrayList<String>();

이 부분을 동시에 사용하려 하기 때문에 발생하는 오류.

 

실제 ArrayList를 쓰는 것은 add, remove

때문에 한번에 하나의 스레드만 데이터를 처리할 수 있도록 lock을 걸어야한다.

 

→ 해결방법 : synchronized로 동기화 처리를 한다.

 

요리사가 6개의 음식을 만들면 일시정지 해야 함.

wait()와 notify()를 통해 일시정지 시킨다.

반드시 동기화 치리 되어진 메소드 안에서만 사용할 수 있다.

synchronized 때문에 생긴 기아 현상

wait() 실행되면 락이 풀린다.

 

모든 문제를 해결한 코드)

public class ThreadWaitTest {

	public static void main(String[] args) throws InterruptedException {
		// wait()와 notify()
		// 고객 2명 스레드, 요리사 스레드 2개를 만들 예정.
		// 고객 2명이 하는 일 - 음식먹기 , 요리사 하는 일 - 한번에 최대 6개 음식 만들기
		// wait()는 따로 관리되는 wait pool이 있음, 그 곳에 들어가면 일시정지
		// wait 되어지는 스레드가 notify를 해줌. (고객이 음식을 다먹어서 먹을 음식이 없으면 요리사에게 음식이 없다고 알림)
		// table 클래스를 만들어 그 위에 음식 6개를 올려둘 수 있게 함.

		Table table = new Table();

		new Thread(new Cook(table), "COOK1").start(); // 0.001초 단위로 음식을 만든다
		new Thread(new Customer(table, "donut"), "CUST1").start(); // 0.01초 단위로 음식을 먹는다
		new Thread(new Customer(table, "burger"), "CUST2").start();

		Thread.sleep(2000); // 0.1초 -> 5초 -> 2초로 수정
		System.exit(0); // 프로그램 강제 종료

	}

}
import java.util.ArrayList;

public class Table {
	String[] dishNames = { "donut", "dount", "burger" };

	final int MAX_FOOD = 6;

	private ArrayList<String> dishes = new ArrayList<String>();

	// 음식을 만드는 기능
	public synchronized void add(String dish) { // 해당 메소드를 동기화 처리

		// 요리사가 6개의 요리를 다 만들고 나면 할 일이 없어짐
		// 스레드의 일시 정지 -> wait() 사용
		// 음식이 모자라면 다시 일 할 수 있도록 하기
		while (dishes.size() >= MAX_FOOD) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "is wating.");

			try {
				wait(); // 일시정지, COOK 스레드를 기다리게 한다.
				Thread.sleep(500); // 0.5초 동안 sleep
			} catch (InterruptedException e) {
			}

		}

		dishes.add(dish);
		notify(); // 기다리고 있는 CUST를 깨우기 위함.
		System.out.println("Dishes : " + dishes.toString());

//		if (dishes.size() >= MAX_FOOD) {
//			return;
//		}
//
//		dishes.add(dish);
//		System.out.println("Dishes : " + dishes.toString());
	}

	// true는 먹을 음식이 있다, false는 먹을 음식이 없다는 뜻
	public void remove(String dishName) {

		String name = Thread.currentThread().getName();

		synchronized (this) { // 객체 자체를 동기화 처리, 범위가 넓다

			// 누가 기다리고 있는지 확인
			while (dishes.size() == 0) {

				System.out.println(name + " is waiting.");
				try {
					wait();
					Thread.sleep(500); // 0.5초 기다리고 있다.
				} catch (InterruptedException e) {

				}
			}

			while (true) {
				for (int i = 0; i < dishes.size(); i++) {
					if (dishName.equals(dishes.get(i))) {
						dishes.remove(i);
						notify(); // 잠자고 있는 COOK을 꺠우기 위함.
						return;
					}
				}

				try {
					System.out.println(name + " is waiting.");
					wait();
					Thread.sleep(500); // 0.5초 기다리고 있다.
				} catch (InterruptedException e) {

				}
			}
		}

		// return false;
	}

	public int dishNum() {
		return dishNames.length; // 배열의 크기
	}
}

 

public class Cook implements Runnable{
	// 요리사, 랜덤으로 음식을 만든다 
	
	private Table table;

	public Cook(Table table) {
		super();
		this.table = table;
	}

	@Override
	public void run() {
		while(true) {
			int idx = (int)(Math.random() * table.dishNum());
			table.add(table.dishNames[idx]);
			
			try {
				Thread.sleep(10); // 0.001초 -> 0.1초 -> 0.001초
			} catch (InterruptedException e) {

			}
		}
	}
	
}
public class Customer implements Runnable {

	private Table table;
	private String food;

	public Customer(Table table, String food) {
		super();
		this.table = table;
		this.food = food;
	}

	@Override
	public void run() {
		while (true) {
			try {
				Thread.sleep(100); // 고객이 먹는 속도 : 0.1초
			} catch (InterruptedException e) {

			}

			String name = Thread.currentThread().getName();

			table.remove(food);

			System.out.println(name + " ate a " + food);

//			if (eatFood()) {
//				System.out.println(name + " ate a " + food);
//			} else {
//				System.out.println(name + " failed to " + food);
//			}
		}
	}

//	boolean eatFood() {
//		return table.remove(food);
//	}
}
ㅍ

 

결과)

(예시)

Dishes : [burger]
Dishes : [burger, burger]
Dishes : [burger, burger, donut]
Dishes : [burger, burger, donut, dount]
Dishes : [burger, burger, donut, dount, burger]
Dishes : [burger, burger, donut, dount, burger, dount]
COOK1 is wating.
CUST1 ate a donut
Dishes : [burger, burger, dount, burger, dount, dount]
CUST1 is waiting.
CUST2 ate a burger
CUST1 is waiting.
CUST2 ate a burger
Dishes : [dount, burger, dount, dount, dount]
CUST1 is waiting.
CUST2 ate a burger
Dishes : [dount, dount, dount, dount, dount]

 

 

람다식

= 이름없는 메소드라고 표현

함수형 인터페이스를 사용해야 사용 가능.

 

표현방식)

public class LambdaTest {
	// 람다식(Lambda expression)
	// 메소드를 하나의 식으로 표현한 것
	// 이름 없는 메소드
	// 람다는 하나의 객체다
	
	// 기존의 식
	int max(int x, int y) {
		return x > y ? x : y;
	}

	// 람다식
	(int x, int y) -> {return x > y ? x : y;}
	
	(int x, int y) -> x > y ? x : y
			
	(x, y) -> x > y ? x : y // 타입이 없어도 상관없다
			
	(x) -> x * x; 
	
	x -> x * x; // 매개변수가 하나인 경우 괄호 생략 가능
	
}

함수형 인터페이스 → 람다식에 이름을 부여하는 역할.

 

public class LambdaTest {
	// 람다식(Lambda expression)
	// 메소드를 하나의 식으로 표현한 것
	// 이름 없는 메소드
	// 람다는 하나의 객체다
	
	// 기존의 식
//	int max(int x, int y) {
//		return x > y ? x : y;
//	}

	// 람다식
//	(int x, int y) -> {return x > y ? x : y;}
//	
//	(int x, int y) -> x > y ? x : y
//			
//	(x, y) -> x > y ? x : y // 타입이 없어도 상관없다
//			
//	(x) -> x * x; 
//	
//	x -> x * x; // 매개변수가 하나인 경우 괄호 생략 가능
//	
	//(x, y) -> x > y ? x : y
	
	// 함수형 인터페이스
	// 오버라이딩 목적이 아니다
	// 람다식에 이름을 부여하는 역할
	// 반드시 하나의 추상메소드만 갖는다
	
	@FunctionalInterface 
	interface MyFuntion {
		// 추상메소드(람다식의 형식에 맞게 구성한다.)
		// 추상 메소드의 선언부를 여기에 정의한다
		public abstract int max(int x, int y);
	}
	
	MyFuntion f = (x, y) -> x > y ? x : y; // 객체
	// 하나의 객체로 사용할 수 있다. 메소드 자체를 하나의 값으로 사용
	
	f.max(10, 20) // 림디식의 호출방식
}

FunctionalInterface → 추상메서드가 딱 하나만 존재하는 인터페이스

 

익명클래스 : 인터페이스를 이용해서 객체를 생성하는 방식

public class LambdaTest2 {

	static void execute(MyFunction f) {
		f.run();
	}

	static MyFunction getMyFunction() {
		MyFunction f = () -> System.out.println("f3.run()");
		return f;
	}

	public static void main(String[] args) {
		MyFunction f1 = () -> System.out.println("f1.run()");

		// 익명클래스 : 인터페이스를 이용해서 객체를 생성하는 방식
		MyFunction f2 = new MyFunction() { // 익명 클래스
			// 부모타입으로 참조변수 선언
			// 람다식과 비슷하다

			@Override
			public void run() {
				System.out.println("f2.run()");
			}
		};
		
		MyFunction f3 = getMyFunction();
		
		f1.run(); // f1.run();
		f2.run(); // f2.run();
		f3.run(); // f3.run();
		
		// 람다식의 주소값을 넘긴다
		execute(f1); // f1.run();
		execute(() -> System.out.println("run()")); // run()
	}

}
@FunctionalInterface // 함수형 인터페이스
public interface MyFunction {
	void run();
}

메소드를 하나의 데이터처럼 사용할 수 있다.

 

iterator 방식과 향상된 for문, 람다식을 이용한 출력

import java.security.AlgorithmConstraints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.lang.model.element.NestingKind;
import javax.security.auth.x500.X500Principal;

public class LambdaTest3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		ArrayList<Integer> list = new ArrayList<Integer>();

		for (int i = 0; i < 10; i++) {
			list.add(i);
		}

		// iterator 방식
		Iterator<Integer> iterList = list.iterator();

		while (iterList.hasNext()) {
			System.out.print(iterList.next()); // 0123456789
		}

		System.out.println();

		// 향상된 for문
		for (Integer i : list) {
			System.out.print(i); // 0123456789
		}

		System.out.println();

		// 함수형 인터페이스 = 람다식을 받겠다
		// 함수형 인터페이스에는 추상메서드가 정의되어있다
		// 람다식을 받은 후에는 accept 로 받아서 쓴다
		list.forEach(i -> System.out.print(i)); // 0123456789

		// removeIf 조건에 맞는 값을 삭제하겠다
		// 2의 배수 또는 3의 배수를 삭제
		list.removeIf(x -> x % 2 == 0 || x % 3 == 0);
		System.out.println(list); // [1, 5, 7]

		// replaceAll 값을 찾아서 바꿔준다
		list.replaceAll(i -> i * 10);
		System.out.println(list); // [10, 50, 70]

		// Map
		Map<String, String> map = new HashMap<String, String>();
		map.put("1", "1");
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", "4");

		// iterator 방식
		Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();

		while (entries.hasNext()) {
			Map.Entry<String, String> entry = entries.next();
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}

		System.out.println();

		// 향상된 for문
		for (Map.Entry<String, String> entry : map.entrySet()) {
			System.out.println("key : " + entry.getKey() + ", value :" + entry.getValue());
		}

	}

}

 

Map를 이용해 값을 불러오는 것도 잘 나왔다.

key : 1, value :1
key : 2, value :2
key : 3, value :3
key : 4, value :4

지난 시간에 이어 스레드에 대해 알아본다.

스레드 : 한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위

 

pc, 휴대폰에 설치된 상태 → 프로그램

실행하는 상태 → 프로세스 (하드웨어의 자원을 사용한다.)

멀티프로세스 → 여러 개의 프로세스

일정 시간동안 한 스레드가 작업을 실행하고 나면 다른 스레드가 와서 작업을 실행(스위칭) → 빠른 작업으로 사용자는 동시적으로 스레드가 돌아간다고 생각함.

 

실제 스레드를 구현해 본다.

public class ThreadTest1 {

	public static void main(String[] args) {
		// 스레드를 만들어보자

		// 스레드를 만드는 두가지 방법
		// Thread 클래스
		// Runable 인터페이스

		// Theard 특징
		// 1. 순서대로 실행되지 않음
		// 2. 끝나는 순서도 정해져있지 않음.

		// (하나의 큰 프로그램 안에 작은 프로그램이 독립적으로 돌아가는 상태)

		// Thread 클래스의 생성방식
		ThreadEx1_1 t1 = new ThreadEx1_1();

		// Runable 인터페이스의 생성방식
		Runnable r = new ThreadEx1_2();
		Thread t2 = new Thread(r);

		t1.start(); // start()는 새로운 스텍을 만들고 그 안에 run()메소드를 넣어서 관리
		t2.start(); // 다 실행되고 나면 각 스텍 안에 main 하나 돌고, run() 두 개가 각각 돌아감.
		// main 스레드는 무조건 대기 상태를 거쳐야함. run() 스레드가 작업을 마칠 때까지.
		// 스레드는 동작할 때 순서가 없다! 자원을 먼저 점유하면 실행이 되어짐.
		// 중간에 스위칭(물리적 교환)이 일어남.

//		t1.run(); // run으로 호출하면 스레드 클래스가 되는 것이 아님.
//		t2.run();
	}
}

 

public class ThreadEx1_1 extends Thread{
	// Thread 클래스
	// run()메소드를 오버라이딩 해야 한다. 
	// 이 클래스 자체는 독립적으로 돌아가는 하나의 프로그램

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			// getName()은 스레드에서 상속 받음
			System.out.println(getName());
		}
	}	
}
public class ThreadEx1_2 implements Runnable{
	// Runable 인터페이스
	// run()메소드를 오버라이딩 해야 한다. 

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			// 인터페이스이기 때문에 getName()을 바로 가지고 올 수가 없다. 
			// currentThread()를 이용해서 스레드에서 getName()을 가지고 온다.
			System.out.println(Thread.currentThread().getName());
		}		
	}
}

 

스레드를 호출할 때 run()이 아닌 start()를 써야하는 이유 :

start() 메소드를 호출하면 JVM이 새로운 스레드를 생성하고 그 스레드에서 run() 메소드를 실행하게 됩니다. 이렇게 하여 병렬 처리가 가능해집니다. 반면에 run() 메소드를 직접 호출하면 현재 스레드에서 run() 메소드가 실행되며, 이는 새로운 스레드를 생성하지 않고 순차적으로 실행됩니다. 따라서 start()를 호출해야 실제로 멀티스레딩을 이용할 수 있습니다.

(노션 AI 참조)

 

 

멀티스레드를 만들어보자.

import javax.swing.JOptionPane;

public class ThreadTest2 {

	public static void main(String[] args) {

		// JOptionPane 값을 입력하지 않으면 대기
		// 값을 입력하면 실행

		Thread2_1 th1 = new Thread2_1();
		th1.start();

		// 멀티 스레드 방식으로 만들기
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요");
		System.out.println("입력하신 값은 " + input + "입니다.");

	}
}
public class Thread2_1 extends Thread {

	@Override
	public void run() {
		for (int i = 10; i > 0; i--) {
			System.out.println(i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}
}

숫자를 세는 것과는 상관없이 두개의 프로그램이 돌아간다.

 

아직 작업이 끝나지 않고 동작하고 있을 때 main 스레드는 대기하고 있다.

모든 작업이 끝나면 main 스레드도 종료된다.

 

스레드는 시작과 끝을 요청할 순 있지만 언제 시작할지, 끝낼지는 관여할 수 없다.

스레드 운영은 자바 버츄얼 머신이 담당.

생성   →                    실행대기 → 실행 (시간이 되면 다시 실행대기 상태로) → 소멸
                         
                         (RUNNABLE)
(New) →          start() → yield() → stop()                 → (TERMINATED)
                 스레드를 양보하고 싶을 때 yield()

(자바의 정석 13장 참조)

 

 

스레드의 제어를 해보자.

→ 스레드 시작과 끝 요청하기

언제 스레드를 시작하고 종료할지 알수가 없다.

하지만 스레드를 대기 상태를 만들어 제어할 수 있는 방법이 있다. → sleep() 사용

 

public class ThreadTest3 {

	public static void main(String[] args) {
		// 스레드 제어
		ThreadEx3_1 th1 = new ThreadEx3_1();
		ThreadEx3_2 th2 = new ThreadEx3_2();
		
		th1.start();
		th2.start();
		
		// sleep 사용 시 예외처리를 해야 한다.
		try {
			Thread.sleep(2000);  // 현재 sleep 메소드를 호출한 Thread를 일시 정지시킨다.
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		System.out.println("<<main종료>>");

	}

}

 

public class ThreadEx3_1 extends Thread{

	@Override
	public void run() {
		for (int i = 0; i < 300; i++) {
			System.out.print("-");
		}
		
		System.out.println("<<th1 종료>>");
	}

}
public class ThreadEx3_2 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 300; i++) {
			System.out.print("|");
		}

		System.out.println("<<th2 종료>>");
	}

}

 

public class ThreadEx3_1 extends Thread{

	@Override
	public void run() {
		for (int i = 0; i < 300; i++) {
			System.out.print("-");
		}
		
		System.out.println("<<th1 종료>>");
	}

}
public class ThreadEx3_2 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 300; i++) {
			System.out.print("|");
		}

		System.out.println("<<th2 종료>>");
	}

}

 

 

main종료가 2초 뒤에 뜬다.

2초 동안 sleep 함수로 main 스레드를 대기 시켰기 때문이다.

sleep은 sleep을 호출한 메서드 자체를 sleep 시켜준다.

 

 

interrupt() 강제로 실행대기 상태를 만들어주는 메소드

1. interrupt()
- interrupted 속성 (boolean 타입) : interrupt() 호출되어지면 변수의 값이 변경된다.
- 함수의 기본값 false -> interrupt()가 사용되어지면 true로 바뀐다.

// isinterrupted()
// interrupted 속성값을 반환한다.

2. interrupt()
- InterruptedException 타입의 예외를 발생 시킨다.
-sleep(), wait(), join() 일시정지된 스레드를 실행 대기 상태로 만든다.

 

 

import javax.swing.JOptionPane;

public class ThreadTest4 {

	public static void main(String[] args) {
		// 1. interrupt() 
			// interrupted 속성 (boolean 타입) : interrupt() 호출되어지면 변수의 값이 변경된다.
			// 함수의 기본값 false -> interrupt()가 사용되어지면 true로 바뀐다.	
		// isinterrupted()
			// interrupted 속성값을 반환한다.
		
		// 2. interrupt()
			// InterruptedException 타입의 예외를 발생 시킨다.
			// sleep(), wait(), join() 일시정지된 스레드를 실행 대기 상태로 만든다. 
		
		ThreadEx4_1 th1 = new ThreadEx4_1();
		th1.start();
		
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요");
		System.out.println("입력하신 값은 " + input + "입니다.");
		
		th1.interrupt();  // interrupted 속성 true로 변경
		System.out.println("isInterrupted() : " + th1.isInterrupted());
	}

}
public class ThreadEx4_1 extends Thread {

	@Override
	public void run() {
		int i = 10;

		// isInterrupted()는 interrupted한 속성값을 반환한다. -> false
		while (i != 0 && !isInterrupted()) {
			System.out.println(i--);
			for (long x = 0; x < 950000000000000000L; x++) {

			}
		}
		
		System.out.println("카운트가 종료되었습니다.");
	}

}

중간에 값을 입력하면 즉시 종료된다.

 

 

public class ThreadEx4_1 extends Thread {

	@Override
	public void run() {
		int i = 10;

		// isInterrupted()는 interrupted한 속성값을 반환한다. -> false
		while (i != 0 && !isInterrupted()) {
			System.out.println(i--);
//			for (long x = 0; x < 950000000000000000L; x++) {

//			}
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				
			}
		}
		
		System.out.println("카운트가 종료되었습니다.");
	}

}

중간에 값을 입력해도 즉시 종료되지 않고 카운트가 돌아간다.

 

자세하게 알아보기 :

import javax.swing.JOptionPane;

public class ThreadTest4 {

	public static void main(String[] args) {
		// 1. interrupt() 
			// interrupted 속성 (boolean 타입) : interrupt() 호출되어지면 변수의 값이 변경된다.
			// 함수의 기본값 false -> interrupt()가 사용되어지면 true로 바뀐다.	
		// isinterrupted()
			// interrupted 속성값을 반환한다.
		
		// 2. interrupt()
			// InterruptedException 타입의 예외를 발생 시킨다.
			// sleep(), wait(), join() 일시정지된 스레드를 실행 대기 상태로 만든다. 
		
		ThreadEx4_1 th1 = new ThreadEx4_1();
		th1.start();
		
		String input = JOptionPane.showInputDialog("아무 값이나 입력하세요");
		System.out.println("입력하신 값은 " + input + "입니다.");
		
		System.out.println("isInterrupted() : " + th1.isInterrupted()); // false
		
		th1.interrupt();  // interrupted 속성 true로 변경
		System.out.println("isInterrupted() : " + th1.isInterrupted()); // true, 카운트가 멈춤
		System.out.println("isInterrupted() : " + th1.isInterrupted()); // InterruptedException(예외) 발생 됨, false로 바뀜
		System.out.println("isInterrupted() : " + th1.isInterrupted()); // InterruptedException(예외) 발생 됨, false로 바뀜
	}

}
public class ThreadEx4_1 extends Thread {

	@Override
	public void run() {
		int i = 10;

		// isInterrupted()는 interrupted한 속성값을 반환한다. -> false
		while (i != 0 && !isInterrupted()) {
			System.out.println(i--);
//			for (long x = 0; x < 950000000000000000L; x++) {
//
//			}
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// interrupt(); 이 생략되어 있다 // interrupted 속성값을 false로 자동 초기화
				System.out.println("InterruptedException 발생 됨");
			}
		}
		
		System.out.println("카운트가 종료되었습니다.");
	}

}

 

 

suspend()와 resume() 으로 스레드 이해하기

  • resume()은 suspend()를 실행 대기 상태로 만든다.
public class ThreadTest5 {

	public static void main(String[] args) {
		// suspend()와 resume()으로 스레드 이해하기
		// 둘은 짝꿍

		RunImpl5_1 r = new RunImpl5_1();

		Thread th1 = new Thread(r, "*");
		Thread th2 = new Thread(r, "**");
		Thread th3 = new Thread(r, "***");

		// 아래 3개와 메인스레드 하나, 총 4개의 스레드가 돌아감.
		th1.start();
		th2.start();
		th3.start();

		try {
			Thread.sleep(2000); // main thread 2초 동안 일시정지
			th1.suspend(); // th1 thread를 일시정지 상태로 만들어라, 2초 후 실행
			Thread.sleep(2000);
			th2.suspend();
			Thread.sleep(3000);
			th1.resume();
			Thread.sleep(3000);
			th1.stop();
			th2.stop();
			Thread.sleep(2000);
			th3.stop();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
public class RunImpl5_1 implements Runnable {

	@Override
	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {

			}
		}
	}
}

 

 

먼저 점유한 스레드가 작업을 마무리할 수 있도록 하는 것 → 동기화

synchronized 를 사용해 계좌 입출금 프로그래밍을 만들어 동기화작업 알아보기

public class ThreadTest6 {

	public static void main(String[] args) {
		RunnableEx6_1 r = new  RunnableEx6_1();
		
		new Thread(r).start(); 
		new Thread(r).start();
		// -100, -200 등 - 가 나온다.
		// 조건문을 통과한 상태에서 sleep()이 실행되기 떄문에 동시에 처리된 것 처럼 보여짐.
		// 서로 경쟁적으로 자원 사용
		// 이는 synchronized 사용으로 해결할 수 있다. (동기화 처리)
	}

}
public class Account {
	
	private int balance = 1000;
	
	public int getBalance() {
		return balance;

	}
	
	// synchronized 앞에 있는 작업이 마무리 될 때까지 기다려라
	public synchronized void withdrew(int money) {
		if (balance >= money) {
			try {
				Thread.sleep(1000); 
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			balance -= money;
		}
	}

}

Thread의 동기화는 메소드에 적용한다.

 

public class RunnableEx6_1 implements Runnable{
	// 돈을 빼는 사람

	Account acc = new Account(); // 멤버변수는 공유 되어진다.
	
	@Override
	public void run() {
		while(acc.getBalance() > 0) {
			// 100, 200, 300
			int money = (int)(Math.random() *3 +1) * 100;
			acc.withdrew(money);
			System.out.println("balance : " + acc.getBalance());
		}
	}

}

 

 

synchronized는 이렇게도 사용할 수 있다.

public class Account {

	private int balance = 1000;

	public int getBalance() {
		return balance;

	}

	// synchronized 앞에 있는 작업이 마무리 될 때까지 기다려라
	public void withdrew(int money) {
		synchronized (this) {
			if (balance >= money) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				balance -= money;
			}

		}
	}

}

 

wait()와 notify()로 스레드 이해하기

→ 이 방법을 권장하고 있다.

→ resume()은 suspend()는 내부에 동기화가 되어있지 않아 교착상태가 발생함.

wait()와 notify()는 순서대로 작업하게 되어있다.

Generics / Generic

 

클래스 맴버의 타입을 컴파일 시에 정의하는 방법이다.

사용가능한 데이터의 타입을 제한한다.

특정타입으로 데이터를 쓸 수 있게 해준다.

타입의 안정성 → 형변환 불필요

 

 

파일 생성 시 이름 : Box<T>

public class Box<T> {
	// 클래스명 -> 원시타입이라고 한다
	// <T> -> 제네릭스타입
}

 

제네릭타입 사용 전

// 일반적인 사용 
// 타입을 정한 상태

	String item;
	
	void setItem(String item) {
		this.item = item;
	}
	
	String getItem() {
		return item; 
	}

 

제네릭타입 사용

// 제네릭클래스 
// 타입이 정해지지 않은 상태

	T item;
	
	void setItem(T item) {
		this.item = item;
	}
	
	T getItem() {
		return item; 
	}

 

BoxTest에서 타입을 지정해 제네릭클래스를 사용하는 예시

public class Box<T> {
	// 클래스명 -> 원시타입이라고 한다
	// <T> -> 제네릭스타입
	
	// 제네릭스(generic)
	// 클래스 맴버의 타입을 컴파일 시에 정의하는 방법 
	// 미리 타입을 지정하는 게 아니라 필요할 때 타입을 정의함.
	// 타입의 안정성을 주기 위해 사용
	// <T> (제네릭스 타입) 은 참조형만 가능 = class 타입만 가능
	
	
	// 제네릭클래스 
	// 타입이 정해지지 않은 상태
	T item;
	
	void setItem(T item) {
		this.item = item;
	}
	
	T getItem() {
		return item; 
	}
}
public class BoxTest {

	public static void main(String[] args) {
		// Box 클래스를 생성
		// 타입을 지정
		Box<String> box = new Box<String>();
		
		box.setItem("Hello");
		System.out.println(box.getItem()); // Hello

	}

}

 

 

Box 클래스에서 만든 제네릭타입을 ArrayListTest 에서 사용

import java.util.ArrayList;

public class Box<T> {
	ArrayList<T> list = new ArrayList<T>();
}
import java.util.ArrayList;

public class ArrayListTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayList<String> list = new ArrayList<String>();

		list.add("1");
		list.add("2");
		list.add("3");

		// 타입 불일치
		// list.add(new Integer(1)); // 타입제한이 생겨 오류가 남

		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i)); // 1, 2, 3
		}

	}

}

 

형변환을 할 필요가 없다!

Test 클래스들에서 이렇게 타입을 지정해주는 것을 ‘구체화(Specialization)’라고 한다.

int형, double형 받을 수 없음.

→ Integer형, Double형 가능

Integer형이어도 자바의 오토박싱과 언박싱 덕분에 연산 시 기본형(int) 로 변환된다.

 

 

 

 

박스에 물건을 담는 프로그래밍

- List 를 사용해 제네릭 클래스를 적용하는 방법

 

박스에 담을 수 있는 물건 4개 -> Fruit, Apply, Grape, Toy

사과와 포도는 fruit을 상속, Toy는 상속 X

 

 

물건을 담을 박스

import java.util.ArrayList;

public class Box<T> {
	// 클래스명 -> 원시타입이라고 한다
	// <T> -> 제네릭스타입
	
	// 제네릭스(generic)
	// 클래스 맴버의 타입을 컴파일 시에 정의하는 방법 
	// 미리 타입을 지정하는 게 아니라 필요할 때 타입을 정의함.
	// 타입의 안정성을 주기 위해 사용
	// <T> (제네릭스 타입) 은 참조형만 가능 = class 타입만 가능
	
	
	// 제네릭클래스 
	// 타입이 정해지지 않은 상태
	// T에 들어가는 타입으로 타입 제한
	
	// 물건을 담을 박스(공간)
	ArrayList<T> list = new ArrayList<T>();
	
	// 박스에 물건 담기
	void add(T item) {
		list.add(item);
	}
	
	// 박스에서 물건 꺼내기
	T get(int i) {
		return list.get(i);
	}
	
	// 박스 사이즈
	int size() {
		return list.size();
	}
	
	// 박스 안에 물건이 몇개인지 
	public String toString() {
		return list.toString();
	}
}

 

박스에 담을 물건들)

apple

public class Apple extends Fruit {
	
	public String toString() {
		return "Apple";
	}
    
}

 

grape

public class Grape extends Fruit {
	
	public String toString() {
		return "Grape";
	}

}

 

toy

public class Toy {
	
	public String toString() {
		return "Toy";
	}

}

 

Fruit

public class Fruit {

	public String toString() {
		return "Fruit";
	}
	
}

 

 

박스에 물건 담기

public class FruitTest {

	public static void main(String[] args) {

		// 물건을 담을 박스 만들기
		// 어떤 물건을 담겠디고 제한해야함.

		Box<Fruit> fruitBox = new Box<Fruit>();
		Box<Apple> appleBox = new Box<Apple>();
		Box<Toy> toyBox = new Box<Toy>();
		// Box<Grape> grapeBox = new Box<apple>(); // 타입불일치

		fruitBox.add(new Fruit());
		fruitBox.add(new Apple()); // 상속관계에 의한 다형성 적용으로 데이터를 담을 수 있다

		appleBox.add(new Apple());
		// appleBox.add(new Toy()); // 타입 불일치

		toyBox.add(new Toy());
		// toyBox.add(new Apple()); // 타입 불일치

		System.out.println(fruitBox); // [Fruit, Apple]
		System.out.println(appleBox); // [Apple]
		System.out.println(toyBox); // [Toy]

	}
}

 

 

 

제네릭타입의 범위를 제한

- 제네릭타입의 범위를 제한하는 방법을 알아보기 위한 클래스와 인터페이스

 

Eatable

public interface Eatable {
	// 타입을 이용한 다형성 구조를 위한 용도
	// 내용 없음
}

 

FruitBox

public class FruitBox<T extends Fruit & Eatable> extends Box<T>{
// 제네릭한 클래스를 상속받는 클래스도 제네릭 해야 한다.
	
	
	// T라는 타입을 제네릭하게 쓰려면 반드시 Fruit를 상속해야한다 -> 범위 지정
	//public class FruitBox<T extends Fruit> extends Box<T>
	
	// 추가적인 제한
	// 인터페이스와 클래스를 구분하지 않고 extends를 쓴다
	// <T extends Fruit & Eatable>
	
	
	
	// 제네릭 타입의 범위를 제한하는 클래스
}

 

 

Toy를 제외하고 Fruit 상속

Apple

public class Apple extends Fruit {
	
	public String toString() {
		return "Apple";
	}

}

 

Grape

public class Grape extends Fruit {
	
	public String toString() {
		return "Grape";
	}

}

 

Toy

public class Toy {
	
	public String toString() {
		return "Toy";
	}

}

 

제네릭타입의 범위를 제한해서 박스에 물건담기

public class FruitTest2 {

	public static void main(String[] args) {

		FruitBox<Fruit> fruitbox = new FruitBox<Fruit>();
		FruitBox<Apple> applebox = new FruitBox<Apple>();
		FruitBox<Grape> grapebox = new FruitBox<Grape>();
		// FruitBox<Toy> toybox = new FruitBox<Toy>(); // 상속 관계가 아니기 때문에 불가능 -> 타입 불일치

		fruitbox.add(new Fruit());
		fruitbox.add(new Apple());
		fruitbox.add(new Grape());
		applebox.add(new Apple());
		// applebox.add(new Grape()); // 타입 불일치
		grapebox.add(new Grape());
		// toybox 는 생성 자체가 불가능

		System.out.println(fruitbox); // [Fruit, Apple, Grape]
		System.out.println(applebox); // [Apple]
		System.out.println(grapebox); // [Grape]
	}

}

 

 

 

Map 를 사용해 제네릭 클래스를 적용하는 방법

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class HashMapTest {

	public static void main(String[] args) {
		// Map 를 사용해 제네릭 클래스를 적용하는 방법
		
		Map<String, String> cities = new HashMap<>();
		cities.put("서울","대한민국");
		cities.put("도쿄","일본");
		cities.put("베이징","중국");
		cities.put("워싱턴","미국");
		cities.put("브라질리아","브라질");
		
		Iterator<Map.Entry<String, String>> it = cities.entrySet().iterator();
		
		// Map.Entry => getKey(), getValue()  제네릭타입
		
		while(it.hasNext()) {
			Map.Entry<String,String> entry = it.next(); // 반환타입 Map.entry<String, String>
			System.out.println("key : " + entry.getKey() + ", value : " + entry.getValue());
		}
		
	}

}

 

HashMap<K, V> → 이렇게 자체적으로 제네릭하게 정의되어 있음.

HashMap<String, String> 이렇게 지정해주면 K = String , V = String 으로 쓸 수 있다.

 

 

Iterator 가 데이터를 읽어오는 방식 → hasNext(), next()

Iterator 는 인터페이스, 역시 제네릭하게 정의되어 있다.

 

 

next() → 반환타입이 Map.entry 데이터를 직접 받아오는게 아님.

Map.entry의 getKey(), getValue() 로 가지고 올 수 있는 Key, Value

 

 

모든 객체가 화면에 찍힐 때는 toString()이 생략되어짐. 내부에 toString()이 없어도 Object에서 상속.

String, Integer 는 내부에 toString()이 정의되어있기 때문에 String, Integer 타입은 오버라이딩이 된다.

 

// 향상된 for문
		for (Map.Entry<String, String> entry :cities.entrySet()) {
			System.out.println("key : " + entry.getKey() + ", value : " + entry.getValue());
		}
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class HashMapTest {

	public static void main(String[] args) {
		// Map 를 사용해 제네릭 클래스를 적용하는 방법

		Map<String, String> cities = new HashMap<>();
		cities.put("서울", "대한민국");
		cities.put("도쿄", "일본");
		cities.put("베이징", "중국");
		cities.put("워싱턴", "미국");
		cities.put("브라질리아", "브라질");

		// 향상된 for문
		for (Map.Entry<String, String> entry : cities.entrySet()) {
			System.out.println("key : " + entry.getKey() + ", value : " + entry.getValue());
		}

	}

}

 

 

 


 

 

스레드 : 한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위

 

pc. 휴대폰에 설치된 상태 → 프로그램

실행하는 상태 → 프로세스 (하드웨어의 자원을 사용한다.)

멀티프로세스 → 여러 개의 프로세스

 

스레드의 개념

: 스레드는 한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위입니다.
각 스레드는 독립적인 실행을 위한 자신만의 프로그램 카운터, 레지스터 집합, 그리고 스택을 갖습니다.
하나의 프로세스 내의 스레드들은 해당 프로세스의 코드, 데이터 및 기타 운영 체제 자원들을 공유하게 됩니다.
스레드는 프로세스 내에서 병렬 작업 수행을 가능하게 하므로, 복잡한 통신을 필요로 하는 여러 분야에서 유용하게 사용됩니다.

 

  • 싱글스레드  

: 하나의 프로세스가 하나의 작업만을 처리하는 것을 싱글 스레드라고 합니다. 한 번에 하나의 명령어만을 처리할 수 있으며, 작업을 순차적으로 진행하게 됩니다. 싱글 스레드의 경우, 특정 작업이 끝나야 그 다음 작업이 시작될 수 있습니다. 이런 방식은 프로그램의 흐름을 쉽게 이해할 수 있게 해줍니다. 그러나, 한 작업이 오래 걸리는 경우 다음 작업이 지연될 수 있어 효율성이 떨어질 수 있습니다.

  • 멀티스레드 (대부분의 프로그램 → 동시다발적으로 사용해야하는 프로그램)

: 멀티스레드란 하나의 어플리케이션에서 두 개 이상의 스레드를 동시에 실행하는 것을 의미합니다. 각각의 스레드는 독립적으로 실행되며, 자신만의 작업을 수행합니다. 이를 통해 하나의 프로세스 내에서 여러 작업을 동시에 처리할 수 있습니다. 멀티스레딩은 CPU의 이용률을 향상시키고, 사용자와의 상호작용성을 높이며, 병렬 작업을 가능하게 합니다. 하지만, 스레드 간의 동기화와 데이터 공유 문제를 해결해야 합니다.

 

스레드, 싱글스레드, 멀티스레드 개념 출저 : 노션 AI

 

 

Map

key - value 값 속성

key 는 중복허용 X

value 는 중복허용 O

유일한 값을 관리할 때 사용 (회원 아이디)

그렇기 때문에 이걸 이용해 로그인 프로그래밍을 해볼 것이다.

 

key 값과 value 값 일치하는지 알아보기 - 로그인 프로그래밍

package come.map;

import java.util.HashMap;
import java.util.Scanner;

public class HashMapTest {

	public static void main(String[] args) {

		HashMap map = new HashMap();
		// map.put(key, value);
		map.put("myId", "1234");
		map.put("asdf", "1111");
		map.put("asdf", "1234");

		// System.out.println(map); // {myId=1234, asdf=1234}
		// 중복 시 마지막 값을 사용한다
		// System.out.println(map.get("myId")); // key를 이용해서 value를 반환한다

		// 로그인 기능 만들기

		// 아이디와 비밀번호 받기
		Scanner sc = new Scanner(System.in);

		while (true) {
			System.out.println("id와 password를 입력하세요");
			System.out.print("id >> ");
			String id = sc.nextLine();

			System.out.print("password >> ");
			String passsword = sc.nextLine();

			System.out.println();

			// 키 값이 일치 체크
			if (!map.containsKey(id)) {
				// 로그인 실패 시
				System.out.println("입력하신 id는 존재하지 않습니다.");
				continue;
			} else {
				// 아이디 일치 시

				// 비밀번호가 일치하지 않을 떄
				if (!(map.get(id)).equals(passsword)) {
					System.out.println("비밀번호가 일치하지 않습니다.");
				} else {
					System.out.println("id와 password가 일치합니다.");
					break;
				}
			}
		}

	}

}

 

 

Iterator, key 값 가지고 오기

package come.map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapTest2 {

	public static void main(String[] args) {
		Map map = new HashMap();

		map.put("김자바", new Integer(90));
		map.put("안자바", new Integer(100));
		map.put("이파리", new Integer(90));
		map.put("이파리", new Integer(40));
		map.put("노드주니어", new Integer(100));

		// key
		// value
		// key, value 동시에 가지고 올 수 있다.

		// 반복자패턴 사용 = Iterator 를 사용
		// map은 Iterator 를 바로 사용할 수 없음, Set 이용

		Set set;
		// key

//		set = map.keySet();
//		System.out.println(set); // [안자바, 김자바, 이파리, 노드주니어]
		// 값이 순서대로 나오지 않는다.

		// Iterator를 이용해 키값을 이용해 value 값 가지고 오기
		Iterator it = map.keySet().iterator();
		while(it.hasNext()) {
			String key = (String) it.next();
			System.out.println("key : " + key + " , " + map.get(key)); 
			// key 는 중복허용이 안되기 때문에 중복된 이름은 나오지 않음.
			// 중복된 값들 중 마지막 값이 나옴
		}

 

 

key, value 을 동시에 읽어오기 위한 map의 인터페이스

	public interface Map<K, y> {
		interface Entry<k, y> {
			K getKey();
			V getValue();

		}
	}

 

 

key, value 을 동시에 읽어오기

package come.map;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapTest2 {

	public static void main(String[] args) {
		Map map = new HashMap();

		map.put("김자바", new Integer(90));
		map.put("안자바", new Integer(100));
		map.put("이파리", new Integer(90));
		map.put("이파리", new Integer(40));
		map.put("노드주니어", new Integer(100));

		// key
		// value
		// key, value 동시에 가지고 올 수 있다.

		// 반복자패턴 사용 = Iterator 를 사용
		// map은 Iterator 를 바로 사용할 수 없음, Set 이용

		Set set;
		
		// key, value 을 동시에 읽어오기
		
		// Map 인터페이스 안에 인터페이스가 정의되어 있다. -> 내부인터페이스라고 함
		// K getKey(); , V getValue(); 를 이용해서 key와 value를 읽어올 것.
		
		Iterator it = map.entrySet().iterator();
		
		while (it.hasNext()) {
			Map.Entry e = (Map.Entry)it.next(); // 내부 인터페이스 Map.Entry를 반환
			// Entry 안에 getKey 와 getValue가 있음.
			System.out.println("이름 : " + e.getKey() + ", 점수 : " + e.getValue());
		}

}
}

 

 

 


갑자기 수업하다 자바에서 라이브러리를 인식 못하는 상황!!!! 수업중에 이런일이 생기다니 정말 울고 싶었다..

 

이런 오류가 발생..

강사님께 여쭤보니 라이브러리 인식 문제라고 하셨다. 수업을 계속 해야했기 때문에.. 일단 다른 프로젝트를 만들어 파일을 실행했다.

 

 

일반적으로 Build path 에 들어가서 라이브러리를 관리해주는 식으로 문제를 해결하던데 난 그것도 안됐다.

코드에도 어디에도 아무 문제가 없는데 왜 저런 오류가 뜨는지 모르겠다.

결국 수업은 따라가야하니.. 새로운 프로젝트를 만들어서 파일을 옮겼다. 이것도 해결이라고 할 수 있을까 ㅠ


 

 

파일과 연결해서 Key 와 Value 값 가지고 오기

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class PropertiesTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Properties prop = new Properties();
		
		String inputFile = "src/input.txt";
		
		try {
            prop.load(new FileInputStream(inputFile));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
		
		System.out.println(prop);
		
		String name = prop.getProperty("name");
		String[] data = prop.getProperty("data").split(",");
		
		System.out.println(Arrays.toString(data));
		
		
		int max = 0;
		int min = 0;
		int sum = 0;
		double avg = 0;
		
		
		int[] nums = new int[data.length];
		
		for (int i = 0; i < nums.length; i++) {
			nums[i] = Integer.parseInt(data[i]);
		}
		
		for (int i = 0; i < nums.length; i++) {
			int sValue = nums[i]; // 기준값
			if (i == 0) max = min = sValue;
			
			if (max < sValue) {
				max = sValue;
			} else if (min > sValue){
				min = sValue;
			}
			
			sum += sValue;
		}
		
		avg = (sum * 100.0 / nums.length) / 100;
		
		
		System.out.println("이름 :" + name);
		System.out.println("최대값 :" + max);
		System.out.println("최소값 :" + min);
		System.out.println("합계 :" + sum);
		System.out.println("평균 :" + avg);
	}

}

 

input.txt 파일의 내용

# 이름과 점수입니다.
name = hong gill dong
data = 7,23,56,75

 

 

배열을 정렬하기

package come.set;

import lombok.AllArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@ToString
public class Student implements Comparable {
	String name;
	int ban;
	int no;
	int kor, eng, math;

	int getTotal() {
		return kor + eng + math;
	}

	float getAverage() {
		return (int) ((getTotal() / 3f) * 10 + 0.5) / 10f;
	}

	// 학생의 이름에 따라 정렬
	@Override
	public int compareTo(Object o) { // Object o -> Student

		if (o instanceof Student) {
			Student other = (Student) o; // 값으로 가지고 와서 사용할 수 있다.

			return this.name.compareTo(other.name); // 오름차순
		}
		// TODO Auto-generated method stub
		return -1;

	}

}

 

 

배열의 정보가 들어있는 main class

package come.set;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class StudentTest {

	public static void main(String[] args) {

		ArrayList list = new ArrayList();

		list.add(new Student("홍길동", 1, 21, 100, 98, 78));
		list.add(new Student("남궁성", 2, 3, 90, 100, 100));
		list.add(new Student("강자바", 3, 10, 99, 59, 84));
		list.add(new Student("콩자반", 9, 7, 84, 65, 96));
		list.add(new Student("고투홈", 2, 1, 65, 89, 55));

		// System.out.println(list);

		Collections.sort(list);
		// System.out.println(list);

		Iterator it = list.iterator();

		while (it.hasNext()) {
			Student s = (Student) it.next();
			System.out.println(s.toString());
		}

	}

}

 

 

collectionProject0523 < come.builder 롬복에 대해 알아봄

 

Member.java

package come.builder;

import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

// 롬북사용법
//@Getter
//@Setter
////@NoArgsConstructor
//@RequiredArgsConstructor

@Data  // 모두 포함

@Builder

public class Member {
	
	private String id;
	private String name;
	private final String age;

}

 

UserInforLombok.java

package come.builder;

import lombok.Builder;
import lombok.Data;

@Data  
@Builder
public class UserInforLombok {
	private String firstName;
	private String secondName;
	@Builder.Default
	private int age = 30; 
	
	public String getUserinfo() {
		return String.format("firstName: %s, secondName: %s, age: %s", firstName, secondName, age);
	}
}

 

UserInforLombokTest.java

package come.builder;

import lombok.Builder;

public class UserInforLombokTest {

	public static void main(String[] args) {
		UserInforLombok userInforLombok = UserInforLombok.builder()
				.firstName("lombok 적용")
				.secondName("객체생성")
				.build();
		
		System.out.println(userInforLombok.getUserinfo()); // firstName: lombok 적용, secondName: 객체생성, age: 0
		// 빌더패턴을 사용하면 명시적 초기화가 먹히지 않는다.
		// 무조건 0을 기본값으로 보냄
		// 명시적인 값을 꼭 사용해야 한다면 해당 값 위에
		// @Builder.Default 를 써야한다. 

	}

}

 

 

 

내일은 피그마를 활용한 디자인 시험..!

프로토타입, 인터럭션 동작 해야함.

이용약관, 로그인 성공 등등 화살표

국비 수업을 통해 내가 얻어갈 것들.. 

언어 : java, html, css, javascript, ajax, spring & spring boot, jpa, mybaits, oracle, android, Git, GitHub,Hibernate

tools : eclipe, intellij, sqldeveloper,tomcat, visual studio, android studio, git, source tree


오늘 수업하며 내가 잘 이해 못하고 있는 것

  • 형변환 활용 (다음에 이해한다 하며 넘김..)
  • 오버라이딩 (이해한다고 착각했다)
  • Iterator (아예 이해 X)

 

✨ int, integer 차이점 정리

 

- int : 자료형(primitive type)

  • 산술 연산 가능함
  • null로 초기화 불가

 

- Integer : 래퍼클래스(Wrapper class)

  • Unboxing 하지 않을 시 산술 연산 불가능
  • null값 처리 가능

boxing : primitive type -> wrapper class 변환 (int to Integer)

unboxing: wrapper class -> primitive type 변환 ( Integer to int)

 


리스트의 특성을 따르는 자료구조, 스택과 큐

 

Queue 에 대해 알아본다.

 

Stack 구조

들어 오는 곳 나가는 곳이 한 곳, LIFO (후입선출): 가장 늦게 들어온 것이 가장 먼저 사용된다

(class 제공이 됨)

 

Queue 구조

들어오는 곳, 나가는 곳 따로 있음, FIFO (선입선출) : 먼저 들어온 것이 먼저 나온다

(인터페이스)

 

Queue 선언

Queue<Integer> q = new LinkdeList<>();

Integer형

 

  • Queue 값 추가

add() : 해당 큐의 맨 뒤에 값 삽입, 값 추가 성공 시 true 반환, 큐가 꽉 찬 경우 에러발생

offer() : 해당 큐의 맨 뒤에 값 삽입, 값 추가 성공 시 true 반환, 값 추가 실패 시 false 반환

 

  • Queue 값 제거

remove() : 큐 맨 앞에 있는 값 반환 후 삭제, 비어있는 경우 에러

poll() : 큐 맨 앞에 있는 값 반환 후 삭제, 비어있는 경우 null 반환

clear() : 큐 비우기 (전체 삭제)

 

  • Queue 맨 앞의 값 확인

element() : 큐의 맨 앞의 값을 반환, 비어있는 경우 에러발생

peek() : 큐의 맨 앞의 값을 반환, 비어있는 경우 null 반환

 

offer(), poll()

package come.list;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

public class QueueTest {

	public static void main(String[] args) {
		
		// Queue
		// FIFO (선입선출)
		Queue q = new LinkedList();
		
		q.offer("0");
		q.offer("1");
		q.offer("2");
		
		while (!q.isEmpty()) {
			System.out.println(q.poll());
		}
	}

}

 

브라우저의 히스토리를 저장하는 방식 → 스택을 쓰는 것이 합리적.

이렇게 계속 고민을 해야 함. 스택을 쓸지 큐를 쓸지.

 

브라우저 히스토리를 저장하는 프로그램 만들기

package come.list;

import java.util.Stack;

public class StackExplore0522 {

	public static Stack back = new Stack();
	public static Stack forward = new Stack();
	

	public static void main(String[] args) {
		
		// 브라우저 히스토리 관리 프로그램 (데이터 주고 받기)
		// 스택을 두개 써야함. 
		// 사이트를 방문하기 이전 정보를 관리, 사이트를 방문한 후 정보를 관리
		// back, forward
		// backStack 의 가장 상단 url 주소가 현재 실행 중인 사이트
		// 중간 단계에서 새로운 사이트로 이동할 경우 forward 스택에 있던 모든 정보는 삭제
		
		goURL("1.네이트");
		goURL("2.카카오");
		goURL("3.네이버");
		goURL("4.구글");
		
		printStatus(); 
		
		// 이전가기
		goBack(); // back :[1.네이트, 2.카카오, 3.네이버]
		System.out.println("이전가기 클릭");
		printStatus(); // forward :[4.구글]
		
		goBack(); // back :[1.네이트, 2.카카오]
		System.out.println("이전가기 클릭");
		printStatus(); // forward :[4.구글, 3.네이버]
		
		// 다음가기
		goForward(); // back :[1.네이트, 2.카카오, 3.네이버]
		System.out.println("다음가기 클릭");
		printStatus(); // forward :[4.구글]
		
		goURL("https://nomadcoders.co"); // back :[1.네이트, 2.카카오, 3.네이버, https://nomadcoders.co]
		System.out.println("새로운 사이트로 이동");
		printStatus(); // forward :[]
		
	}
	
	// 새로운 주소를 입력했을 때 
	public static void goURL(String url) {
		back.push(url); 
		// 스택이 비어있지 않다면 (새롭게 주소를 입력하는 순간) 다 지워라
		if (!forward.empty()) {
			forward.clear(); 
		}
	}
	
	// 출력 함수
	public static void printStatus() {
		System.out.println("back :" + back); 
		System.out.println("forward :" + forward); 
		// 현재 실행되는 화면 정보
		// peek() 가장 상단의 정보를 읽어옴.
		System.out.println("현재 실행 중인 사이트는 " + back.peek() + " 입니다.");
	}
	
	// 새로운 브라우저 실행 이전으로 옮겨주는 것
	// 백스택에서 정보를 옮겨야 함. 동시에 백스택에서 데이터 정보 지우기
	public static void goBack() {
		if (!back.empty()) {
			forward.push(back.pop());
			// forward에 백의 정보 넣기, 동시에 백 정보 삭제
		}
	}
	
	// 다음으로 가기
	// forward 정보 끄집어 내 back 에 담고, forward 에서는 삭제
	public static void goForward() {
		if (!forward.empty()) {
			// back에 값을 넣는 동시에 forward 값 삭제
			back.push(forward.pop());
		}
	}

}

어제 복습한 함수를 다 써봤다.

이젠 점점 문제를 이해하는 게 더 어려워진다..; 이래서 진짜 문제를 많이 푸는 게 중요한듯!

 

 

정렬에 대해 배우기 앞서 Iterator 소개)

Iterator
컨테이너,리스트를 순회할 수 있게 해준다
인터페이스)

 

Iterator의 장점

  1. 컬렉션에서 요소를 제어하는 기능
  2. next() 및 previous()를 써서 앞뒤로 이동하는 기능
  3. hasNext()를 써서 더 많은 요소가 있는지 확인하는 기능

출저) 

 

[JAVA] Iterator란? Iterator를 사용하는 이유와 사용법

먼저 iterator와 원형인 iterate의 사전적 정의가 무엇인지 확인해보자. iterate : (계산, 컴퓨터 처리 절차를) 반복하다 iterator : 반복자 계산 같은 컴퓨터의 작업 처리 절차를 반복한다는 뜻 같다. 그럼

onlyfor-me-blog.tistory.com

 

 

정렬

 

중요한 내용, 잘 이해해야 함수 배울 때 사용한다.

 

sort()를 이용한 정렬

String 클래스는 미리 오름차순 정렬을 하기로 정의되어있음.

→ 각 클래스에는 정렬 방식이 저장되어 있다.

package come.list;

import java.util.Arrays;

public class ComperatorTest0522 {

	public static void main(String[] args) {
		// 정렬에 대해 배운다
		
		// 문자열 대소비교 (사전의 알파벳 순서)
		String[] strArr = {"cat", "Dog", "Lion", "tiger"};
		System.out.println(Arrays.toString(strArr)); // [cat, Dog, Lion, tiger]
		
		// Arrays 클래스 : 배열 정리
		// 그 중 sort() : 오름차순
		
		// 오름차순 배열 정렬
		Arrays.sort(strArr); // String -> 기본정렬 방식이 정의가 되어있음
		System.out.println(Arrays.toString(strArr)); // [Dog, Lion, cat, tiger]
		
	}

}

 

정렬과 관련되어 사용 되어지는 인터페이스

Comperable 인터페이스와 Comperator 인터페이스가 있음.

sort 로 정렬 → 클래스에 기본 정렬 방식이 정의가 되어있어야 한다.

기본 정렬 방식이 정의 되어 있지 않은 경우 → Comperable , Comperator

 

 

// 오름, 내림

Comperable : 기본 정렬을 구현할 때 사용하는 인터페이스

                      int compareTo(Object o1. Object o2)

                      오버라이딩 시 양수 or 0 or 음수를 리턴하도록 한다  (두 수의 차를 구한다)

 

Comperator : 정렬의 기준과 방식을 변경하고 싶을 때 사용하는 인터페이스

                      int compare(Object o)

                      오버라이딩 시 양수 or 0 or 음수를 리턴하도록 한다  (두 수의 차를 구한다)

* 기본 정렬은 주로 오름차순을 나타낸다.

 

양수가 리턴 → 앞의 값이 큰 것

0 → 같다

음수 → 뒤의 값이 큰 것

 

오름차순 정렬

// Descending = 내림차순
class Descending implements Comparator{

	// 오름차순
	@Override
	public int compare(Object o1, Object o2) {
		if (o1 instanceof String && o2 instanceof String) {
			String s1 = (String)o1;
			String s2 = (String)o2;
			
			return s1.compareTo(s2); // 양수 , 0 , 음수
		}
		return 0;
	}

 

내림차순 정렬

package come.list;

import java.util.Arrays;
import java.util.Comparator;

public class ComperatorTest0522 {

	public static void main(String[] args) {
		// 정렬에 대해 배운다
		
		// 문자열 대소비교 (사전의 알파벳 순서)
		String[] strArr = {"cat", "Dog", "Lion", "tiger"};
		System.out.println(Arrays.toString(strArr)); // [cat, Dog, Lion, tiger]
		
		// Arrays 클래스 : 배열 정리
		// 그 중 sort() : 오름차순
		
		// 오름차순 배열 정렬
		Arrays.sort(strArr); // String -> 기본정렬 방식이 정의가 되어있음
		System.out.println(Arrays.toString(strArr)); // [Dog, Lion, cat, tiger]
		
		// 내림차순 배열 정렬
		// sort(값,  Comperator 타입의 객체)
		// 이 객체의 정렬 방식이 Descending 이 정의해놓은대로 정렬이 된다
		Arrays.sort(strArr, new Descending());  
		System.out.println(Arrays.toString(strArr)); // [tiger, cat, Lion, Dog]
		
		// Comperable 인터페이스와
		// Comperator 인터페이스가 있음.
		// 정렬과 관련되어 사용되어지는 인터페이스	
		
	}

}

// Descending = 내림차순
class Descending implements Comparator{

	@Override
	public int compare(Object o1, Object o2) {
		if (o1 instanceof String && o2 instanceof String) {
			String s1 = (String)o1;
			String s2 = (String)o2;
			
			// return s1.compareTo(s2); -> 오름차순
			// 값의 순서만 바꾸면 된다.
			return s2.compareTo(s1); // -> 내림차순 
			// 양수 , 0 , 음수
		}
		// 기본값 
		// 내림차순할때 참고해라
		return -1;
	}
}

 

class Descending implements Comparator{

	@Override
	public int compare(Object o1, Object o2) {
		if (o1 instanceof String && o2 instanceof String) {
			String s1 = (String)o1;
			String s2 = (String)o2;
			
				return o1 - o2; // 오름차순 (숫자 였다면)
				return o2 - o1; // 내림차순 (숫자 였다면)
				
				
				return s1.compareTo(s2); // 오름차순
				return s2.compareTo(s1); // 내림차순
				return s1.compareTo(s2) * -1; // 내림차순

 

 

추가)

Integer 에도 String 처럼 기본 정렬 방식이 정의되어 있다.

때문에 sort()로 오름차순 정렬이 된다.

Integer[] intg = {new Integer(1), 5, 2, 4, 3};
		System.out.println(Arrays.toString(intg));
		
		Arrays.sort(intg);
		System.out.println(Arrays.toString(intg)); // [1, 2, 3, 4, 5]

 

 

Set

package come.set;

import java.util.HashSet;
import java.util.Set;

public class HashSetTest {

	public static void main(String[] args) {
		
		// set 중복된 값을 허용하지 않는다
		// 데이터가 작은 경우에는 순서대로 들어가는 듯 보여지긴 하지만
		// 순서대로 들어가지 않는다
		// 해당 데이터 타입의 클래스에는 equals(), hashCode() 두개의 메서드가 오버라이딩 되어 있어야한다.
		// 중복체크를 위해서
		
		Object[] objArr = {"1", new Integer(1), "2", "2", "3", "3", "4", "4", "4"};
		
		Set set = new HashSet();
		
		for (int i = 0; i <objArr.length; i++) {
			set.add(objArr[i]);
		}
		
		// HashSet() 중복제거
		// 타입이 다른 데이터는 set에서도 다른 값이라고 판단한다.
		System.out.println(set); // [1, 1, 2, 3, 4]
	}

}

 

문제)

  • 중복된 값을 제거하는 것 때문에 번호가 5개가 나오기도 하고 4개가 나오기도 한다.
  • 44가지의 수만 체크하고 있었다.

 

해결된 코드 :

package come.set;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class HashSetLotto {

	public static void main(String[] args) {
		
		Set set = new HashSet();
		
		// 1 ~ 45 사이의 난수 중에서 6개의 숫자를 중복없이
		// set에 저장해서 출력하세요
		
		Random rand = new Random();
		
		while (set.size() < 6) {
			int number = rand.nextInt(45) + 1;
			set.add(number);
		}
		
		System.out.println("lotto 번호: " + set);

	}

}
  • rand.nextInt(45) 에 1을 더해 45까지 나오게 했다.
  • 조건 하나만 실행되면 되기 때문에 for문 두개를 빼주고 while문을 썼다.
  • 꼭 6자리가 나오게 하기 위해 size()를 사용했다.

 

위 문제를 풀고나니 어제 했던 로또번호 출력 프로그램도 뭔가 잘못됐다는걸 알았다.

그래서 돌려보니 역시 +1을 안해서 44까지의 수만 나오고 있었다.

 

수정된 코드 :

 

 

HashSet 을 이용한 빙고 번호판 만들기

List list = new ArrayList(set);
Collections.shuffle(list);
		
Iterator it = list.iterator();

위 코드를 이용해 섞어주기

 

오전 1,2시간 피그마를 배우고 그 후론 자바를 이어나간다.

 

피그마에서 유용한 검색어들

  • google material design icon
  • font awesome icons
  • feather icon
  • iconduck
  • open iconic

일단 피그마페이지에서 과부하로 아무것도 안뜨길래 그냥 아래 일들을 하기로 함.

자바2에 들어가면 스프링과 스프링부트를 사용한다고 해 맥을 쓰는 나는 또 설치방법에서 해맬게 분명하기 때문에 미리 설치하기로 함..

오전에 할 것

  • IntelliJ 설치 (완료)
  • JetBrains 학생인증 (완료)
  • 스프링부트까지 설치 (완료)

오후에 할 것 (일단 자습)

  • 노마드코더 강의 듣기 (완료)
  • 오늘 배운 자바 내용 복습 - 로또번호생성기 (완료)

 


약간의 딴짓을 하며 했던 것..

 

와 이제 스프링부트를 사용할 수 있다!

 

근데 이클립스에서는 어떻게 사용하지..

스프링부트는 이클립스에서 사용하는 법, 비주얼스튜디오코드에서 사용하는 법이 다 각각 있었다..!

각자 편한 곳에 설치해서 사용하면 될 듯한데 일단 해봤으니 어떤 툴에 사용할지는 강사님의 이야기를 들어보고 따라가야겠다.

 

vs코드 스프링부트 설치

 

[Spring Boot] vscode 개발 환경 구축하기 (MAC)

1. 배경 오늘은 스프링부트 개발환경 구축하기에 대해서 포스팅하겠습니다.

codingjalhaja.com

 

아마 나도 vs코드에 설치할듯해서 일단 참고 블로그 링크를 저장해둔다.

지금까지는 연습이었던 걸로..

 

추가로 깃에 대해 알아봤다. 


✨ Git, Github 사용방법

 

Git : 로컬에서 관리하는 버전관리시스템, 다른 사람이 나의 작업내용을 볼 수 없음

Github : 클라우드서비스로 다른 사람과 협업 시 소스코드 공유 가능

CLU : 명령어 인터페이스, 터미널을 통해 컴퓨터와 상호작용

GLU : 입출력 등의 기능을 아이콘으로 나타낸 것

local : 우리가 사용하고 있는 컴퓨터

remot : 원격저장소

repository (repo, 저장소) : 프로젝트가 존재하는 공간

branch : 저장소의 공간에서 독립적으로 작업하는 공간

commit : 소스코드의 업데이트가 확정된 순간 git repo에 저장, push 해주면 반응

pull : 원격저장소의 내용을 로컬장소에 끌어오는 것

push : commit한 내용을 원격저장소에 업로드



 

여기까지 하고 이렇게 딴 짓을 하다 피그마 놓친 부분이 좀 있지만..

비록 하필 오늘 결과물을 제출하는 날이었지만..

그냥 내 진도를 나가기로 한다.

주말 하루 반나절 정도 잡아서 지금까지 배운 거 복습할 겸 유튜브 강의 찾아보면 될 듯하다.

사실 그전에 시험을 볼 수도 있다; 이번 주에 볼 듯;;

시험 볼 때는 이모티콘 비슷한거라도 성실하게 넣기 + 디테일 챙기기

 

피그마를 하면서 어려웠던 부분 기록)

피그마에서 이미지 자동으로 넘기기

 

한참 헤맸는데 이렇게 하면 되는 거 였다.

참고)

 

무한 캐러셀(Carousel) 만들기

slide1부터 slide까지는 이런식으로바꿔준다.하늘색 부분을 slide1은 slide2로, slide2는 slide3으로, slide3은 slide4로slide4는 slide1로 Navigete to 해주고 시간은 100ms, Smart animate가 아닌 in

velog.io

 

감사합니다.. 늘 은인이 있구나!

 


드디어 다시 자바로 넘어갔다!

자바2

 

→ 객체지향

class 클래스명 {

-속성(변수)

-생성자(기본생성자)

-기능(메소드)

}

객체 → 속성 + 기능

 

collection framework (데이터 저장 방식)

컬렉션 프레임 워크 → 변수 (자료구조)

‘프레임워크’가 붙으면 사용방법이 구조화되어 있다는 뜻.

= 형태와 사용방법이 정해져있다.

  • List : 대표적인 클래스 ) ArrayList → 순서를 지켜 들어간다, 중복허용 O
  • Set : 대표적인 클래스 ) HashSet → 순서를 지키지 않는다, 중복허용 X
  • set - 로또번호생성기 같은 프로그램 만들때 좋음
  • Map : 대표적인 클래스 ) HashMap → 순서를 지키지 않는다,map - 데이터 관리할 때 좋다 (학생, 회원 등)
  • key와 value가 한쌍, key/value값 key → 중복허용 X value → 중복허용 O

위 세가지 자료구조를 기본적으로 자바에서 제공한다.

실제 존재하는 인터페이스 → 사용방식을 표준화 , 데이터를 관리하는 방식의 표준화

list 와 set 은 묶일 수 있음. → collection

상속 구조 정의가 이뤄진다.

공통의 특징 :

모든 collection framework 는 크기가 의미가 없다, object 타입이다.

 

ArrayList

: 배열에서 10개 이상의 값을 입력하면 알아서 늘려준다. 그렇기 때문에 크기는 큰 의미가 없어 지정하지 않는다.

데이터 관리의 핵심 - 읽고 쓰고 수정하고 삭제하기

package come.list;

import java.util.ArrayList;

public class ArraylistTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		// 읽고, 쓰고, 수정하고, 삭제하기
		
		ArrayList list = new ArrayList(); // 배열로 저장공간이 만들어진다.
		
		// 값 넣기
		list.add(new String("5"));
		list.add("1");
		list.add("4");
		list.add("2");
		list.add("3");
		list.add("3");
		
		// 읽기, 쓰기
		//list.get(0); // get 메소드 반환 타입은 object, 하지만 받는 타입은 String 때문에 형변환을 해야한다.
		String s1 = (String)list.get(0);
		String s2 = (String)list.get(4);
		String s3 = (String)list.get(5);
		
		System.out.println(s1); // 5  -> toString() 오버라이딩 되어 있음.
		System.out.println(s2); // 3
		System.out.println(s3); // 3  -> 중복 허용 
		
		// 수정하기
		list.set(3, "AA");
		String s4 = (String) list.get(3);
		System.out.println(s4); // AA 
		
		
		// 배열의 크기 알아보기
		System.out.println(list.size()); // 6 사이즈 크기를 반환해준다.
		
		// 삭제하기
		list.remove(5);
		System.out.println(list.size()); // 5 
		
		// 중간데이터 삭제
		list.remove(2);
		
		System.out.println(list.get(2)); // AA
		System.out.println(list.get(3)); // 3
		//System.out.println(list.get(4)); // 에러남. 값이 없다. 


	}

}

 

 

Linked List 연결리스트

주소값을 사용

클래스의 데이터를 관리하는 방식

(순서대로 데이터를 처리할때는 ArrayList , 데이터를 차례로 처리한다.

때문에 중간에서 데이터를 처리하는 것은 굉장히 느리다.)

Linked List는 주소값을 바로 찾는다.

바로 연결 → 중간 데이터 처리 빠름

package come.list;

import java.util.ArrayList;

public class ArraylistTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		// 읽고, 쓰고, 수정하고, 삭제하기
		
		ArrayList list = new ArrayList(); // 배열로 저장공간이 만들어진다.
		
		// 값 넣기
		list.add(new String("5"));
		list.add("1");
		list.add("4");
		list.add("2");
		list.add("3");
		list.add("3");
		
		//linked List
		
		for (int i = 0; i <list.size(); i++) {
			String s = (String)list.get(i);
			System.out.println(s); // 5 1 3 4 2 3 3 들어간 순서 그대로 출력되는 것을 알 수 있다.
		}

 

나머지는 api 에서 찾아서 써보기

 

 

기본형을 참조형으로 바꾸는 방법 :

래퍼 클래스(Wrapper Class)를 사용 → auto boxing

package come.list;

import java.util.ArrayList;

public class ArraylistTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		// 읽고, 쓰고, 수정하고, 삭제하기
		
		ArrayList list = new ArrayList(); // 배열로 저장공간이 만들어진다.
		
		
		// auto boxing
		// 1,2,3,4,5
		// Integer(1) -> 박싱
		// Integer i = 3 -> 자동캐스팅으로 가능
		// Object obj = 3 -> Object obj = new Integer(3); 같은 뜻		
		
		list.add(new Integer(1));  // new Integer 생략 가능
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		list.add(6);
		list.add(7);
		list.add(8);
		list.add(9);
		list.add(10);
		
		int sum = 0;
		
		for (int i = 0; i < list.size(); i++) {
			int num = (Integer)list.get(i);
			sum += num; 
		}
		
		System.out.println(sum); // 55

 

향상된 for문은 데이터를 읽을 때 사용

// 향상된 for문
		for (Object obj : list) {
			int i = (Integer)obj;
			sum += i;
		}
		
		System.out.println(sum); // 55

 

 

 

리스트의 특성을 따르는 자료구조, 스택과 큐

Stack 에 대해 알아본다.

 

Stack 구조

들어 오는 곳 나가는 곳이 한 곳, LIFO : 가장 늦게 들어온 것이 가장 먼저 사용된다

(class 제공이 됨)

 

Queue 구조

들어오는 곳, 나가는 곳 따로 있음, FIFO : 먼저 들어온 것이 먼저 나온다

(인터페이스)

peek() 가장 최근의 값 읽어오기

pop() 삭제하며 읽어오기, 데이터가 없는데 pop()을 하면 오류가 발생한다!

→ 오류를 방지하기 위해 데이터가 있는지 없는지 체크하거나

→ 예외처리를 사용

 

 

pop()과 empty() 사용

package come.list;

import java.util.Stack;

public class StackTest {

	public static <E> void main(String[] args) {
		// TODO Auto-generated method stub
		
		Stack st = new Stack();
		
		st.push("0");
		st.push("1");
		st.push("2");
		
		while(!st.empty()) {
			System.out.println(st.pop()); // 2 1 0 반대로 읽어온다
		}
		
		
	}

}

 

peek()과 search() 사용

package come.list;

import java.util.Stack;

public class StackTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Stack st = new Stack();
		
		st.push("0");
		st.push("1");
		st.push("2");
		
//		while(!st.empty()) {
//			System.out.println(st.pop()); // 2 1 0 반대로 읽어온다
//		}
		
		if (!st.empty()) {
			System.out.println(st.peek()); // 2 가장 최근에 추가된 값
			System.out.println(st.search("0")); // 3  
			// search는 인덱스를 반환하는 것이 아니라 순번을 반환한다. 
			// search의 인자 값으로 받은 값이 스택구조에서 몇번째 있는지 반환
			// 스택구조 -> LIFO : 가장 늦게 들어온 것이 가장 먼저 사용된다
			// 가장 먼저 들어간 것이 가장 아래부터 쌓이면 위에서부터 순번 부여
		}
		
	}

}

 

System.out.println(st.search(0)); 을 넣었을 때 없는 값인데 -1 이 나온 이유

→ 퐁규의 답변 :

코드에서 System.out.println(st.search(0));**를 실행했을 때 -1이 나오는 이유는 search 메서드가 0이라는 정수 값을 찾기 때문입니다. 하지만 스택에는 문자열 "0"이 들어있기 때문에 일치하는 값을 찾지 못해서 -1을 반환합니다.

Stack 클래스의 search 메서드는 객체를 찾을 때 해당 객체가 스택에 존재하면 1부터 시작하는 위치를 반환하고, 존재하지 않으면 -1을 반환합니다. 따라서, "0"이라는 문자열을 찾아야 합니다.

잘못넣었는데 값이 나와서 뭔지 궁금해서 gpt한테 물어봄.

 

결과적으로 search()는 값이 없으면 -1 를 반환한다.

 

 

오늘 배운 것을 복습하며 로또번호 생성 프로그램을 만들어보자

 

(팀원이 내준 숙제)

// 정수형 배열리스트 al

// 1~45까지 리스트에 추가

// 랜덤으로 6개를 삭제하며

// 또 따른 배열리스트 lotto에 추가

// lotto 출력

 

초기 코드 :

package come.list;

import java.util.ArrayList;
import java.util.Random;

public class LottoNum {

	public static void main(String[] args) {
		// 정수형 배열리스트 al
		// 1~45까지 리스트에 추가
		// 랜덤으로 6개 삭제하며
		// 또 따른 배열리스트 lotto에 추가
		// lotto 출력
		
		// 정수형 배열리스트 al 
		ArrayList<Integer> al = new ArrayList<Integer>();
		// 배열리스트 lotto 6개의 값을 받는 곳
		ArrayList<Integer> lotto = new ArrayList<Integer>();
		
		// 랜덤클래스 생성
		Random rand = new Random();
		
		// 45개의 로또번호 al에 넣기
		for (int i = 1; i <= 45; i++) {
			 al.add(i);
		}
		
		//System.out.println(al);
		
		// 로또번호 6개의 값이 들어갈 공간
		for (int i = 0; i <= 5; i++) {
			lotto.add(i);	
		}
		
		lotto.set(0, rand.nextInt(45));
		lotto.set(1, rand.nextInt(45));
		lotto.set(2, rand.nextInt(45));
		lotto.set(3, rand.nextInt(45));
		lotto.set(4, rand.nextInt(45));
		lotto.set(5, rand.nextInt(45));
		
		
		System.out.println("lotto 번호: " + lotto);
		

	}

}

처음에 하다가 저 삭제한다는게 어디서 왜 삭제를 한다는건지 문제를 이해 못하고 그냥 로또번호 생성이면 금방인데 오늘 배운 걸 쓰려니까 어떻게 하는 건지도 모르겠고.. 꼬이고 꼬여 그냥 값만 나오게 만들었던 코드다.

al를 사용하지 않았고, set 도 for문으로 처리할 수 있는데 사용을 안한 그냥 성의가 없는 상태..

이걸 다시 정신차리고 바꿔보자.

하면서 생각해보니, 문제에서 ‘랜덤으로 6개를 삭제하며’라는 게 중복제거를 위한 거라는 걸 알게됐다! ㅋ

 

완성된 코드 :

package come.list;

import java.util.ArrayList;
import java.util.Random;

public class LottoNum {

	public static void main(String[] args) {
		// 정수형 배열리스트 al
		// 1~45까지 리스트에 추가
		// 랜덤으로 6개 삭제하며
		// 또 따른 배열리스트 lotto에 추가
		// lotto 출력
		
		// 정수형 배열리스트 al 
		ArrayList<Integer> al = new ArrayList<Integer>();
		// 배열리스트 lotto 6개의 값을 받는 곳
		ArrayList<Integer> lotto = new ArrayList<Integer>();
		
		// 랜덤클래스 생성
		Random rand = new Random();
		
		// 45개의 로또번호 al에 넣기
		for (int i = 1; i <= 45; i++) {
			 al.add(i);
		}
		
		// 로또번호 6개의 값이 들어갈 공간만들기
		// 공간에 al의 크기 범위 안 랜덤값 6개 받기 (al의 크기 = 길이 = 45까지 들어간 값) 
		for (int i = 0; i < 6; i++) {
			int index = rand.nextInt(al.size());
			// al에서 중복값 제거를 위해 lotto에 넣어진 index는 삭제
			lotto.add(al.remove(index));
		}
		
		System.out.println("lotto 번호: " + lotto);

	}

}

 

문제를 잘 이해하고 모르면 출제자에게 정확하게 물어보는게 시간을 아끼는 법.. 이다!

 

 

UIUX _국비과정 0517 | Notion

오늘 하루는 피그마를 다뤄본다.

ringed-tartan-f02.notion.site

 

 

UIUX _국비과정 0520 | Notion

오늘까지 피그마

ringed-tartan-f02.notion.site

 

 

 

에어비엔비 따라 만들기

 

프로토타입과 인터럭션

 

실제 에어비엔비 사이트의 모바일 버전을 따라 만들어보기도 하고,

프로토타입과 인터럭션을 사용해보며 페이지가 넘어가는 방법을 설정해보기도 했다.

근데 이걸로 디자인과 설계를 해놓으면 개발자가 다 자바스크립트로 짜야한다는 거..

이래서 디자이너와 개발자가 싸우는구나 알게된 수업이다

ㅠㅠ 이런걸 배울 수록 자바스크립트를 더 잘해야 하는구나 싶은 생각뿐

실제 해보며 아는 기능이 대부분이고 피그마는 단축키가 핵심인것같다.

그래서.. 피그마 수업부터는 정리한 내용들이 거의 없음..

추가로 내가 피그마에 관심있어하니 강사님이

 

OvenApp.io

Oven(오븐)은 HTML5 기반의 무료 웹/앱 프로토타이핑 툴입니다. (카카오 제공)

ovenapp.io

 

이런 것도 알려주셨다. 카카오에서 만든 오븐이라는 틀.

피그마와 비슷한데 기능이 훨씬 적다고 한다. 그래도 카카오에서 만들었으니 다음 개발자들은 이걸 쓰는듯..?(아닐 수 있음)

+ Recent posts