Java_문법뽀개기 06

5w31892p·2022년 11월 16일
0

Java

목록 보기
7/17

📜 Java

:: ✍ 컬렉션 - 개념 설명

:: 컬렉션 프레임워크

  • 다수의 데이터를 다루기 위한 자료구조를 표현하고 사용하는 클래스의 집합
  • 데이터를 다루는데 필요한 풍부하고 다양한 클래스와 기본함수를 제공
  • 컬렉션 프레임워크의 모든 클래스는 Collection
  • interface를 구현(implement)하는 클래스 또는 인터페이스
  • 실제 자바 어플리케이션 개발할 때 숨쉬듯이 계속 사용하고 활용할 것

:: 컬렉션 인터페이스와 자료구조

  • 모든 자료구조가 구현(implement)하는 인터페이스
  • 컬렉션 인터페이스는 데이터 다루는 기본적인 메소드 정의
  • 아래 모든 자료구조에 해당하는 클래스, 인터페이스는 언제나 컬렉션 인터페이스 구현함
List
- 순서가 있는 데이터 집합
- 데이터 중복 허용
- ArrayList, LinkedList, Stack 등

Set
- 순서 유지하지 않는 데이터 집합
- 데이터 중복 허용 안함
- HashSet, TreeSet 등

Map
- key와 value의 쌍으로 이뤄진 데이터 집합 (파이썬의 딕셔너리)
- 순서 필요 없음
- key 중복 안되지만, value는 중복 허용
- HashMap, TreeMap 등

Stack
- 마지막에 넣은 데이터 먼저 꺼내는 자료구조
- LIFO(Last In First Out)
- Stack, ArrayDeque 등

Queue
- 먼저 넣은 데이터 먼저 꺼내는 자료구조
- FIFO(First In First Out)
- ack, ArrayDeque 등

:: ✍ List

  • 순서가 있는 나열된 데이터
  • 데이터 중복 허용
  • primitive타입 int 사용
  • ArrayList는 배열을 이용하여 데이터를 저장하는 List 인터페이스
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> integerList = new ArrayList<>(); // List 다음은 해당하는 타입

        integerList.add(1);
        integerList.add(5);
        integerList.add(4);
        integerList.add(11);
        integerList.add(10);

        // ArrayList에 값 한개씩 입력
        // 리스트는 add하는 순간 사이즈가 정해짐
        // 자료가 많으면 많을수록 그 크기도 늘어남

        System.out.println(integerList);
        // [1,5,4,11,10] 넣어준 순서대로 출력

        Collections.sort(integerList); // integerList 정렬
        System.out.println(integerList);
        // [1, 4, 5, 10, 11]

        System.out.println(integerList.size());
        // 5 element 개수만큼 size 출력

        integerList.remove(4);
        System.out.println(integerList);
        // [1, 4, 5, 10]
        // 인덱스를 활용하여 해당하는 값 제거
        for (int i = 0; i < integerList.size(); i++) {
            System.out.println(integerList.get(i));
            // get을 이용하여 값 1개씩 출력
        }
        for (int current : integerList) {
            System.out.println(current); // 위와 같은 결과
        }
    }
}

:: ✍ Set

  • 순서를 유지하지 않는 데이터의 집합
  • 데이터 중복 불가
  • HashSet은 Set 인터페이스를 구현한 대표적인 컬렉션

    컬랙션프레임워크 쓰기 위해서는
    자료구조를 담는 것은 객체이다 보니 객체를 담을 수 있는 타입이여야함
    레퍼런스타입, 즉 참조형 자료
    list에서는 primitive타입인 int로 담았는데 이는 참조형 자료가 아니기 때문에 사용할 수 없음

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Integer> integerSet = new HashSet<>(); // Set 다음은 해당하는 타입
        integerSet.add(1);
        integerSet.add(1);
        integerSet.add(3);
        integerSet.add(2);
        integerSet.add(9);
        integerSet.add(8);
        System.out.println(integerSet);
        // [1, 2, 3, 8, 9]
        // 중복된 숫자는 1개만 출력
        // 내가 넣어준 순서는 유지되지 않음


        Set<String> stringSet = new HashSet<>();
        stringSet.add("LA");
        stringSet.add("New York");
        stringSet.add("LasVegas");
        stringSet.add("San Francisco");
        stringSet.add("Seoul");
        System.out.println(stringSet);
        // [San Francisco, New York, LasVegas, LA, Seoul]
        // 여전히 순서 유지되지 않음

        stringSet.remove("Seoul");
        System.out.println(stringSet);
        // [San Francisco, New York, LasVegas, LA]
        // 순서가 없기 때문에 인덱스로 지울 수 없음
        // 정확하게 지정해줘야 함함

        List<String> target = new ArrayList<>(); // 여러개 삭제
        target.add("New York");
        target.add("LasVegas"); // 제거할 항목들 삽입
        stringSet.removeAll(target);
        // set이나 다른것이 아니더라도 컬렉션타입이면 괄호 안에 뭐든지 올 수있음
        System.out.println(stringSet);
        // [San Francisco, LA]

        System.out.println("LA가 포함되어있나요? " + stringSet.contains("LA"));
        System.out.println("LasVegas가 포함되어있나요? " + stringSet.contains("LasVegas"));
        // contains() 자료가 포함되었는지 확인하는 함수, true or false return
        // LA가 HashSet에 포함되어있으면 true 아니면 false 반환

        System.out.println("현재 HashSet의 크기는 : " + stringSet.size() + "입니다.");
        //HashSet의 크기 반환

        stringSet.clear();// 자료구조 다 지우기
        System.out.println(stringSet);
        // [] 빈 자료구조 표현
    }
}

:: ✍ Map

  • key와 value의 쌍으로 이뤄진 데이터 집합 (파이썬의 딕셔너리)
  • 순서 필요 없음
  • key 중복 안되지만, value는 중복 허용
  • key와 value를 하나의 데이터로 저장
  • 하나의 데이터로 저장 하는 것을 통해 해싱(hashing)을 가능하게 하여 데이터를 검색하는데 뛰어난 성능
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>(); // key, value 두개 받아와야 하니 타입 2개 적음
        map.put(1, "apple");
        map.put(2, "berry");
        map.put(3, "cherry");
        // map은 add가 아니가 put을 해서 key, value 값 넣어줌
        System.out.println(map);
        // {1=apple, 2=berry, 3=cherry}

        System.out.println("1st in map: " + map.get(1));
        // 1st in map: apple
        // 첫번째 값 가져오기
        // key로 불러오기 때문에 0을 넣으면 null이 나옴

        map.remove(2); // 지울 key값 적기
        System.out.println(map);
        // {1=apple, 3=cherry}
        System.out.println(map.containsKey(2));
        // false
        // 해당 키값 있는지 없는지 확인
        System.out.println(map.containsValue("cherry"));
        // true
        // 해당 벨류 있는지 없는지 확인
        // 여기에 key값을 넣으면 value는 String타입인데 Integer타입을 넣었으니 없을거야 라고 알려줌

        map.clear(); // 모든 자료구조 삭제
        System.out.println(map);
        // {}
    }

:: ✍ Stack

  • 마지막에 저장한 데이터를 가장 먼저 꺼내는 자료구조
  • 이러한 자료구조를 LIFO(Last In First Out) 라고함
  • 그릇 쌓기/ 빨래통
  • 가장 먼저 넣은 값인 17이 가장 아래로 가고,
  • 이후 삽입되는 값들이 그 위로 쌓임
  • pop()을 통해 값을 반환할 때도 마지막에 삽입된 값인 45가 가장 먼저 반환

import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>(); // Stack 다음은 해당하는 타입 넣기
        stack.push(1); // push로 자료 추가
        stack.push(3);
        stack.push(7);
        stack.push(5);
        System.out.println(stack);
        // [1, 3, 7, 5] 넣은 순서대로 출력

        System.out.println(stack.peek());
        // 5 가장 상단에 있는 값 확인만하기

        System.out.println("size : " + stack.size());
        // 4
        // peek은 확인만 하는 것이기 때문에 4 출력

        System.out.println(stack.pop());
        // 5 삭제한 값 반환
        // 가장 상단에 있는 값 삭제

        System.out.println("size : " + stack.size());
        // 3
        // pop은 가장 위에 값을 삭제하는 것이기 때문에 3 출력

        System.out.println(stack.contains(1));
        // 1이 들어 있으면 true 아니라면 false

        System.out.println(stack.empty());
        // 마지막에 pop 후 또 pop이 되었는지 확인하기 위해서 사용
        // stack이 비어있다면 true 그렇지 않으면 false

        stack.clear(); // 모든 자료 삭제
        System.out.println(stack.isEmpty());
        // stack이 비었다면 true, 아니면 false
        System.out.println(stack);
        // clear 모든 자료 삭제 했기 때문에 [] 출력
    }
}

:: ✍ Queue

  • 처음에 저장한 데이터를 가장 먼저 꺼내게 되는 FIFO(First In First Out) 구조
  • 은행 창구 줄서기 / 인쇄작업 대기목록 / 놀이기구
  • 양 쪽 끝의 통로가 뚫려있다고 생각하면 됨
  • 가장 먼저 들어온 Data가 반환이 될때도 가장 먼저 반환
  • 우선순위 큐, 원형 우선순위 큐, 원형 큐 등 다양하게 존재
  • 가장 먼저 들어온 Data가 가장 먼저 반환
import java.util.LinkedList;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        // 큐는 인터페이스라 구현체가 필요한데 대표적으로 LinkedList

        queue.add(1);// add로 값 삽입
        queue.add(5);
        queue.add(3);
        System.out.println(queue);
        // [1, 5, 3] 입력된 순서대로 출력

        System.out.println(queue.poll());
        // 가장 앞에 값 삭제
        // 1 삭제한 값 반환

        System.out.println(queue);
        // [5, 3] poll로 삭제 했기 때문에

        System.out.println(queue.peek());
        // 5 가장 앞에 있는 값 확인만 하기

        System.out.println(queue);
        // peek은 확인만 한것이기 때문에 [5, 3] 그대로 출력

        System.out.println(queue.isEmpty());
        // false
        // queue가 비었다면 true, 아니면 false

        queue.clear(); // 모든자료 삭제
        System.out.println(queue.isEmpty()); // true
        System.out.println(queue); // []
    }
}

:: ✍ ArrayDeque

  • 실무에서 많이 사용
  • 기본 Stack, Queue의 기능을 모두 포함하면서 더 좋은 성능

:: deque

  • 큐는 한쪽에서만 값이 삽입되고 다른 한쪽에서만 값을 반환하는 자료구조였지만
  • deque의 경우 양 끝에서 삽입과 반환이 가능
import java.util.ArrayDeque;

public class Main {
    public static void main(String[] args) {
        ArrayDeque<Integer> arrayDeque = new ArrayDeque<>(); // ArrayDeque를 이용한 선언(제네릭스 이용)
        arrayDeque.addFirst(1); // addFirst 첫번째에다 추가
        arrayDeque.addFirst(2);
        arrayDeque.addFirst(3);
        arrayDeque.addFirst(4);
        System.out.println(arrayDeque);
        // [4, 3, 2, 1] 계속 앞에다가 추가 했으므로

        arrayDeque.addLast(0); // addLast 가장 마지막에 추가
        System.out.println(arrayDeque);
        // [4, 3, 2, 1, 0]

        arrayDeque.offerFirst(10);
        // 가장 첫번째에 추가
        // addFirst와 비슷
        // 큐의 크기 문제가 생길 때 offerFirst는 false를 addFrist는 exception을 반환
        System.out.println(arrayDeque);
        // [10, 4, 3, 2, 1, 0]

        arrayDeque.offerLast(-1); // arrayDeque의 가장 끝에 값 삽입
        System.out.println(arrayDeque);
        // [10, 4, 3, 2, 1, 0, -1]

        arrayDeque.push(22); // 22값 가장 앞에 추가
        System.out.println(arrayDeque); // [22, 10, 4, 3, 2, 1, 0, -1]
        System.out.println(arrayDeque.pop()); // 22 가장 앞에서 꺼내서 보여주고 삭제
        System.out.println(arrayDeque); // [10, 4, 3, 2, 1, 0, -1]

        System.out.println(arrayDeque.removeFirst());
        // 10
        // 그냥 remove도 맨앞에 값 제거
        // 첫번째 값을 제거하면서 그 값을 리턴

        System.out.println(arrayDeque.removeLast());
        // -1
        // 마지막 값을 제거하면서 그 값을 리턴

        System.out.println(arrayDeque); // [4, 3, 2, 1, 0]
        System.out.println(arrayDeque.size()); // 5

        System.out.println(arrayDeque.pollFirst());
        // 4
        // 그냥 poll도 맨 앞에 값 제거
        // 첫번째 값을 반환 및 제거하면서 그 값을 리턴

        System.out.println(arrayDeque.pollLast());
        // 0
        // 마지막 값을 반환 및 제거하면서 그 값을 리턴

        System.out.println(arrayDeque); // [3, 2, 1]
        System.out.println(arrayDeque.size()); // 3

        System.out.println(arrayDeque.peekFirst());
        // 3
        // 그냥 peek도 맨 앞에 값 보여줌
        // 첫번째 값을 반환, 제거하지 않음
        System.out.println(arrayDeque.peekLast());
        // 1
        // 마지막 값을 반환, 제거하지 않음
        System.out.println(arrayDeque.size()); // 3

        arrayDeque.clear(); // 전부 삭제
        System.out.println(arrayDeque.isEmpty());
        // 다 비었다면 true 아니면 false
    }
}

:: ✍ Generics (제네릭스)

  • 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능
  • 주로 사용하는 구조, 동작 이런 것을 한번 세팅해 놓고 사용
공장차리고 
거기에 컨베이어 벨트 깔고 물건 싣고 나르는 것도 까는 등 인프라 다 갖춘 후
어떤 날은 코카콜라, 어떤 날은 오렌지 주스, 또 어떤 날은 캔커피 생산한다고 할 때

컨베이어 벨트는 
제네릭스를 활용해서 짠 코드, 클래스, 메소드

코카콜라, 오렌지주스, 캔커피는 자바에서 짜는 클래스
  • 이전 강의에서 컬렉션에 꺽쇠를 해서 거기다 stirng, integer 넣은 것은
    • 컬렉션이 이미 제네릭스를 이용해서 구현되어 있기 때문
  • 제네릭스를 가장 잘 사용한 예제는 바로 Collection (또는 Collection 을 구현한 다른 자료구조들)

:: 제네릭스 사용하는 이유

  • 객체의 타입을 컴파일 시에 체크하기 때문에 안정성 높아짐
  • 컴파일 에러가 나지 않고 컴파일 시에 타입 체크가 된다면
    • 런타임 실행 도중에 클래스 타입이 잘못되서 에러가 나거나 프로그램이 죽는 일이 없다는 것
  • 의도하지 않은 타입의 객체가 저장되는 것을 막고 잘못된 형변환을 막을 수 있기 때문

:: 제네릭스 형식

public class 클래스명<T> {...}
public interface 인터페이스명<T> {...}

:: 자주 사용되는 타입인자 약어

- <T> == Type 
- <E> == Element 
- <K> == Key 
- <V> == Value 
- <N> == Number 
- <R> == Result (return타입 제네릭스로 할 때 많이 사용)

잘 짜여져있는 제네릭스 구조 파악하기

import java.util.*;

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

        List<String> list = new ArrayList();
        // ArrayList의 구현체를 만들어 인터페이스에는 List타입, 변수인 list 만듦
        list.add("String");
        Collection<String> collection = list;
        // 만든 list를 List보다 더 상위에 있는 인터페이스인 Collection의 변수에 할당

        List<Exception> exceptionList = new ArrayList<>();
        Collection<Exception> exceptionCollection = exceptionList;

        exceptionCollection.addAll(list);
        // Exception을 상속받은 콜렉션인데 스트링 타입 받는 리스트 써서 빨간 줄

        List<IllformedLocaleException> exceptions = new ArrayList<>();
        exceptionCollection.addAll(exceptions);
        // 빨간 줄 사라짐
        // exceptionCollection은 Exception 타입에 대한 콜렉션인데
        // IllformedLocaleException이 Exception의 자식클래스 중 하나라서 빨간 줄 사라짐

Collection.java 중 일부

Collection<E>
어떤 타입이든 올 수 있는 제네릭스를 이용하는데 Element에 대해 다룰 거라는 뜻

contains 
뭐가 있냐~ 라는 함수 확인 할 때

add(E e)
E는 어떤 타입이든 올 수 있는 E를 결정
e는 실제로 내가 <String>했을 때는 String이 되고, <Integer>이 되면 Integer가 되는 그 e

<T> T[] toArray(T[] a);
맨 위 클래스에서 <>로 선언한 타입이 아닌 경우 
이 메소드에서만 쓰이는 어떤 제네릭스를 표현 할 때
가장 왼쪽 <T>로 선언한 것은 이제 어떤 타입을 T라고 표현하고 
T라는 제네릭스를 이 메소드에만 사용할 것이라는 표현
toArray -> 어떤 타입을 배열 받아서 배열을 리턴하는 함수
<T>으로 선언하고 이 안에서 타입처럼 사용한다고 이해하면 됨

containsAll(Collection<?> c);
?는 내가 위에서 선언했던 E가 아니여도 된다는 것 
아무거나 와도 된다는 말, 특정 클래스 지칭안함

addAll(Collection<? extends E> c);
<? extends E> 
어떤 것들을 추가할 때에는 다른 콜렉션 넣어서 여러 개 추가할 수 있는데
대신 그 콜렉션은 E라는 타입이거나 E타입을 상속받은 자식클래스만 받겠다는 말
→ 잘 짜여져있는 제네릭스 구조 파악하기에 ex 있음
public interface Collection<E> extends Iterable<E> {
    int size();
    boolean isEmpty();
    Iterator<E> iterator();
    boolean add(E e);
    <T> T[] toArray(T[] a);
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
}

List.java 중 일부

public interface List<E> extends Collection<E> {
    // Collection 에 있는 메소드들 모두 포함 
    // + List 에만 있는 메소드들
// Collection에 있는 E와 같은 타입만 받는다는 의미
    boolean add(E e);
}

ArrayList.java 중 일부

  • 함수의 바디가 있음
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
}

  • 제네릭스는 동작은 같지만 클래스 타입만 바뀌어야 하는 경우 쉽게 다룰 수 있음
  • 컴파일언어어의 특징인 타입 안정성을 보장하면서도 유연한 프로그램을 작성할 수 있음
  • 대표적인 것은 콜렉션!

ctrl + 클릭으로 명세 확인

:: ✍ Lambda

  • 람다는 보다 단순하게 표현하는데 유용한 개념
  • 함수를 좀 쉽게 표현하고, 곧바로 표현

:: 람다식(Lambda expression)

  • 식별자 없이 실행 가능한 함수
  • 즉, 함수의 이름을 따로 정의하지 않아도 곧바로 함수처럼 사용할 수 있는 것
  • 문법이 간결하여 보다 편리한 방식
  • 익명함수라고도 부름

람다식이 코드를 보다 간결하게 만들어주는 역할을 하지만 그렇다고 무조건 좋다고만 이야기 할 수는 없음

이유는

  • 람다를 사용하여서 만든 익명 함수는 재사용이 불가능
  • 람다만을 사용할 경우 비슷한 메소드를 중복되게 생성할 가능성이 있으므로 지저분해질 수 있음
    • 똑같은 코드를 또 작성해야 한다면 함수로 만드는 것을 생각해보길

:: 람다식 형식

  • '→'의 의미는 매개변수를 활용하여 {}안에 있는 코드를 실행한다는 것
[기존의 메소드 형식]
반환타입 메소드이름(매개변수 선언) {
    수행 코드 블록
}

[람다식의 형식]
반환타입 메소드이름(매개변수 선언) -> {
    수행 코드 블록
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("korea");
        list.add("japan");
        list.add("france");
        Stream<String> stream = list.stream(); // stream은 list에 있는 것을 하나씩 꺼내주는 것을 의미

//        stream.map(str -> str.toUpperCase()).forEach(it -> System.out.println(it));
        stream.map(str -> {  // 사용할 변수 이름 지정(str)
            System.out.println(str);
            return str.toUpperCase(); // 이렇게 중괄호를 열어서 사용할 때에는 return 해줘야 함
//        }).forEach(it -> System.out.println(it));
        }).forEach(System.out::println); // alt + enter로 람다식 표현으로 바꿔주기
        // str -> 이런식으로 넘겨주지 않아도 forEach로 넘어온 변수
        // 그 파라미터를 이 함수에 자동으로 넘겨준다는 의미
    }
}
korea
KOREA
japan
JAPAN
france
FRANCE

:: 이중 콜론 연산자

  • str -> 이런식으로 넘겨주지 않아도 forEach로 넘어온 변수 즉, 그 파라미터를 이 함수에 자동으로 넘겨준다는 의미
  • 매개변수를 중복해서 사용하고 싶지 않을 때 사용
  • 출력결과를 보면 cities의 요소들이 하나씩 출력 될 것
  • 즉, cities.forEach(x -> System.out.println(x)); 와 똑같은 의미

:: ✍ Stream

  • 곧 '데이터의 흐름'
  • 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
  • 컬렉션을 통해 스트림 만들어 내는 것이 가장 대표적
  • 스트림을 활용해서 필터링,데이터 변경, 다른 타입이나 자료구조로 변환 등을 할 수 있음
  • 스트림을 활용해서 흘러 들어오는 데이터를 좀 더 유연하게 코드로 작성할 수 있음

:: 스트림 특징

  • 데이터 소스를 변경하지 않음 (ex. list)
  • 작업을 내부적으로 반복 처리(데이터 반복처리)
  • 컬렉션의 요소를 모두 읽고 나면 닫혀서 재사용이 불가능
    ∴ 그러므로 필요할 경우 재생성을 해야함
    • 또 스트림을 만들어 무언가를 하고 싶다면
      • list에 대해서 새로운 스트림을 만들어서 Stream2를 활용해야함

:: 스트림 구조

스트림 생성

Stream<T> Collection.stream()

중간 연산

  • 데이터의 형변환 혹은 필터링, 정렬 등 스트림에 대한 가공을 해줌
  • map(변환) / sorted(정렬) / skip(스트림 자르기) / limit(스트림 자르기) 등

최종 연산

  • 스트림의 요소를 소모해서 결과를 반환하는 단계
  • 최종 연산 이후에는 스트림이 닫히게 되고 더 이상 사용할 수 없음
  • 최종 연산의 결과값은 단일 값일 수도 있으며 배열 혹은 컬렉션일 수도 있음
  • collect()를 이용해서 다른 콜렉션으로 바꾸는 것, reduce를 이용해서 incremental calculation하는 것도 가장 많이 쓰이는 패턴

:: 스트림을 활용한 예제

1. 기본 구조

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class Main {
   public static void main(String[] args) {
       List<String> list = new ArrayList<>();
       list.add("서울");
       list.add("부산");
       list.add("대구");
       list.add("서울");
       System.out.println(list);
       // [서울, 부산, 대구, 서울]

       List<String> result = list.stream() // 스트림 생성
               .limit(2) //중간 연산
               // 앞에 두개만 남기고 뒤에는 지우는 것
               .collect(Collectors.toList()); // 최종 연산
               // stream 통해 또 다른 list 만듦
       System.out.println(result);
       // [서울, 부산]

       System.out.println("list -> transformation -> set");
       Set<String> set = list.stream() // list를 set이라는 자료구조로 바꾸기
               .filter("서울"::equals)
               // 람다식 사용
               // 받은 문자가 서울이라는 글자와 같다면
               .collect(Collectors.toSet());
               // set으로 바꿀거야
       System.out.println(set);
       // [서울]
       // 즉, list 하나씩 꺼내서 서울이라는 글자와 같은지 보고 같은거만 남긴다.
       // 그리고 set의 자료구조로 바꿔서 모을 것이라는 의미
      
       set.forEach(System.out::println);
       // 서울
   }
}
[서울, 부산, 대구, 서울]
[서울, 부산]
list -> transformation -> set
[서울]
서울

2. Array를 Stream으로 변환

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

public class Main {
    public static void main(String[] args) {
        String[] arr = {"SQL", "Java", "Python"};
        Stream<String> stringStream = Arrays.stream(arr);
//        stringStream.forEach(System.out::println);
//        // SQL, Java, Python 하나씩 꺼내 출력
        stringStream.forEach(className -> System.out.println("수업명 : " + className));
        System.out.println();
    }
}
수업명 : SQL
수업명 : Java
수업명 : Python

3. map연산 활용

  1. 라이브러리 추가
build.gradle의 dependencies 코드 블록에 붙혀넣기
implementation 'org.apache.commons:commons-lang3:3.0' 
  1. 오른쪽 Gradle 누르고 🔄 클릭
    • 외부에 있는 라이브러리 코드를 내 프로젝트에 추가하는 방법
    • 코드 추천 같은 것을 잘 받으려면 🔄 버튼 눌러야 함
import org.apache.commons.lang3.tuple.Pair;

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

class Sale {
    String fruitName;
    int price;
    float discount;

    public Sale(String fruitName, int price, float discount) {
        this.fruitName = fruitName;
        this.price = price;
        this.discount = discount;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Sale>  sales = Arrays.asList(
                new Sale("Apple", 5000, 0.05f), // 5%
                new Sale("Grape", 3000, 0.1f), // 10%
                new Sale("Orange", 4000, 0.2f), // 20%
                new Sale("Tangerine", 2000, 0)
                // 상품명, 가격, 할인율
        );

        // 실구매가를 구해서 어떤 과일이 실제로 얼만지 출력
        // 즉. 할인된 가격
       Stream<Sale> saleStream = sales.stream();
        saleStream.map(sale -> Pair.of(sale.fruitName, sale.price * (1 - sale.discount)))
                .forEach(pair -> System.out.println(pair.getLeft() + " 실 구매가:  " + pair.getRight() + "원 "));
    }
}
Apple 실 구매가:  4750.0원 
Grape 실 구매가:  2700.0원 
Orange 실 구매가:  3200.0원 
Tangerine 실 구매가:  2000.0원

4. reduce를 이용한 계산

  • reduce
    • integer뿐만 아니라 다른 여러가지 연산도 첫번째 오는 element와 수행할 함수 적용시켜주면 reduce 연산 가능
import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(list.stream().reduce(0, Integer::sum));
        // 처음을 0으로 시작해서 stream을 하나씩 꺼내서 sum함수 실행할 것이라는 의미
        // reduce와 sum을 활용하여 1부터 10까지 더함
        // 55
    }
}

:: ✍ 스트림퀴즈

'이'씨 성을 가진 사람들의 수를 세볼려고 합니다. 
스트림을 활용해서 코드를 만들어보세요! 
스트림을 활용해서 코드를 만들어보세요! 

힌트 Javadoc (자바의 클래스 명세)를 확인 후 참고
1. Javadoc1 : startsWith
2. Javadoc2 : count

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("김정우", "김호정", "이하늘", "이정희", "박정우", "박지현", "정우석", "이지수");
        System.out.println("이씨 성을 가진 친구들 : " + names.stream()
                                                    .filter(name -> name.startsWith("이"))
                                                    .count() + "명");
    }
}
이씨 성을 가진 친구들 : 3명

:: ✍ 네트워킹 - Retrofit, OpenAPI

:: 네트워킹(Networking)

  • 두 대이상의 컴퓨터를 케이블 또는 인터넷으로 연결하여 네트워크 구성하는 것

:: 네트워크의 기본적인 개념

1. 클라이언트(Client) / 서버(Server) (컴퓨터간의 관계를 역할로 구분)

  • 서버 : 서비스를 제공하는 컴퓨터, 요청에 응답하는 컴퓨터
  • 클라이언트 : 서비스를 사용하게 되는 컴퓨터, 요청을 하는 컴퓨터


2. IP 주소

  • 컴퓨터를 구별하는데 사용된느 고유한 값으로 인터넷에 연결이 되어있는 모든 컴퓨터는 IP 주소를 갖습니다.
내 컴퓨터 IP 주소 확인

CMD -> ipconfig 입력

3. URL(Uniform Resource Locator)

인터넷에 존재하는 여러 서버들이 제공하는 자원에 접근할 수 있는 주소를 표현하기 위한 것

  • URL의 형식
    • '프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조' 의 형식
    • 프로토콜은 복수의 컴퓨터 사이에서 데이터 통신을 원활하게 하기 위해 필요한 통신 규약을 의미
    • 대표적인 예시로는 Http
    • 포트번호
      • 통신에 사용되는 서버의 포트번호
      • 포트(port)는 보통 항구나 공항을 의미하는 것 컴퓨터에서도 포트는 비슷한 역할
      • 외부의 다른 장비와 접촉하기 위한 플러그 역할
      • 포트번호는 어떤 프로그램에 접속 할 것인지를 식별하는 역할
      • 아무것도 입력하지 않으면 기본값은 http는 80번, https는 443 포트

4. API(Application Programming Interface)

  • 응용 프로그램에서 사용할 수 있도록 운영체제나 프로그래밍 언어가 제공하는 기능을 제어 할 수 있게 해주는 인터페이스
  • client-server 관점에서 API는 요청과 응답의 형식에 대한 약속

:: Retrofit 라이브러리를 활용하여 API 호출하기

  • Retrofit : API네트워크 구현하기 쉽게 구현된 라이브러리

Retrofit 사용하는 이유

  • Retrofit을 사용하지 않고 원시적인 방법으로 구현을 하려면, JSON 파싱, 캐싱 등 구현해야할 부분이 너무나 많음
  • Retrofit 라이브러리를 사용하게 될 경우 복잡한 구현들을 감소
  • 즉, API에 대한 접근을 보다 쉽게 만들어줌
  • 각종 에러처리를 쉽게 해결할 수 있으며 원시적인 방법에 비해 가독성 훌륭
  • 실제로 프로젝트나 프로그램을 만들 때 공공 데이터 API를 많이 활용

Retrofit 라이브러리 실습 준비 단계

  1. 3가지 라이브러리가 필요
build.gradle 파일의 dependencies{}에 추가

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.google.code.gson:gson:2.8.5'
  1. 추가 후 🔄 ( 단축키 : Ctrl + Shift + O )
  2. 어떤 API에 요청할 것인지 API 확인 -> API 확인을 위한 예제 모아놓은 사이트
    • 통신 프로토콜은 https 사용
    • GET은 HTTP METHOD중 하나로, 해당하는 데이터 가져옴
    • 요청을 하는 도메인(홈페이지 또는 서비스의 주소)은 reqres.in
    • 요청을 하는 API의 위치는 /api/users
    • 요청을 하는 API에 전달할 데이터는 page=2
      • API의 2페이지를 줘! 라는 의미
GET – LIST USERS 확인

request
/api/users?page=2  (page=2 == 키=벨류)

response
200

쿼리스트링(Query String)

  • url 주소에 ?는 쿼리스트링을 작성하겠다는 신호
  • 사용자가 입력 데이터를 전달하는 방법중 하나

:: Retrofit 라이브러리 실습

1. RetrofitService 인터페이스 파일 생성

import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface RetrofitService {
    @GET("/api/users/")
    Call<Object> retrofitTest(@Query("page") int page);
}
//@Query는 Retrofit 라이브러리를 이용할 때 쿼리스트링을 입력하는 방법
//이렇게 파라미터 변수로 작성해놓으면 함수를 호출할 때 파라미터를 바꿔가며 원하는 페이지를 조회할 수 있음

2. 클라이언트 클래스 생성

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String BASE_URL = "https://reqres.in/";
    // BASE_URL에는 변하지 않는 URL주소를 입력해 주면 됨. 데이터의 위치에 따라 변하지 않는 주소

    public static RetrofitService getApi() {
        return getInstance().create(RetrofitService.class);
        // api 요청
    } // getInstance 메소드를 통해 인스턴스를 반환

    private static Retrofit getInstance() {
        Gson gson = new GsonBuilder().setLenient().create();
        // Gson은 JSON 다루는 라이브러리
        // 통신을 할 때 JSON 사용 및 해당 객체로의 파싱을 위해 생성
        return new Retrofit.Builder().baseUrl(BASE_URL)
                                   .addConverterFactory(GsonConverterFactory.create(gson))
                                    .build();
        //서버에서는 JSON으로 응답하므로 우리는 build.gradle에 설정한 gson을 이용
}

3. Main

import retrofit2.Call;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        Call<Object> retrofitTest = RetrofitClient.getApi().retrofitTest(2);
        //우리는 2페이지를 확인할 것이기 때문에 2 삽입
        //Call은 retrofit라이브러리의 인터페이스이며 서버에서 응답하는 값을 담는 역할
        try {
            System.out.println(retrofitTest.execute().body());
            //서버에서 받은 데이터 확인
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

0개의 댓글