[ 에러 발생 코드 ]

public enum StockEnum {

    KAKAO(035720,"카카오"),
    NAVER(035420,"네이버"),
    KIA(000270,"기아"),
    SKENOVATION(096770, "SK이노베이션"),
    LGCHEMISTRY(051910,"LG화학"),
    SAMSUNG(005930,"삼성");

    int number;
    String name;

    StockEnum(int number, String name) {
        this.number = number;
        this.name = name;
    }
}

위 코드를 작성해보면 SK이노베이션의 숫자에 에러가 발생한다.

그 이유는 위 코드는 숫자 0부터 시작하는데, 그 뜻은 java 에서 8진수(base-8) 로 해석한다는 의미입니다.

 

그래서 0 뒤에 있는 8,9에 대해 해석할 수 없으므로 에러가 발생합니다.

 

위 코드를 수정하려면 아래와 같이 String 으로 작성하면 됩니다.

 

 

[ 수정 코드 ]

public enum StockEnum {

    KAKAO("035720","카카오"),
    NAVER("035420","네이버"),
    KIA("000270","기아"),
    SKENOVATION("096770", "SK이노베이션"),
    LGCHEMISTRY("051910","LG화학"),
    SAMSUNG("005930","삼성");

    String number;
    String name;

    StockEnum(String number, String name) {
        this.number = number;
        this.name = name;
    }
}

'JAVA' 카테고리의 다른 글

stream 활용해 list 최소, 최대값 구하기  (0) 2022.11.20
2차원 배열 정렬, 문자열 배열 정렬  (1) 2022.10.03
Queue와 BFS  (1) 2022.09.30
정렬과 lambda  (0) 2022.09.29
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27

stream 활용해 list 최소, 최대값 구하기

 

1) IntStream 으로 변환하는 경우

List<Integer> list = Arrays.asList(2,3,6,4,12);
Integer minValue = list.stream().mapToInt(x->x).min().orElseThrow(NoSuchElementException::new);

 

mapToInt()

stream 에서 Int 원시 스트림으로 변환

 

min()

최솟값을 구하는데, 결과값을 Optional로 반환한다.

 

orElseThrow()

결과 값이 없는경우 괄호안의 예외를 throw 한다.

 

 

 

2. IntStream 으로 변환하지 않는 경우

List<Intger> list = Arrays.asList(2,3,7,1,5,4);
Integer maxValue = list.stream().max(Comparator.comparing(x->x)).orElseThrow(~~);

 

Comparator.comparing(x->x)

두 매개 변수 객체를 비교해주는 것입니다.

자기 자신의 상태가 어떻든 상관없이 피라미터로 들어오는 두 객체를 비교하는 것

 

* comparable : 자기 자신과 매개변수 객체를 비교하는 것

'JAVA' 카테고리의 다른 글

JAVA Enum 에러  (0) 2023.04.09
2차원 배열 정렬, 문자열 배열 정렬  (1) 2022.10.03
Queue와 BFS  (1) 2022.09.30
정렬과 lambda  (0) 2022.09.29
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27

2차원 배열을 정렬하는 방법

Comparator 을 오버라이딩하여 정렬 기준을 제시하면 된다.

 

 

1) 첫번째 요소를 고려해서 정렬한다면

 

arr { {1,3}, {2,5}, {1,1} } 일 때

Arrays.sort(arr, Comparator.comparingInt(o1->o1[0]));

하게 되면 arr{ {1,3}, {1,1}, {2,5} } 이렇게 오름차순으로 정렬된다.

 

 

2) 두번째 요소도 같이 고려하고 싶다면

Arrays.sort(arr, (o1,o2) -> {
	if(o1[0] == o2[0]){ // 0번째 요소가 같으면 1번 째 요소로 비교
    	return Integer.compare(o1[1], o2[1]);
    }
    else { // 요소 비교
    	return Integer.compare(o1[0], o2[0]);
    }
});

하게 되면 arr { {1,1}, {1,3}, {2,5} } 가 오게 된다.

 

 

 

문자열 배열 비교 방법

Arrays.sort(tickets, new Comparator<String[]>() {
            @Override
            public int compare(String[] o1, String[] o2) {
                return o1[1].compareTo(o2[1]);
            }
        });

위와 같이 하면 a ~ z 순으로 정렬하게 됩니다.

'JAVA' 카테고리의 다른 글

JAVA Enum 에러  (0) 2023.04.09
stream 활용해 list 최소, 최대값 구하기  (0) 2022.11.20
Queue와 BFS  (1) 2022.09.30
정렬과 lambda  (0) 2022.09.29
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27

Queue<E> 인터페이스

클래스로 구현된 스택과는 달리 자바에서 큐 메모리 구조는 별도의 인터페이스 형태로 제공됩니다.

큐를 상속받는 하위 인터페이스는 다음과 같습니다.

 

 1) Deque<E>

 2) BlockingDeque<E>

 3) BlockingQueue<E>

 4) TransferQueue<E>

 

큐 메모리 구조는 선형 메모리 공간에 데이터를 저장하면서 선입선출의 시맨틸을 따르는 자료구조입니다.

즉, 먼저 저장된 데이터가 먼저 인출됩니다.

 

 

Queue 메소드

 

Queue를 활용한 BFS 구현

import java.io.*;
import java.util.*;

/* 인접 리스트를 이용한 방향성 있는 그래프 클래스 */
class Graph {
  private int V; // 노드의 개수
  private LinkedList<Integer> adj[]; // 인접 리스트

  /** 생성자 */
  Graph(int v) {
    V = v;
    adj = new LinkedList[v];
    for (int i=0; i<v; ++i) // 인접 리스트 초기화
      adj[i] = new LinkedList();
  }

  /** 노드를 연결 v->w */
  void addEdge(int v, int w) { adj[v].add(w); }

  /** s를 시작 노드으로 한 BFS로 탐색하면서 탐색한 노드들을 출력 */
  void BFS(int s) {
    // 노드의 방문 여부 판단 (초깃값: false)
    boolean visited[] = new boolean[V];
    // BFS 구현을 위한 큐(Queue) 생성
    LinkedList<Integer> queue = new LinkedList<Integer>();

    // 현재 노드를 방문한 것으로 표시하고 큐에 삽입(enqueue)
    visited[s] = true;
    queue.add(s);

    // 큐(Queue)가 빌 때까지 반복
    while (queue.size() != 0) {
      // 방문한 노드를 큐에서 추출(dequeue)하고 값을 출력
      s = queue.poll();
      System.out.print(s + " ");

      // 방문한 노드와 인접한 모든 노드를 가져온다.
      Iterator<Integer> i = adj[s].listIterator();
      while (i.hasNext()) {
        int n = i.next();
        // 방문하지 않은 노드면 방문한 것으로 표시하고 큐에 삽입(enqueue)
        if (!visited[n]) {
          visited[n] = true;
          queue.add(n);
        }
      }
    }
  }
}

 

'JAVA' 카테고리의 다른 글

stream 활용해 list 최소, 최대값 구하기  (0) 2022.11.20
2차원 배열 정렬, 문자열 배열 정렬  (1) 2022.10.03
정렬과 lambda  (0) 2022.09.29
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27
putIfAbsent  (0) 2022.08.11

lambda의 스타일

 

1. (parameter) -> expression

2. (parameter) -> {statement;}

 

이를 활용한 정수 리스트를 정렬하는 방식 : 

numbers.sort((Integer num1, Integer num2) -> {return num1-num2;});

 

우선, 람다는 함수형 인터페이스만 쓰일 수 있습니다.

함수형 인터페이스란 오직 하나의 추상메서드만 가지는 인터페이스를 의미합니다.

그래서 유일한 추상메서드를 보고 타입을 추론할 수 있기 때문에 타입을 명시하지 않아도 상관없습니다.

 

numbers.sort((num1,num2)->num1-num2);
or
numbers.sort((num1,num2)->Integer.compare(num1,num2));

 

그래서 이를 응용하여 sort() 함수에 적용시켜준다면 아래와 같이 작성할 수 있습니다.

 

ArrayList<String> g = new ArrayList<>();
g.add("a");
g.add("c");
g.add("b");
Collections.sort(g,(p1,p2)->g.get(p2)-g.get(p1));

'JAVA' 카테고리의 다른 글

2차원 배열 정렬, 문자열 배열 정렬  (1) 2022.10.03
Queue와 BFS  (1) 2022.09.30
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27
putIfAbsent  (0) 2022.08.11
Java의 이중 중괄호 초기화  (0) 2022.08.11

우선순위 큐

일반적인 큐 구조를 가지면서, 데이터가 들어온 순서대로 나가는것이 아닌 우선순위를 먼저 결정하고

우선순위가 높은 데이터가 먼저 나가는 자료구조입니다.

 

 

 

우선순위 큐의 조건

필수적으로 Comparable Interface를 구현해야 한다.

 

 - Comparable Interface를 구현하면 compareTo method 를 오버라이딩하게 되고,

    객체에서 처리할 우선순위 조건을 리턴해주면 PriorityQueue가 알아서 우선순위 높은 객체를 추출하는 구조입니다.

 

Heap 을 이용해 구현하는 것이 일반적입니다.

 

 - 데이터를 삽입할 때  우선순위를 기준으로 최대 힙 또는 최소 힙을 구성합니다.

    데이터를 꺼낼 때 루트 노드를 얻어낸 뒤 루트 노드를 삭제할 때는 빈 루트 노드 위치에 맨 마지막 노드를 삽입 후 아래로 내려가면서

    적절한 위치를 찾아 옮기는 방식으로 진행됩니다.  ( * 최대힙과 최소힙 * )

 

 

우선순위 큐 특징

1. 높은 우선순위의 요소를 먼저 꺼내 처리하는 구조입니다.

2. 내부 요소는 힙으로 구성되어 이진트리 구조를 가집니다.

3. 우선순위를 중요시해야하는 상황에 주로 쓰입니다.

4. 시간복잡도는 O(NlogN) 입니다.

 

 

 

우선순위 큐 선언

Priority Queue 를 사용하면 java.util.PriorityQueue 를 import 합니다.

import java.util.PriorityQueue;
import java.util.Collections;

//낮은 숫자가 우선 순위인 int 형 우선순위 큐 선언
PriorityQueue<Integer> priorityQueueLowest = new PriorityQueue<>();

//높은 숫자가 우선 순위인 int 형 우선순위 큐 선언
PriorityQueue<Integer> priorityQueueHighest = new PriorityQueue<>(Collections.reverseOrder());

 

 

 

우선순위 큐 동작 방법

// add(value) 메서드의 경우 만약 삽입에 성공하면 true를 반환, 
// 큐에 여유 공간이 없어 삽입에 실패하면 IllegalStateException을 발생
priorityQueueLowest.add(1);
priorityQueueLowest.add(10);
priorityQueueLowest.offer(100);

priorityQueueHighest.add(1);
priorityQueueHighest.add(10);
priorityQueueHighest.offer(100);


// 첫번째 값을 반환하고 제거 비어있다면 null
priorityQueueLowest.poll();

// 첫번째 값 제거 비어있다면 예외 발생
priorityQueueLowest.remove(); 

// 첫번째 값을 반환만 하고 제거 하지는 않는다.
// 큐가 비어있다면 null을 반환
priorityQueueLowest.peek();

// 첫번째 값을 반환만 하고 제거 하지는 않는다.
// 큐가 비어있다면 예외 발생
priorityQueueLowest.element();

// 초기화
priorityQueueLowest.clear();

'JAVA' 카테고리의 다른 글

Queue와 BFS  (1) 2022.09.30
정렬과 lambda  (0) 2022.09.29
putIfAbsent  (0) 2022.08.11
Java의 이중 중괄호 초기화  (0) 2022.08.11
HashSet  (0) 2022.08.11

putIfAbsent

- Key 값이 존재하는 경우 : Map의 value 값 반환

- Key 값이 존재하지 않는 경우 : Key와 value 를 Map 에 저장하고 Null 을 반환

 

 

매개변수

- key : 지정된 값이 연관될 키

- value : 지정된 키와 연결될 값

 

 

기본 예제

import java.util.HashMap;

public class putIfAbsentTest {

	public static void main(String[] args) {
		
		HashMap<String, Integer> map = new HashMap<String, Integer>();
		map.put("A", 1);
		map.put("B", 1);
		map.put("C", 1);
		map.put("D", null);
		
		System.out.println("result : " + map.toString());
		//result : {A=1, B=1, C=1, D=null}
		
		map.putIfAbsent("E", 1);
		System.out.println("result1 : " + map);
		//result1 : {A=1, B=1, C=1, D=null, E=1}
		
		map.putIfAbsent("E", 2);
		System.out.println("result2 : " + map);
		//result2 : {A=1, B=1, C=1, D=null, E=1}
		
		map.putIfAbsent("D", 1);
		System.out.println("result3 : " + map);
		//result3 : {A=1, B=1, C=1, D=1, E=1}
	}
}

 

'JAVA' 카테고리의 다른 글

정렬과 lambda  (0) 2022.09.29
PriorityQueue ( 우선순위큐 )  (0) 2022.09.27
Java의 이중 중괄호 초기화  (0) 2022.08.11
HashSet  (0) 2022.08.11
Stream  (0) 2022.08.10

이중 중괄호 사용

단일 표현식에서 Java 객체를 생성하고 초기화하기 위해 이중 중괄호를 사용할 수 있습니다.

이렇게하면 인스턴스 이니셜라이저만 포함된 익명의 내부 클래스가 생성됩니다.

 

 

예제

[ 자바 객체 초기화 ]

import java.util.Arrays;
 
class Employee
{
    private String name;
    private int id;
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setAge(int id) {
        this.id = id;
    }
 
    // 게터
 
    @Override
    public String toString() {
        return Arrays.asList(name, String.valueOf(id)).toString();
    }
}
 
// Java에서 객체 초기화
class Main
{
    public static void main(String[] args)
    {
        // 익명 클래스
        Employee person = new Employee() {{
            // 초기화 블록
            setName("Aditya");
            setAge(27);
        }};
 
        System.out.println(person);
    }
}

 

 

[ Java 에서 세트 초기화 ]

Set<Integer> set = new HashSet<Integer>() {{
    add(1);
    add(2);
    add(3);
}};

 

 

이외에 List, Map 초기화에도 세트 초기화와 같은 형식으로 사용됩니다.

 

하지만 이중 중괄호는 사용할 때마다 추가 클래스가 필요하므로 이 기술은 피하는 것이 좋습니다.

또한 인스턴스 및 개체에 대한 숨겨진 참조를 보유하므로, 메모리 누수 문제를 일으킬 수 있습니다.

'JAVA' 카테고리의 다른 글

PriorityQueue ( 우선순위큐 )  (0) 2022.09.27
putIfAbsent  (0) 2022.08.11
HashSet  (0) 2022.08.11
Stream  (0) 2022.08.10
HashMap  (0) 2022.08.10

HashSet

  • Set 인터페이스에서 지원하는 구현 클래스
  • 순서대로 입력되지 않고, 일정하게 유지되지 않습니다.
  • null 요소 허용
  • 중복을 허용하지 않음

 

 

 

중복을 걸러내는 과정
  • hashCode() 메서드를 호출해 코드를 얻음
  • 저장되어 있는 객체들의 해시 코드와 비교해 같은 해시 코드가 있는지 확인
  • 있다면 다시 equlas() 메서도르 두 객체를 비교
  • true 가 나오면 동일한 객체로 판단하고 중복 저장하지 않습니다.

 

* String 형에 대해서 중복을 걸러내는 과정의 특징

: 앞의 과정과 같은데, 다른 점은 String 클래스가 hashCode(), equals() 메소드를 재정의해서 같은 문자열일 경우,

  리턴값을 true 로 나오게 했기 때문입니다.

 

 

변수 선언 방법
  • hashSet<데이터 타입> 변수명 = new HashSet<데이터타입>();

   해당 데이터 타입형의 HashMap 데이터가 들어갑니다.

 

 

다양한 메서드들
  1. add
  2. remove
  3. clear
  4. size
  5. contains(찾고자 하는 값 )

 

 

예제

프로그래머스 [ 신고 결과 받기 ] 문제에서 사용될 수 있습니다.

 

https://tjdwns4537.tistory.com/97

 

신고 결과 받기

프로그래머스 문제: 신고 결과 받기 [ 문제 설명 ]  신입사원 무지는 게시판 불량 이용자를 신고하고 처리 결과를 메일로 발송하는 시스템을 개발하려 합니다. 무지가 개발하려는 시스템은 다음

tjdwns4537.tistory.com

 

'JAVA' 카테고리의 다른 글

putIfAbsent  (0) 2022.08.11
Java의 이중 중괄호 초기화  (0) 2022.08.11
Stream  (0) 2022.08.10
HashMap  (0) 2022.08.10
백트래킹  (0) 2022.07.27

Stream 이 왜 사용되는가 ?

자바8 에서 추가됬으며 람다를 활용할 수 있는 기술 중 하나입니다.

자바8 이전에는 배열 또는 컬렉션을 다룰려면 for/foreach 문을 사용하는 방법이 었습니다.

 

이 방법은 단점이 있습니다.

 

1) 복잡해질 수록 로직이 섞입니다.

2) 코드의 양이 많아집니다.

3) 루프가 여러번 도는 현상이 발생할 수 있습니다.

 

그래서 이를 보완하고자 등장한 것이 스트림입니다.

 

 

Stream

  • 스트림은 '데이터의 흐름' 입니다.

1) 배열 또는 인스턴스에 함수 여러 개를 조합

2) 원하는 결과에 대해 필터링, 매핑해 가공된 결과를 얻음

 

전체 -> 매핑 -> 필터링1 -> 필터링2 -> 결과 생성 -> 결과물

 

  • 스트림은 람다를 이용할 수 있습니다.

1) 코드의 양을 줄일 수 있음

2) 배열과 컬렉션을 함수형으로 처리 가능

  • 병럴처리 가능

1) 하나의 작업을 둘 이상 잘게 나눠 동시에 진행 가능

2) 즉, 쓰레드를 활용해 많은 요소를 빠르게 처리 가능

 

 

Stream 의 생성

스트림은 배열과 컬렉션 인스턴스를 이용해서 생성할 수 있습니다.

 

// 1. 배열로 스트림 생성
String[] arr = new Stringp[]{"a","b","c"};
Arrays.stream(arr); 

// 2. 컬렉션으로 스트림 생성
List<String> list = Arrays.asList("a","b","c");
Stream<String> stream = list.stream(); 

// 3. StreamBuilder 로 생성
Stream<String> streamBuilder = Stream.<String>builder().add("sungjun").add("jun").build(); //[ sungjun, jun ]

// 4. Stream.generate() 로 생성
Stream<String> generatedStream = Stream.generate(()->"gen").limit(5) // [ "gen","gen","gen","gen","gen" ]

 

  • Stream.generate()

제너레이트 메서드를 사용하면 람다로 값을 넣을 수 있습니다.

 

 

Stream.iterate()

iterate 메서드를 이용하면 초기값과 해당 값을 다루는 람다를 이용해 스트림에 들어갈 요소를 만듭니다.

예제에서는 30 이라는 초기값을 기준으로 2씩 증가하는 값들이 들어갑니다.

 

Stream<Integer> iteratedStream = Stream.iterate(30,n->n+2).limit(5);
[ 30,32,34,36,38 ]

 

 

기본 타입형 스트림

제네릭 ( Stream<String> ) 을 사용하지 않고 기본 (int, double, long) 스트림을 생성할 수 있습니다.

IntStream intStream = IntStream.range(1,5) [1,2,3,4] 
// 두번째 인자는 종료지점에 포함 안됩니다.

 

이를 활용한 랜덤 난수 생성

DoubleStream doubles = new Random().doubles(3); // 랜덤 난수 3개 생성

 

 

Stream 의 다양한 예제

 

  • 문자열 스트링을 숫자로 변환
IntStream charStream = "Stream".chars(); // [ 83,116,114,101,97,109 ]

 

  • 파일 스트림

Files 클래스의 lines 메소드는 해당 파일의 각 라인을 스트링 타입의 스트림으로 만들어줍니다.

Stream<String> lineStream = Files.lines(Paths.get("file.txt"), Charset.forName("UTF-8"));

 

  • Stream 연결하기
Stream<String> stream1 = Stream.of("a","b","c");
Stream<String> stream2 = Stream.of("d","e","f");

stream<String> concat = Stream.concat(stream1,stream2);

 

 

가공하기 ( 중간 작업 )

전체 요소 중에서 API 를 활용해 내가 원하는 것만 뽑아내는 과정입니다.

가공 단계를 중간 작업이라고 하는데, 이러한 작업은 스트림을 리턴하기 때문에 여러 작업을 이어 붙여서 작성할 수 있습니다.

 

 

List<String> names = Arrays.asList("sun","mon","jun");

//Filtering
Stream<String> FilteringName = names.stream().filter(name->name.contains("s"); // [sun]

//Mapping
String<String> MappingName = names.strea().map(String::toUpperCase);
  • Filtering

스트림 내에 요소들을 하나씩 평가해서 걸러내는 작업

  • Mapping

스트림 내에 요소들을 특정 값으로 변환해줍니다. 이때 값을 변환하기 위한 람다를 인자로 받습니다.

 

 

 

IntStream.of(10,20,50,40,30).sorted().collect(Collectors.toList());
// [10,20,30,40,50]

IntStream.of(10,20,50,40,30).sorted(Comparator.reverseOrder()).collect(Collectors.toList());
// [50,40,30,20,10]

List<String> lan = Arrays.asList("seoulpower","geoje","busanwon","bew")
lan.stream().sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList());
// [ bew, geoje,busanwon ... ]
lan.stream().sorted(s1,s2->s2.length()-s1.length()).collect(Collectors.toList());
// [ seoulpower, busanwon, ... ]
  • sorted : 스트림 내에 요소를 오름차순으로 정렬
  • collect(Collectors.변환될 타입) : 스트림으로 반환하는 요소를 해당 컬렉션으로 바꿔서 리턴해줍니다.
  • sorted(Comparator.reverseOrder()) : 스트림 내에 요소를 내림차순으로 정렬
  • sorted(Comparator.comparingInt(String::length)) : 스트림 내에 요소를 문자열 길이 기준으로 정렬

 

결과 만들기

가공한 스트림을 가지고 내가 사용할 결과 값을 만들어내는 단계입니다. 따라서 최종 작업입니다.

 

long count = IntStream.of(1,2,3,4,5).count();
long count = IntStream.of(1,2,3,4,5).sum();
long count = IntStream.of(1,2,3,4,5).min();
long count = IntStream.of(1,2,3,4,5).max();

최소, 최대, 합, 평균 등 기본형 타입으로 결과를 만들어 낼 수 있습니다.

 * 이때, [ 평균/최소/최대 ] 의 경우에는 스트림이 비어있는 경우를 표현할 수 없기때문에 Optional 을 이용해 리턴합니다.

 

 

이 외에, 

Reduction, Collecting, Matching, foreach

에 대한 개념이 있지만 현재 쓸 일이 없어서 기억을 못할 것 같으므로 다음에 사용할 일 있을 때

다시 따로 블로깅 하도록 하겠습니다.

 

 

 

 

조건검사

  • findAny, findFirst()

filter() 연산과 함께 사용됩니다. 조건식에 맞는 요소가 있는지 확인할 때 사용됩니다.

반환 타입이 Optional<T> 이며, 스트림의 요소가 없을 때는 비어 있는 Optional 객체를 반환합니다.

 

  • allMatch() : 조건식에 모든 요소가 일치하면 true
  • anyMath() : 어떤 요소 하나라도 조건에 일치하면 true
  • noneMatch() : 조건식에 모든 요소들이 일치하지많으면 true

 

 

 

 

[ 소스코드 예제 ]

현재 제가 진행중인 프로젝트에서 사용된 스트림에 대해서 알아보겠습니다.

 

private static Map<Long, Member> store = new HashMap<>();
public Optional<Member> findByname(String name) {
        return store.values().stream()
                .filter(member -> member.getName().equals(name))
                .findAny();
    }

 

1. store.values () 의 반환 타입

: Collection<Member> 

 즉, values 는 값을 컬렉션 타입으로 반환 해줍니다. 

 

2. .stream()

: 컬렉션으로 반환된 values 를 stream 으로 만들어줍니다.

 

3. .filter( member -> member.getName().equals(name)) 

: store 내부의 member 인자에서 getName() 을 통해서 name 값을 받아옵니다.

 받아온 name 과 findByname 의 인자로 주어진 name 을 비교해줍니다.

 

4. .findAny()

: filter 한 조건식에 맞는 요소가 있으면 해당 요소를 모두 반환해줍니다. ( 첫번째 하나만 하려면 findFirst )

만약 findAny() 를 붙치지 않는다면 Stream() 을 그대로 반환하는 모습을 볼 수 있습니다.

 

 

 

'JAVA' 카테고리의 다른 글

Java의 이중 중괄호 초기화  (0) 2022.08.11
HashSet  (0) 2022.08.11
HashMap  (0) 2022.08.10
백트래킹  (0) 2022.07.27
람다식  (0) 2022.07.18
Map

key,value 로 구성된 Entry 객체를 저장하는 구조

 

특징

1. 키와 값은 모두 객체

2. 키는 중복될 수 없음

3. 만약 동일한 키를 가지고 있는 Entry객체를 Map 컬렉션에 저장하면, 기존의 값은 없어지고 새로운 값으로 대치됨

 

 

컬렉션이란?

객체의 모음, 그룹이라고 할 수 있습니다.

Collection 인터페이스 : [ List, Set, Queue ] 인데,

Map 은 컬렉션이 사실 아니지만, 그냥 컬렉션으로 분류해줍니다.

 

컬렉션을 왜 사용할까?

1. 일관된 API

컬렉션 하위에 있는 모든 클래스 컬렉션에서 상속 받아 통일된 메서드가 사용 가능해집니다.

 

2. 프로그래밍 효율성

객체 지향 프로그램의 추상화 개념이 성공적으로 구현되어 있음

 

3. 프로그램 속도 및 품질 향상

유용한 데이터 구조 및 알고리즘을 지니고 있어 성능이 향상됩니다.

 

 

그러면 다시 돌아가 Map 에 대해 알아보도록 하겠습니다.

 

 

Map 컬렉션 기능 및 종류
  • 종류 : HashMap, HashTable, LinkedHashMap, Properties, TreeMap 등

Map Interface 의 메서드들은 특징에서 알아 봤듯이 키로 객체를 관리합니다.

그래서 키를 매개변수 값으로 사용하는 경우가 많습니다.

 

그러면 이제 HashMap 에 대해 알아보겠습니다.

 

HashMap

Map Interface 에서 대표적으로 많이 사용되는 Map Collection 입니다.

[ HashMap 의 키로 사용될 객체는 hashCode(),equals() 메서드를 오버라이딩해서 동등 객체 조건을 정의해야함 ]

 

  • 동등 객체가 될 조건

1) hashCode() : 같은 키 반환

2) equals() : true 반환

 

 

 

Iterator

컬렉션 구현 방법을 노출시키지 않으면서도 원소를 순차적으로 접근 가능하게 해줍니다.

 

[ Iterator 을 사용해 key, value 모두 반복하며 호출 ]

HashMap<Integer,Interger> map = new HashMap<>();
Iterator<map.Entry<Integer,Integer>> entries = map.entrySet().iterator();
while(entries.hasNext()){
	HashMap.Entry<Integer,Integer> entry = entries.next();
    sout(entry.getKey() + entry.getValue());

 

[ for loop 로 key값으로 value 찾기 ]

HashMap<Integer,Integer> map = new HashMap<>();
for(Integer Key : map.keySet()){
	Integer value = map.get(key);
    sout ( key + value );
}

 

 

 

getOrDefault

찾는 키가 존재한다면 찾는 키의 값을 반환하고 없다면 기본 값을 반환하는 메서드

 

기본 형태

getOrDefault(Object Key, V DefaultValue)

- Key : 값을 가져와야 하는 요소

- defaultValue : 지정된 키로 매핑된 값이 없는 경우 반환되어야 하는 기본 값입니다.

 

기본 사용법

public class MapGetOrDefaultEx {
	public static void main(String arg[]) {
		String [] alphabet = { "A", "B", "C" ,"A"};
		HashMap<String, Integer> hm = new HashMap<>();
		for(String key : alphabet) hm.put(key, hm.getOrDefault(key, 0) + 1);
		System.out.println("결과 : " + hm);
        	// 결과 : {A=2, B=1, C=1}
	}
}

- 동일한 키 값을 추가할 경우 Value 의 값이 덮어쓰기 됩니다.

- 따라서 기존 key값의 value를 계속 사용하고 싶을 경우 getOrDefault 메서드를 사용해서 위의 예와 같이 사용할 수 있습니다.

'JAVA' 카테고리의 다른 글

HashSet  (0) 2022.08.11
Stream  (0) 2022.08.10
백트래킹  (0) 2022.07.27
람다식  (0) 2022.07.18
좋은 객체 지향 설계의 5가지 원칙  (0) 2022.07.08
백트래킹

해를 찾는 도중 해가 아니어서 막히면, 돌아가서 다시 해를 찾아가는 기법입니다.

최적화 문제와 결정 문제를 풀때 주로 사용합니다.

 

DFS

가능한 모든 경로를 탑색합니다.

그래서 불필요할 것 같은 경로를 사전에 차단하거나 하는 행동이 없어서 경우의 수를 줄이기 힘듭니다.

그래서 경우의 수를 요구하는 문제는 DFS 로 처리하기 힘들 것입니다.

 

 

 

 

백트래킹

해를 찾아가는 도중, 지금 경로가 해가 될 것 같지 않으면 그 경로를 더 이상가지 않고 되돌아갑니다.

즉, 반복문의 횟수까지 줄일 수 있으므로 효율적입니다.

이를 가지치기라고 부릅니다.

 

일반적으로 불필요한 경로를 조기에 차단할 수 있게 되어 경우의 수가 줄어들어 효율성이 결정됩니다.

 

정리하자면, 백트래킹이란 모든 가능한 경우의 수 중에서 특정 조건을 만족하는 경우만 살펴보는 것입니다.

 

'JAVA' 카테고리의 다른 글

Stream  (0) 2022.08.10
HashMap  (0) 2022.08.10
람다식  (0) 2022.07.18
좋은 객체 지향 설계의 5가지 원칙  (0) 2022.07.08
객체 지향 프로그래밍  (0) 2022.07.08
Lambda Expression

함수형 프로그래밍 기법을 지원하는 자바의 문법 요소

 

 

람다식이란?

메서드를 하나의 식으로 표현한 것으로, 코드를 간결하고 명확하게 표현할 수 있다는 장점이 있습니다.

JDK1.8 이후에 도입되었으며, 익명 객체이기 때문에 기존의 자바 문법 요소를 해치지 않는 프로그래밍 기법이

필요했습니다. 이에 따라 함수형 인터페이스가 만들어 졌습니다.

 

 

람다식의 기본 문법
//기존 메서드 표현 방식
void sayhello() {
	System.out.println("HELLO!")
}

//위의 코드를 람다식으로 표현한 식
() -> System.out.println("HELLO!")

기존의 메서드와 가장 두드러지게 나타나는 차이는

기본적으로 반환타입과 이름을 생략할 수 있다는 점입니다. 따라서 람다 함수를 익명 함수라고도 부릅니다.

 

 

람다식을 만드는 방법
// 기본 문법
int sum(int num1, int num2) {
	return num1 + num2;
}

// 람다식
(int num1, int num2) -> {
	num1+num2
}

1) 반환타입, 메서드명 제거 후 화살표 추가

2) return , 세미콜론 제거

 

// 기존 방식
void example1() {
	System.out.println(5);
}

// 람다식
() -> System.out.println(5);

: 실행문이 하나만 존재할 경우 중괄호 생략 가능

 

 

함수형 인터페이스

자바에서 함수는 반드시 클래스 안에서 정의되어야 하기 때문에 메서드가 독립적으로 있을 수 없습니다.

반드시 클래스 객체를 먼저 생성한 후 생성한 객체로 메서드를 호출해야합니다.

이러한 맥락에서 람다식 또한 사실은 객체입니다. 더 정확히는 이름이 없기 때문에 익명 클래스라 할 수 있습니다.

그래서 실제 우리가 사용하는 익명함수의 형태는 아래 코드와 같으며,

익명 클래스는 생성고 선언을 동시에 하는 단 한번만 사용된느 일회용 클래스입니다.

new Object() {
	int sum(int num1, int num2) {
		return num1 + num1;
	}
}

그런데 문제는 익명 객체를 Object 클래스에 담는다고 해도 sum 메서드를 사용할 방법이 없습니다.

이 같은 문제를 해결하기 위해 사용되는 것이 함수형 인터페이스입니다.

이 방법은 기존의 인터페이스 문법을 활용해 람다식을 다루는 것입니다.

아래의 예제 코드로 살펴볼 수 있습니다.

public class LamdaExample1 {
    public static void main(String[] args) {
		   /* Object obj = new Object() {
            int sum(int num1, int num2) {
                return num1 + num1;
            }
        };
			*/ 
		ExampleFunction exampleFunction = (num1, num2) -> num1 + num2
		System.out.println(exampleFunction.sum(10,15))
}

@FunctionalInterface // 컴파일러가 인터페이스가 바르게 정의되었는 지 확인할 수 있도록
interface ExampleFunction {
		public abstract int sum(int num1, int num2);
}

// 출력값
25

다시 순차적으로 설명해보겠습니다.

 

1. 함수형 인터페이스 작성

public interface MyFunctionalInterface {
    public void accept();
}

 

2. 인터페이스의 람다식 작성

MyFunctionalInterface example = () -> { ... };

람대식이 대입된 인터페이스의 참조 변수는 accept를 위와 같은 호출할 수 있습니다.

 

3. 람다식으로 호출

public class MyFunctionalInterfaceExample {
	public static void main(String[] args) throws Exception {
		MyFunctionalInterface example;
		example = () -> {
			String str = "첫 번째 메서드 호출!";
			System.out.println(str);
		};
		example.accept();

		example = () -> System.out.println("두 번째 메서드 호출!");
		//실행문이 하나라면 중괄호 { }는 생략 가능
		example.accept();
	}
}

// 출력값
첫 번째 메서드 호출!
두 번째 메서드 호출!

 

 

매개변수가 있는 람다식

1. 함수형 인터페이스에 매개변수가 존재

public interface MyFunctionalInterface {
    public void accept(int x);
}

 

2. 해당 인터페이스를 타겟 타입으로 갖는 람다식을 작성

public class MyFunctionalInterfaceExample {
    public static void main(String[] args) throws Exception {
        MyFunctionalInterface example;
        example = (x) -> {
            int result = x * 5;
            System.out.println(result);
        };
        example.accept(2);

        example = (x) -> System.out.println(x * 5);
        example.accept(2);
    }
}

// 출력값
10
10

 

 

 

return 문이 있는 람다식
public class MyFunctionalInterfaceExample {
    public static void main(String[] args) throws Exception {
        MyFunctionalInterface example;
        example = (x, y) -> {
            int result = x + y;
            return result;
        };
        int result1 = example.accept(2, 5);
        System.out.println(result1);
        

        example = (x, y) -> { return x + y; };
        int result2 = example.accept(2, 5);
        System.out.println(result2);
       

	      example = (x, y) ->  x + y;
				//return문 만 있을 경우, 중괄호 {}와 return문 생략가능
        int result3 = example.accept(2, 5);
        System.out.println(result3);
       

        example = (x, y) -> sum(x, y);
				//return문 만 있을 경우, 중괄호 {}와 return문 생략가능
        int result4 = example.accept(2, 5);
        System.out.println(result4);
 
    }

    public static int sum(int x, int y){
        return x + y;
    }
}

//출력값
7
7
7
7

여기서 주의 깊게 볼점은 return문만 있는 경우 중괄호를 생략한다는 점입니다.

 

 

메서드 레퍼런스

메서드 참조는 불필요한 매개변수를 제거할 때 주로 사용합니다.

람다식은 종종 기존 메서드를 단순히 호출만 하는 경우가 많습니다.

예를 들어, 아래 max 메서드를 호출하는 람다식에서 람다식의 역할은 단순히 매개 값 전달만 하여 불편해보입니다.

(left, right) -> Math.max(left, right);

그래서 이러한 경우 때문에 메서드 참조를 이용해 깔끔하게 처리할 수 있습니다.

Math :: max

 

 

메서드 레퍼런스의 정적 메서드와 인스턴스 메서드 참조
  1. 정적 메서드를 참조할 경우 클래스 이름 뒤에 :: 기호를 붙이고 정적 메서드 이름을 기술합니다.
  2. 인스턴스 메서드의 경우에는 먼저 객체를 생성한 다음 참조 변수 뒤에 ::기호를 붙이고 인스턴스 메서드 이름을 기술하면 됩니다.
public class Calculator {
  public static int staticMethod(int x, int y) {
                        return x + y;
  }

  public int instanceMethod(int x, int y) {
   return x * y;
  }
}

----------------------------------------------------------------

import java.util.function.IntBinaryOperator;

public class MethodReferences {
  public static void main(String[] args) throws Exception {
    IntBinaryOperator operator;

    /*정적 메서드
		클래스이름::메서드이름
		*/
    operator = Calculator::staticMethod;
    System.out.println("정적메서드 결과 : " + operator.applyAsInt(3, 5));

    /*인스턴스 메서드
		인스턴스명::메서드명
		*/
		
    Calculator calculator = new Calculator();
    operator = calculator::instanceMethod;
    System.out.println("인스턴스 메서드 결과 : "+ operator.applyAsInt(3, 5));
  }
}
/*
정적메서드 결과 : 8
인스턴스 메서드 결과 : 15
*/

 

메서드 레퍼런스의 생성자 참조

단순히 메서드 호출로 구성된 람다식은 메서드 참조로 대치할 수 있듯이,

단순히 객체를 생성하고 리턴하도록 구성된 람다식은 생성자 참조로 대치 가능합니다.

 

(a,b) -> {return new 클래스(a,b);};
// 이런 경우 아래로 바꾼다.
클래스 :: new

 

예제를 보겠습니다.

//Member.java
public class Member {
  private String name;
  private String id;

  public Member() {
    System.out.println("Member() 실행");
  }

  public Member(String id) {
    System.out.println("Member(String id) 실행");
    this.id = id;
  }

  public Member(String name, String id) {
    System.out.println("Member(String name, String id) 실행");
    this.id = id;
    this.name = name;
  }

  public String getName() {
    return name;
  }

public String getId() {
    return id;
  }
}
import java.util.function.BiFunction;
import java.util.function.Function;

public class ConstructorRef {
  public static void main(String[] args) throws Exception {
    Function<String, Member> function1 = Member::new;
    Member member1 = function1.apply("kimcoding");

    BiFunction<String, String, Member> function2 = Member::new;
    Member member2 = function2.apply("kimcoding", "김코딩");
  }
}

/*
Member(String id) 실행
Member(String name, String id) 실행
*/

 

스트림

배열, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자입니다.

 

  1. 스트림을 사용하면 List, Map, Set, 배열 등 다양한 데이터 소스로 부터 스트림을 만들 수 있고, 표준화된 방법으로 다룰 수 있습니다.
  2. 스트림은 데이터 소스를 다루는 풍부한 메서드를 제공합니다.
  3. 다량의 데이터에 복잡한 연산을 수행하면서도 가독성, 재사용성이 높은 코드를 작성할 수 있습니다.

 

선언형 프로그래밍

어떻게 수행하는지보다는 무엇을 수행하는지에 관심을 두는 프로그래밍 패러다임입니다.

명령형 방식은 하나하나 절차를 따라가야 코드를 이해할 수 있지만,

선언형 방식은 코드를 작성하면 내부 동작 원리를 모르더라도 코드가 무슨일을 하는지 이해할 수 있습니다.

즉, "어떻게" 영역은 추상화되있습니다.

 

// 기존의 리스트 조회 방식
public class ImperativeProgrammingExample {
    public static void main(String[] args){
        // List에 있는 숫자들 중에서 4보다 큰 짝수의 합계 구하기
        List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
        int sum = 0;

        for(int number : numbers){
            if(number > 4 && (number % 2 == 0)){
                sum += number;
            }
        }

        System.out.println("# 명령형 프로그래밍 : " + sum);
    }
}


// 스트림 방식
public class DeclarativeProgramingExample {
    public static void main(String[] args){
        // List에 있는 숫자들 중에서 4보다 큰 짝수의 합계 구하기
        List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);

        int sum =
                numbers.stream()
                        .filter(number -> number > 4 && (number % 2 == 0))
                        .mapToInt(number -> number)
                        .sum();

        System.out.println("# 선언형 프로그래밍: " + sum);
    }
}

 

 

람다식, 매서드 참조를 이용해 요소 처리
//Student.java
public class Student {
    private String name;
    private int score;

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

    public String getName(){
        return name;
    }

    public int getScore(){
        return score;
    }
}

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

public class StreamLambdaExample {
    public static void main(String[] args) throws Exception {
        List<Student> list = Arrays.asList(
            new Student("김코딩", 95),
            new Student("이자바", 92)
        );

        Stream<Student> stream = list.stream();
        stream.forEach( s -> {
            String name = s.getName();
            int score = s.getScore();
            System.out.println(name+ " - " +score);
        });
    }
}
/*
김코딩 - 95
이자바 - 92
*/

1) list를 stream형으로 바꿔서 stream 객체에 할당

2) stream 객체를 람다식으로 순환

 

 

내부 반복자를 사용하므로 병렬 처리가 쉽다는 장점이 있습니다.
  • 외부 반복자 : 개발자가 코드로 직접 컬렉션 요소를 반복해서 가져오는 것으로, [ for, Iterator, while ] 등이 해당됩니다.
  • 내부 반복자 : 컬렉션 내부에서 요소들을 반복시키고 개발자는 요소당 처리해야할 코드만 제공하는 코드 패턴입니다.

 - 내부 반복자의 장점

 

  1. 내부 반복자를 사용하게되면 컬렉션 내부에서 어떻게 반복시킬지는 컬렉션에 맡겨두고, 개발자는 요소 처리코드에만 집중할 수 있다는 점이 있습니다.
  2. 내부 반복자는 멀티 코어 CPU를 최대한 활용하기 때문에 병렬 작업을 통해 효율적으로 요소를 반복할 수 있습니다
  3. 중간 연산과 최종 연산을 할 수 있습니다.

* 병렬 처리

: 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬처리하는 것을 의미합니다.

 병렬 스트림을 사용하기 위해선 스트림의 parallel() 메서드를 사용해야 합니다.

 

 * 중간연산과 최종연산 예시

: 학생 객체를 요소로 가지는 컬렉션에서 중간 연산에서는 학생의 점수를 뽑아내고, 최종 연산에는 점수의 평균값을 산출할 수 있습니다.

 

 

리덕션

데이터를 가공해서 축소하는 것을 의미합니다.

평균값, 카운팅, 최대/최소 값 등이 대표적인 예시입니다.

그러나 컬렉션의 요소를 바로 집계할 수 없을 때는 filter, mapping, sort 등 중간 연산이 필요합니다.

 

 

파이프라인

스트림은 필터링,매핑,그루핑,정렬 등의 중간 연산과 합계, 평균, 카운팅, 최대/최소 값 등의 최종 연산을

파이프라인으로 해결합니다.

 

파이프 라인은 여러 스트림이 연결되있는 구조입니다.

파이프 라인에선 최종 연산을 제외하곤 모두 중간 연산입니다.

 

중간 스트림이 생성될 때 중간 연산이 되는게 아니라 최종 연산이 시작되기 전까지 지연됩니다.

최종 연산이 시작되면 비로소 컬렉션 요소가 하나씩 중간 스트림에서 연산되고 최종 연산에 오게 됩니다.

 

예제로 살펴보겠습니다.

Stream<Member> maleFemaleStream = list.stream();
Stream<Member> maleStream = maleFemaleSTream.filter(m -> m.getGender() == Member.MALE);
IntStream ageStream = maleStream.mapToInt(Member::getAge);
OptionalDouble opd = ageStream.average();
double ageAve = opd.getAsDouble();

.filter(m-> m.getGender() == Member.MALE) 는 남자 Member 객체를 요소로 하는 새로운 스트림을 생성합니다.

.mapToInt(Member::getAge) 는 Member 객체를 age 값으로 매핑해서 age를 요소로 하는 새로운 스트림을 생성합니다.

average() 메소드는 age 요소의 평균을 OptionalDouble에 저장합니다. OptionalDouble에 저장된 평균 값을 읽으려면 getAsDouble() 메소드를 호출 하면 됩니다(Optional은 이후에 자세히 다룹니다).

 

위 코드에서 로컬 변수를 생략하고 연결하면 다음과 같은 형태의 파이프라인 코드만 남습니다.

 

스트림의 주의할 점
  1. 스트림은 데이터 소스로부터 데이터를 읽기만 할 뿐 변경하지 않습니다.
  2. 스트림은 일회용입니다. 한번 사용하면 닫히므로 새로운 스트림을 열어야합니다.

* 단, 중간 스트림은 하나의 스트림에 여러번 사용할 수 있습니다.

 

 

스트림 생성
// List로부터 스트림을 생성
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> listStream = list.stream();
listStream.forEach(System.out::prinln); //스트림의 모든 요소를 출력.

 

 

중간 연산
  • distinct() : stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용합니다.
  • filter() : stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어냅니다.
  • map() : 기존의 stream 요소들을 대체하는 요소로 구성된 새로운 스트림을 형성하는 연산                                                    mapToInt(), mapToLong(), mapToDouble() 등이 있음
  • sorted() : Comparator 인자 없이 호출할 경우 오름차순으로 정렬되며, 내림차순 정렬은 reverseOrder 이용합니다.
  • peek() : 요소를 하나씩 돌면서 출력 

 

 

최종 연산

연산 결과가 스트림이 아니므로 한번만 연산이 가능합니다.

  • forEach() : 파이프라인 마지막에서 요소를 하나씩 연산
  • match() : 특정한 조건을 충족하는지 검사할 때 사용 [ allMatch, anyMatch, noneMatch ]
  • sum,count,average,max,min() : 기본 집계
  • reduce() : 하나의 응축으로 하는 방식입니다. count, sum 등 집계 메서드는 내부적으로 reduce가 있습니다.
  • collect() : List,Set,Map 등 다른 종류의 결과로 수집하고 싶은 경우 이용합니다.

 

'JAVA' 카테고리의 다른 글

HashMap  (0) 2022.08.10
백트래킹  (0) 2022.07.27
좋은 객체 지향 설계의 5가지 원칙  (0) 2022.07.08
객체 지향 프로그래밍  (0) 2022.07.08
String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
좋은 객체 지향 설계의 5가지 원칙 ( SOLID )

SRP, OCP, LSP, ISP, DIP

 

SRP ( Single repsonsibility principle ) 단일 책임 원칙
  1. 한 클래스는 하나의 책임만 가져야 한다.
  2. 하나의 책임이라는 것은 문맥과 상황에 따라 그 크기가 다를 수 있다.
  3. 중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.

        ex. 1) UI변경 : UI변경을 하는데 SQL 코드 등을 바꿔야한다면 그건 잘못 설계한 것이다.

              2) 객체의 생성과 사용을 분리

 

 

 

OCP ( Open/Closed Principle ) 개방-폐쇄 원칙 **
  1. 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
  2. 다형성을 활용해 인터페이스를 구현한 새로운 클래스를 하나 만들어 새로운 기능을 구현
  3. 역할과 분리를 이용한다.
  • OCP 문제점
  1. 예제에서 MemberService client에 Repository를 변경할때마다 구현 클래스를 변경하며 직접 선택
  2. 결국 구현 객체를 변경하려면 클라이언트 코드가 변경되야함
  3. 분명 다형성을 사용했지만 OCP원칙을 지킬 수 없음
  4. 해결하기 위해서는 객체를 생성하고, 연관관계를 맺어주는 별도의 조립/설정자가 필요함

        => 별도로 해주는게 스프링 컨테이너가 해주는 역할

            즉, OCP원칙을 지키기 위해 DI,Container가 필요한 것이다.

 

 

 

 

LSP 리스코프 치환 원칙
  1.  프로그램의 객체는 프로그램의 정확성을 깨드리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함
  2.  다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야하며 인터페이스를 구현한 구현체는 믿고 사용하려면 이 원칙이 필요함
  3. 단순히 컴파일에 성공하는 것을 넘어서는 이야기
  • 예시

자동차 인터페이스의 엑셀은 앞으로 가라는 기능인데, 뒤로 가게 구현하면 LSP 위반임

느리더라도 무조건 앞으로 가야함

 

 

 

 

ISP 인터페이스 분리 원칙
  1. 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
  2. 자동차 인터페이스 - 운전 인터페이스 / 정비 인터페이스로 분리
  3. 사용자 클라이언트 - 운전자 클라이언트 / 정비사 클라이언트로 분리
  4. 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음
  5. 인터페이스가 명확해지고 대체 가능성이 높아진다.

 

 

 

DIP 의존관계 역전 원칙 **
  1. 의존성 주입은 "추상화에 읜존해야지, 구체화에 의존하면 안된다" 라는 원칙을 따르는 방법이다.
  2. 즉, 구현클래스에 의존하지말고 인터페이스에 의존하라는 의미
  3. 역할에 의존하게 해야 한다는 것과 같다. 객체에서 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있음
  • 예시

MemberService client 코드를 바꿔야 했던 이유는 MemberService가 MemberRepository에도 의존을 하고, MemoryMeberMemberRepository도 의존을 하기때문에 

Repository를 변경하려고 할때마다 코드를 변경해야한다는 것입니다. 즉 인터페이스에 의존하지 못하는 상태로 DIP위반입니다.

 

 

 

* 정리

1) 객체 지향의 핵심은 다형성
2) 다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없다.
3) 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.
4) 다형성 만으로는 OCP,DIP를 지킬 수 없다.
5)  스프링의 DI, Container 기능을 통해 가능하게 지원할 수있다.

 

 

 

 

 

 

 

 

 

 

 

 

'JAVA' 카테고리의 다른 글

백트래킹  (0) 2022.07.27
람다식  (0) 2022.07.18
객체 지향 프로그래밍  (0) 2022.07.08
String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
객체 지향 프로그래밍(OOP) 을 하는 이유는 뭘까?

프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용됩니다.

 

 


객체 지향 프로그램을 설계하는 핵심적인 4가지 [ 다형성 ] [ 캡슐화 ] [ 상속 ] [ 추상화 ]

 

다형성

ㅇ 운전자와 자동차 예시

 

 운전자는 BMW를 타다가 아우디로 차를 바꾼다.

 차를 바꾼다고 해서 운전자가 운전을 못하게 되는 것은 아니다.

 여기서 요점은 자동차가 바뀌어도 운전자에게 영향을 안준다는 것이다. 

 이러한 것을 보고 "유연하고 변경이 용이하다" 라고 한다.

 

 이러한 다형성은 "클라이언트를 위해" 이뤄진다는 것이다.

 프로그램을 확장시키고, 새로운 기능을 추가를 해도 클라이언트는 무언가를 변경할 필요가 전혀 없다는 것입니다.

 이런게 가능 한 이유는 역할과 구현으로 세상을 구분했기 때문에 가능한 것입니다.

 여기서 중요한 요점은 새로운 기능을 추가한다는게 아니라 [ 새로운 기능이 추가되어도 클라이언트에게 전혀 영향을 미치지 않는다 ]

 라는 것입니다.

 

 

ㅇ 역할과 구현을 분리

  • 장점
  1. 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
  2. 클라이언트는 내부 구조를 몰라도 된다.
  3. 클라이언트는 내부 구조가 변경되어도 전혀 영향을 받지 않는다.
  4. 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다. 
  • 자바언어 활용
  1. 자바 언어의 다형성을 활용 [ 역할=인터페이스 | 구현=인터페이스를 구현한 클래스, 구현 객체 ]
  2. 객체를 설계할 때 역할과 구현을 명확히 분리
  3. 객체 설계시 역할을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기
  • 자바 언어의 다형성
  1. 오버라이딩 된 메서드 실행
  2. 다형성으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.
  3. 클래스 상속 관계도 다형성이고, 오버라이딩 적용 가능하다.
  • 다형성의 본질
  1. 인터페이스를 구현한 객체 인스턴스 "실행시점"에 유연하게 변경할 수 있다.
  2. 협력이라는 객체사이의 관계에서 시작해야 한다.
  3. 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다
* 스프링과 객체 지향
스프링에서 제어의 역전(IoC), 의존 관계 주입(DI) 는 다형성을 활용해 역할과 구현을 편리하게 다룰 수 있도록 지원한다.

 

상속

기존의 클래스를 재사용하여 새로운 클래스를 작성하는 자바의 문법 요소

상위 클래스와 하위 클래소르 나누어 상위 클래스의 멤버를 하위 클래스에게 내려주는 것을 의미합니다.

 

ㅇ 클래스의 트리구조로 이해하기

위의 그림을 보게되면 세개의 클래스에 공통적인 속성과 기능이 정의되어 있다는 것을 알 수 있습니다.

즉, 사람이라면 누구나 먹고 배우고 걷는 특성이 있습니다. 

 

  • 왜 상속을 사용할까?

상속을 통해 앞선 예제처럼 코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있습니다.

더 나아가 다형적 표현이 가능하다는 장점이 있습니다.

 

 * 다형적 표현이 가능하다 ?
프로그래머는 프로그래머다 = 참
프로그래머는 사람이다 = 참
이와 같이 하나의 객체가 여러 모양으로 표현될 수 있는것을 의미합니다.

 

  • 상속에서 주의할 점 !!

자바의 객체지향 프로그래밍에서는 다중 상속을 허용하지 않고, 단일 상속만을 허용한다는 것입니다. 

하지만 인터페이스를 통해 다중상속과 비슷한 효과를 낼 수 있습니다.

 

  • 상속과 함께 알아두면 좋을 포함관계

상속처럼 클래스를 재사용할 수 있도록 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것

public class Employee {
    int id;
    String name;
    Address address;  // 참조 변수 !!

    public Employee(int id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    void showInfo() {
        System.out.println(id + " " + name);
        System.out.println(address.city+ " " + address.country);
    }

    public static void main(String[] args) {
        Address address1 = new Address("서울", "한국"); //Address 할당 후
        Address address2 = new Address("도쿄", "일본");

        Employee e = new Employee(1, "김코딩", address1); // Employee와 함께 재할당
        Employee e2 = new Employee(2, "박해커", address2);

        e.showInfo();
        e2.showInfo();
    }
}

class Address {
    String city, country;

    public Address(String city, String country) {
        this.city = city;
        this.country = country;
    }
}

 

  • 상속과 포함관계를 어떻게 구분해서 설정할 수 있을까 ?
  1. ~은 ~이다 ( IS-A 관계 ) : "Employee 는 Address이다" 는 성립하지 않는다.
  2. ~은 ~을 가지고 있다. ( HAS-A 관계 ) : "Employee는 Address를 가지고 있다" 는 성립

 이렇게 HAS-A 관계에서는 포함관계를 사용해주는게 적합합니다.

 

 

  • super 키워드

상속 관계에서 상위 클래스의 객체, super() 은 상위 클래스의 생성자를 의미합니다.

public class Super {
    public static void main(String[] args) {
        Lower l = new Lower();
        l.callNum();
    }
}

class Upper {
    int count = 20; // super.count
}

class Lower extends Upper {
    int count = 15; // this.count

    void callNum() {
        System.out.println("count = " + count);
        System.out.println("this.count = " + this.count);
        System.out.println("super.count = " + super.count);
    }
}
  • 위의 예제에서 super을 붙치지 않는다면?

 

자바 컴파일러가 해당 객체의 자신이 속한 인스턴스 객체의 멤버를 먼저 참조합니다.

즉, 자동적으로 this 역할을 하게 되는것입니다.

  • 상위클래스의 멤버 = super 
  • 자신의 멤버 = this

 

캡슐화

특정 객체 안에 관련된 속성과 기능을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것

 

  • 캡슐화를 왜 하는가?
  1. 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 데이터를 보호
  2. 데이터가 변경되어도 다른 객체에 영향을 주지 않게 독립성을 확보
  3. 결론적으로 유지보수와 코드 확장시에 오류의 범위를 최소화 할 수 있음

 

  • 접근 제어자

데이터 노출을 방지할 수 있는 핵심적인 방법

  1. private : 동일 클래스에서만 접근 가능
  2. default : 동일 패키지내에서만 접근 가능
  3. protected : 동일 패키지 + 다른 패키지의 하위 클래스에서만 접근 가능
  4. public : 접근 제한 없음

 여기서 사실 private / default / public 은 이해가 되는데 protected가 잘 이해되지 않았습니다.

예제로 살펴보겠습니다.

package package1; // 패키지명 package1 

//파일명: Parent.java
class Test { // Test 클래스의 접근 제어자는 default
    public static void main(String[] args) {
        Parent p = new Parent();

        System.out.println(p.c);
    }
}

public class Parent { // Parent 클래스의 접근 제어자는 public
    protected int c = 3;

    public void printEach() { // 동일 클래스이기 때문에 에러발생하지 않음
        System.out.println(c);
    }
}

package package2; // package2 

//파일명 Test2.java
import package1.Parent;

class Child extends package1.Parent { // package1으로부터 Parent 클래스를 상속
    public void printEach() {
        System.out.println(c); // 다른 패키지의 하위 클래스
    }
}

public class Test2 {
    public static void main(String[] args) {
        Parent p = new Parent();
        
//        System.out.println(p.c); // 호출 에러!
    }
}

다른 패키지에서 prarent를 상속받은 클래스에서는 protect 멤버를 호출이 가능

단, 다른 클래스에서는 상속받지 않은 클래스의 경우 호출 에러가 나옵니다.

 

 * getter, setter메서드  :  private 객체의 데이터 값을 추가, 수정하고 싶은 경우에 사용

 * final 키워드
 - 클래스 : 변경/확장/상속 불가능
 - 메서드 : 오버라이딩 불가능
 - 변수 : 값 변경 불가능

 

 

 

추상화

객체의 공통적인 속성과 기능을 추출하여 정의하는 것

 

  • abstract, interface

- abstract : 추상메서드를 만드는 미완성 설계도입니다. 

- interface : 기본 설계도

 

  • 추상화를 왜 사용하는가?

메서의 내용이 상속 받는 클래스에 따라 종종 달라지기 때문에 상위 클래스에서 선언부만을 작성하고

실제 구현 내용은 상속을 받는 하위 클래스에서 구현하도록 비워둔다면 설계하는 상황이 변하더라도

유연하게 대응할 수 있습니다.

 이때 사용되는 것이 "오버라이딩" 입니다.

 

  • Interface 의 특징

1) class 대신 inteface 키워드를 사용하며 모든 필드가 public static final로 정의됩니다.

2) extends 키워드 대신 implements 키워드를 사용합니다.

3) 구현 클래스에서 는 해당 인터페이스에 정의된 모든 추상메서드를 반드시 구현해야합니다.

4) 하나의 클래스가 여러 인터페이스를 구현할 수 있는 다중 구현이 가능합니다.

    단, 인터페이스는 인터페이스로부터만 상속이 가능합니다.

 

 

* 인터페이스는 왜 다중 구현이 가능할까?
: 클래스에서 다중 상속이 불가능했던 이유는 부모 클래스에 동일한 이름의 필드,메서드가 있는 경우 충돌할 수 있기 때문입니다.
하지만 인터페이스는 애초에 미완성 멤버를 가지고 있기 때문에 충돌할 여지가 없습니다.

 

  • 추상클래스와 인터페이스의 사용의도 차이
  1. 추상클래스 : IS-A "~이다" 의 개념
  2. 인터페이스 : HAS-A "~을 할 수 있는" 의 개념

 

 

 

 

'JAVA' 카테고리의 다른 글

람다식  (0) 2022.07.18
좋은 객체 지향 설계의 5가지 원칙  (0) 2022.07.08
String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
Optional 이란?  (0) 2022.07.02
public String[] test(String str) {
    if (str.isEmpty()) {
      return new String[]{};
    }

    return str.split(" ");
  }

- 여기서 주의깊게 볼것은 str.isEmpty() 의 활용성입니다. 이와 비슷하게 isBlank() 라는 메서드도 존재하는데

둘을 비교해보자면

 

1) isEmpty()

문자열의 길이를 체크하여, 문자열의 길이가 0인 경우에만 true를 리턴하기 때문에

빈 공백이 들어있는 문자열은 false를 리턴합니다.

 

2) isBlank()

문자열이 비어 있거나, 빈 공백(white space)를 포함하고 있는 경우에 true를 리턴하기 때문에

위 케이스의 경우 true를 리턴합니다.

 

즉, if문은 문자열이 빈칸일때에 반환타입인 빈 String[]을 반환하는 것입니다.

 

 

- str.split(" ")

: str에 빈 공백(" ") 을 기준으로 나눈다는 의미입니다.

'JAVA' 카테고리의 다른 글

좋은 객체 지향 설계의 5가지 원칙  (0) 2022.07.08
객체 지향 프로그래밍  (0) 2022.07.08
Hamcrest, Matcher란?  (0) 2022.07.03
Optional 이란?  (0) 2022.07.02
상속을 응용한 스택 구현  (0) 2021.10.07

ㅇ Matcher 클래스

 

: Matcher 클래스는 대상 문자읠 패턴을 해석하고 주어진 패턴과 일치하는지 판별할 때 주로 사용됩니다.

입력 값으로는 CharSequence라는 새로운 인터페이스가 주로 사용되는데, 다양한 형태의 입력 데이트로부터 문자 단위의 매칭 기능을

지원 받을 수 있습니다.

 

 

ㅇ Matcher 클래스 주요 메서드

 

matches() : 대상 문자열과 패턴이 일치할 경우 true 반환합니다.
find() : 대상 문자열과 패턴이 일치하는 경우 true를 반환하고, 그 위치로 이동합니다.
find(int start) : start위치 이후부터 매칭검색을 수행합니다.
start() : 매칭되는 문자열 시작위치 반환합니다.
start(int group) : 지정된 그룹이 매칭되는 시작위치 반환합니다.
end() : 매칭되는  문자열 끝 다음 문자위치 반환합니다.
end(int group) : 지정되 그룹이 매칭되는 끝 다음 문자위치 반환합니다.
group() : 매칭된 부분을 반환합니다.
group(int group) : 매칭된 부분중 group번 그룹핑 매칭부분 반환합니다. 
groupCount() : 패턴내 그룹핑한(괄호지정) 전체 갯수를 반환합니다.

 

 

 

 

ㅇ Hamcrest

: 소프트웨어 테스트를 위한 framework

: 기존의 matchers 클래스를 통해 코드의 조건을 확인 할 수 있음

: JUnit 에서 Hamcrest matcher를 사용하려면 assertThat 문 뒤에 하나 또는 여러 개의 matchers 를 사용한다.

: Spring boot 에서 사용하기 위해선 dependenct 를 추가해줘야 한다.

 

 

 

ㅇ Hamcrest matcher 모음

allOf - matches if all matchers match (short circuits)
anyOf - matches if any matchers match (short circuits)
not - matches if the wrapped matcher doesn’t match and vice
equalTo - test object equality using the equals method
is - decorator for equalTo to improve readability
hasToString - test Object.toString
instanceOf, isCompatibleType - test type
notNullValue, nullValue - test for null
sameInstance - test object identity
hasEntry, hasKey, hasValue - test a map contains an entry, key or value
hasItem, hasItems - test a collection contains elements
hasItemInArray - test an array contains an element
closeTo - test floating point values are close to a given value
greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo
equalToIgnoringCase - test string equality ignoring case
equalToIgnoringWhiteSpace - test string equality ignoring differences in runs of whitespace
containsString, endsWith, startsWith - test string matching

* 참조 : http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html

 

Matchers (Hamcrest)

org.hamcrest Class Matchers java.lang.Object org.hamcrest.Matchers public class Matchersextends java.lang.Object Constructor Summary Matchers()               Method Summary static allOf(java.lang.Iterable > matchers)           Create

hamcrest.org

 

 

'JAVA' 카테고리의 다른 글

객체 지향 프로그래밍  (0) 2022.07.08
String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Optional 이란?  (0) 2022.07.02
상속을 응용한 스택 구현  (0) 2021.10.07
배열 큐  (0) 2021.10.07

ㅇ Optinal 이란?

- 값이 있을 수도, 없을 수도 있는 객체이다. 

- 원소를 최대 1개 가질 수 있는 불변 Collection 이다.

- null 에 대한 처리를 안전하게 할 수 있게 해준다.

 

 * Optional 을 리턴하는 메서드에서는 null을 리턴해서는 안된다. ( Optional 취지와 맞지 않기 때문이다. )

 

 

 

ㅇ 왜 Optional 을 사용하는가?

 : 검사 예외와 취지가 비슷하다. 즉 반환값이 있을 수도 있고, 없을 수도 있음을 API 사용자에게 명확히 알려준다.

 

만약 기존 로직처럼 사용하여 비검사 예외를 던지거나 null 을 반환하는 경우

 => 사용자가 그 사실을 인지못해 런타임에서 예상치 못한 장애로 발전할 수 있음

 

검사 예외 ( checked Exception ) 을 던지는 경우

 => 사용자 코드에서는 try-catch 구문을 통해 예외처리하는 로직을 추가해야하는데,

      비슷하게 메서드가 Optional 을 반환한다면 클라이언트는 값을 받지 못했을 때의 취할 행동을 선택해야한다.

 

 

 

 

 

ㅇ Optinal 기본값을 설정하는 방법

String lastWord = max(words).orElse("단어 없음..");

 : 값을 받지 못했을 때에 단어 없음이라는 행동이 선택된다.

 

 

 

 

 

 

ㅇ Optional 메서드

 

1) Optional.empty()

 - 내부 값이 비어있는 Optional 객체 반환

 

2) Optional.of(T value)

 - 내부 값이 value 인 Optional 객체 반환

 - 만약 value가 null인 경우 NPE 발생 ( NullPointerException )

 

3) Optional.ofNullable(T value)

 - 가장 자주 쓰이는 Optional 생성 방법

 - value 가 null 이면, empty Optional 을 반환하고 값이 있으면 Optional.of 로 생성한다.

 

 

 

 

 

'JAVA' 카테고리의 다른 글

String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
상속을 응용한 스택 구현  (0) 2021.10.07
배열 큐  (0) 2021.10.07
Stack  (0) 2021.10.06
package practice;

import java.util.Scanner;

public class Main{
    static public String mArr[];
    static public int mCnt=0; //자식클래스가 쓰기 위한 저역 변수
    static private int mN;
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);

        int max = sc.nextInt();

        mArr = new String[max];

        stack stk = new stack();

        for(int i=0; i<max; i++){
            String tmp = sc.next();
            stk.push(tmp);
        }
        mCnt--;
        for(int i=0; i<max; i++){
            stk.pop();
        }
    }
}
package practice;

public class stack extends Main { //자식클래스
    public void push(String input) {
        try{
            mArr[mCnt] = input;
            System.out.println("입장: " + mArr[mCnt++]);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }
    }

    public void pop(){
        try{
            System.out.println("퇴장: " + mArr[mCnt--]);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }
    }
}

'JAVA' 카테고리의 다른 글

String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
Optional 이란?  (0) 2022.07.02
배열 큐  (0) 2021.10.07
Stack  (0) 2021.10.06
package practice;

import java.util.Scanner;

// .idea 폴더 : 프로젝트의 구조 정보 등 프로젝트 관련 메타데이터가 들어 있음
// iml 파일 : 인텔리제이 모듈 구성에 대해 xml 형태로 기술해 놓은 파일
// @override: 상속을 받아서 메서드를 만들때 모든 메서드는 같은걸 상속받게되는데 이때 맘에 들지않는 것을 내가 원하는대로 재정의하겠다는 의미

/*
static : 이미 객체화 되어 바로 사용가능
non-static : 아직 객체화되지 않아 new 키워드를 통해 생성한 후 사용 가능
 */

/*
Scanner(System.in) : Scanner 를 통해 값을 입력받음
-> sc.nextInt / sc.next / sc.nextLine 과 같이 응용하여 사용한다
*/
/*
 static : 그냥 정적 변수로 static protecd 로 선언되므로 다른 클래스에서 불러올 수 없다
 static public : 다른 클래스에서 불러올 수 있는 정적변수
*/
/*
throw : Exception 을 발생시킬때 사용하는 키워드
throws : 메소드를 정의할 때 사용하며, 이 메소드에서 발생할 수 있는 Exception 을 명시적으로 정의할 때 사용합니다.
*/
/*
 - 에러 : 1.개발자가 해결할 수 없는 오류
         2.하드웨어의 잘못된 동작 또는 고장으로 인한 오류
         3. 에러 발생하여 프로그램 종료
         4. 정상상태로 돌아갈 수 없음
 - 예외 : 1.개발자가 해결할 수 있는 오류
         2.사용자의 잘못된 조작이나 코딩 오류
         3.예외 처리 추가하면 정상 실행 상태로 돌아갈 수 있음
 */
/*
if-else : 모든 예외상황을 예측하고 있어야한다.
try-catch : try구문에서 예외가 Throw 되면 그 즉시 블럭이 종료되고, catch 블럭으로 제어가 이동됨 ( try: 블럭 )
 */
/*
try{} catch{} : try에서 예외가 발생하면 Catch 블록의 참조변수로 전달한다.
 -> 하나의 try 에 여러 catch가 있을 수 있지만 동시에 발생하지는 않음
 -> 예외 발생 여부와 상관없이 무조건 살행해야하는 문장이 있다면 finally를 사용
  ex. try{ } catch{ } catch{ } finally{" 무조건 실행 "}
 */
/*
ArrayIndexOutOfBoundsException : 배열의 범위를 벗어난 인덱스 사용시 발생
NumberFormatException : 메소드의 인수를 숫자로 바꿀 수 없을때 발생
ArithmeticException : 인수가 0일 때 나눌 수 없으므로 발생
 */
/*
throws : 메소드에서 발생한 메소드를 내부에서 처리하기 어려울 때 사용해서 던져줌 ( 말 그대로 throw 해줌 )
         단, 메소드에 선언한 예외보다 더 괌범위한 검사형 예외를 던질 수는 없음
EX.
public void method(){
    try{
        method2();
    }catch(ClassNotFoundException e){
        System.out.println("클래스가 존재하지 않음");
    }
}
public void method2() throw ClassNotFoundException {
    Class clazz = Class.forName("java.lang.String2");
}
 */
public class Main {
    static int[] mArr;
    static int mN,mFront=0,mTail=0;

    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        mN = input.nextInt();
        mArr = new int[mN];

        Main enq = new Main();
        Main deq = new Main();
        /*
        enqueue 와 dequeue 가 전역 메소드로 선언된것이 아니기 때문에
        각 함수를 클래스에 객체화 시켜서 선언하였다.
        전역 메소드를 안쓰는 이유는 그 메소드를 추적하기 힘들어지기 때문에 미리 전역메소드를 안쓰는 식으로 연습하는 것이다.
         */

        for(int i=0; i<mN; i++){
            int enq_num = input.nextInt();
            enq.enqueue(enq_num);
        }

        for(int i=0; i<mN; i++){
            System.out.println(deq.dequeue());
        }
    }

    public void enqueue(int num){
        try{
            mArr[mTail] = num;
            System.out.println("enqueue: "+ mArr[mTail++]);
        } catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e); // e만 출력해줘도 예외 처리를 해줘야한다는 것을 알려준다.
        }
    }

    public int dequeue(){
        try{
            int outnum = mArr[mFront];
            for(int i=0; i<mTail-1;i++){
                mArr[i] = mArr[i+1];
            }
            mTail--;
            return outnum;
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e);
            return 0;
        }
    }
}

'JAVA' 카테고리의 다른 글

String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
Optional 이란?  (0) 2022.07.02
상속을 응용한 스택 구현  (0) 2021.10.07
Stack  (0) 2021.10.06
package stack;

import java.util.Scanner;

// .idea 폴더 : 프로젝트의 구조 정보 등 프로젝트 관련 메타데이터가 들어 있음
// iml 파일 : 인텔리제이 모듈 구성에 대해 xml 형태로 기술해 놓은 파일
// @override: 상속을 받아서 메서드를 만들때 모든 메서드는 같은걸 상속받게되는데 이때 맘에 들지않는 것을 내가 원하는대로 재정의하겠다는 의미

/*
static : 이미 객체화 되어 바로 사용가능
non-static : 아직 객체화되지 않아 new 키워드를 통해 생성한 후 사용 가능
 */

public class Main {

    static int[] arr;
    static int count,incnt;

    public static void main(String[] args){
        Scanner input = new Scanner(System.in);

        count = input.nextInt(); // 입력받은 값을 INT 로 받겠다는 의미 ( next(), nextByte(), nextFloat(), nextLine() 등이 존재 )

        arr = new int[count]; // 초기값 0으로 할당됨

        incnt = 0;

        push(5);
        push(1);
        push(3);
        push(9);

        for(int i=0; i<incnt; i++){
            System.out.print(arr[i]+" ");
        }

        System.out.println(pop());
        System.out.println(pop());
        System.out.println(pop());
        System.out.println(pop());
    }

    public static void push(int input){
        if(incnt == count){
            System.out.println("스택이 초과하였습니다.\n");
            throw new SecurityException();
        }else{
            arr[incnt++] = input;
        }
    }

    /*
    throw : 에러를 고의로 발생시킴
    throws : 자신을 호출한 상위 메소드로 에러를 던지는 역할
     */
    public static int pop(){
        if(incnt <= 0){
            System.out.println("빈 스택입니다.");
            throw new SecurityException();
        }
        return arr[incnt--];
    }
}

'JAVA' 카테고리의 다른 글

String 중간 공백기준으로 배열 만들기  (0) 2022.07.06
Hamcrest, Matcher란?  (0) 2022.07.03
Optional 이란?  (0) 2022.07.02
상속을 응용한 스택 구현  (0) 2021.10.07
배열 큐  (0) 2021.10.07

+ Recent posts