Notice
Recent Posts
Recent Comments
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
10-10 22:21
Archives
Today
Total
관리 메뉴

Developer_Neo

[코드스테이츠 백엔드 2기(40기) SEB BE] 16일차 본문

코드스테이츠

[코드스테이츠 백엔드 2기(40기) SEB BE] 16일차

_Neo_ 2022. 7. 14. 13:46
반응형

오늘 나의 학습 목표는 무엇인가요?

- 제네릭 클래스를 정의하고 활용하며 제네릭 메서드를 정의하고 활용할 수 있다.
- 컬렉션 프레임워크의 핵심 인터페이스, 주요 인터페이스와 컬렉션 클래스의 핵심 메서드를 사용할 수 있다.

한가지 의문점

- String이나 참조변수선언시에 해당 참조변수에 주소값을 저장하는데 왜 서로다른 타입값들이 필요한지 의문이었다. 왜냐하면 주소값에 해당하는 것은 최대 크기가 정해져있고 이에 해당하는 것을 저장한다면 모두 일괄적인 크기를 가지면 되지 않나라는 생각에서 이어졌다.

String a = new String("asd");
// Generic 사용시 - Collection
List<String> list = new LinkedList<>();

즉, 위의 a와 list라는 참조변수를 볼때 주소값을 저장한다. 이때 왜 String과 List<String>과 같은 타입들을 지정해주어야하는가 그냥 float라는 것으로 지정해주어도 되지 않나라는 생각을 했다는 것이다.

 

해답은 C언어의 포인터연산에서 얻었다.

왜 적어주어야하냐면

참조변수들은 각각 List나 String이나 배열 등의 주소값들을 가지고 있는데 이때 참조변수가지고 처음에 해당하는 값들만 알 수 있다.

예를 들어 "abc"라면 a만 알 수 있고, [1,2,3] 이라면  1만 알수 있다는 것이다.

그런데 우리는 String에서 charAt(0), charAt(1)이나 arr[0] , arr[1]로써 처음값이 아닌 다음값에 해당하는 것도 접근해서 정보를 얻어내었다. 이때 얻어내는 방식은 내부적으로 주소값을 증가시켜서 얻어낸다. 그러면 주소값을 증가시킬 때 +1을 해서 보통 얻어낸다. 1은 각 타입에 따라서 지정된 크기이다.

만약 모두 일괄적인 타입으로 참조변수를 생성해 주소값을 저장했다면 +1을 했을때 이상한 값이 나올 수 있는 것이다.

따라서 이런한 것들을 수행하기 위해 타입을 지정해준다고 결론을 얻어내었다.

하지만 클래스의 경우에는? 왜 적을까 주소값을 증가시켜서 가지고 있을 필요가 없는데?

왜 클래스에 대한 참조변수는 . 으로 안에있는 변수나 메서드에 접근이 가능한가

 


제네릭(Generic)

- 타입을 구체적으로 지정하는 것이 아니라, 추후에 지정할 수 있도록 일반화해두는 것작성한 클래스 또는 메서드의 코드가 특정 데이터 타입에 얽매이지 않게 해둔 것을 의미

- 컴파일 시 강한 타입체크를 통해 코드에서 잘못 사용된 타입 때문에 발생하는 에러들을 사전에 방지한다.

- 컴파일러가 타입캐스팅을 해주기에 개발자가 편리하며, 타입만 다르고 코드의 내용이 거의 일치할때 코드의 재사용성이 좋아진다.

위의 2가지 이유로 제네릭을 사용한다.

class Basket {
    private String item;

    Basket(String item) {
        this.item = item;
    }

    public String getItem() {
        return item;
    }

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

Basket 클래스가 String이 아닌 숫자를 가질수도 있고 참, 거짓값들을 가질 수 있다고 한다면 제네릭이 좋다.

 

제네릭으로 클래스와 메서드 정의하는 법

class Basket<타입매개변수> {

}


// 타입 매개변수 여러개 사용시
class Basket<K, V> { ... }

주의점

- 클래스변수인 클래스 내에 static으로 설정된 클래스 변수에는 타입 매개변수를 사용할 수 없다

 

타입매개변수의 약칭들

Type T Value V Element E
Key K Number N Result R

 

맨 처음의 코드를 제네릭으로 변경한 것

class Basket<T> {
    private T item;

    public Basket(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }

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

 

제네릭을 사용한 변수 선언 및 초기화

Basket<String> basket1 = new Basket<String>("기타줄");
//위의 코드 실행시 내부적으로 Basket 클래스는 
class Basket {
    private String item;

    Basket(String item) {
        this.item = item;
    }

    public String getItem() {
        return item;
    }

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

// 이렇게 된다

 

Basket<Integer> basket2 = new Basket<Integer>(10);
Basket<Double>  basket3 = new Basket<Double>(3.14);

위와 같이 타입 매개변수로 될 수 있는 타입들은 기본 타입을 지정할 수 없습니다. 따라서 원시타입인 int나 double에 해당하는 것을 지정해야한다면 해당 래퍼클래스인 Integer, Double와 같은 래퍼클래스를 활용해야한다.

 

오른쪽에 해당하는 new를 쓸 때 <> 안의 타입매개변수타입은 생략이 가능하다 왜냐하면 왼쪽에서 참조변수의 타입으로 유추가 가능하기 때문이다.

Basket<String>  basket1 = new Basket<>("Hello");
Basket<Integer> basket2 = new Basket<>(10);
Basket<Double>  basket2 = new Basket<>(3.14);

 

래퍼클래스

- 8개의 기본타입에 해당하는 데이터를 객체로 표현하기 위해 포장해주는 클래스이다.

기본 타입 래퍼 클래스
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

 

위에서 보았던 제네릭 클래스는 타입을 지정하는데 제한이 없었다. 하지만 제한 하는 법도 존재한다.

class 클래스명<T extends (클래스 or 인터페이스)>{}

위와 같이 하며 의미는 T는 extends 뒤의 것을 상속 또는 구현하는 클래스가 T인 타입이 되야한다는 것을 명시하는 것이다.

 

만약, 특정클래스를 상속받으면서 동시에 특정 인터페이스를 구현한 클래스만 타입으로 지정할 수 있도록 제한하려면  

class 클래스명<T extends 클래스 & 인터페이스 >{}

와 같이 하면 된다.

 

제네릭 메서드 (자세히)

여러가지가 가능하다. 

1. 클래스 선언시 타입매개변수에 선언하지 않았지만 메서드에서만 타입매개변수 사용

public class main {
    public static void main(String[] args) {
        Basket basket = new Basket();
        basket.<String>p("Hello");


    }
}

class Basket {
    public <T> void p(T element) {
        System.out.println("element = " + element);
    }
}

/*
class Basket {
    public <T> T p(T element) {
        System.out.println("element = " + element);
        return element;
    }
}
여도 똑같은 결과출력
*/

//출력 : element = Hello

 

2. 클래스 선언시 타입매개변수에 선언했지만 메서드에서는 다른 타입매개변수 사용

public class main {
    public static void main(String[] args) {
        Basket<String> basket = new Basket<>(); // 클래스에서 쓰인 T가 String으로 지정됩니다.
        basket.<Integer>p(5);  // 메서드에서 쓰인 T를 따로 Integer로 설정
        basket.p("Hello");    // 타입 지정을 생략해도 된다.


    }
}

class Basket<T>{

    public <T> void p(T element) {
        System.out.println("element = " + element);
    }
}

/*
element = 5
element = Hello
*/

 

3. 클래스 선언시 타입매개변수에 선언하지않고 static 메서드에서의 타입매개변수 사용

class Basket {
		...
		static <T> int setPrice(T element) {
				...
		}
}


컬렉션 프레임워크

- 여러 데이터들을 그룹으로 묶어놓은 것을 컬렉션이라고 하며, 이러한 컬렉션을 다루는 데에 있어 편리한 메서드들을 미리 정의해놓은 것

 

https://sowon-dev.github.io/2020/08/12/200813javai/


1. List<E>

- 인터페이스이다.

- 배열과 같이 객체를 일려로 늘어놓은 구조

- 객체를 인덱스로 관리하기에 인덱스로 객체를 검색, 추가, 삭제할 수 있는 기능등을 제공

- Iterable인터페이스 구현이 되어있다.

- ________.iterator()는 Iterator 객체를 리턴

 

 

1-1. 메서드

- List 인터페이스를 구현한 클래스들에서 쓰이는 메서드들이 된다.

(1) 객체 추가

void add(int index, Object element) 주어진 인덱스에 객체를 추가
boolean addAll(int index, Collection c) 주어진 인덱스에 컬렉션을 추가
Object set(int index, Object element) 주어진 위치에 객체를 저장

(2) 객체 검색

Object get(int index) 주어진 인덱스에 저장된 객체를 반환
int indexOf(Object o) / lastIndexOf(Object o) 순방향 / 역방향으로 탐색하여 주어진 객체의 위치를 반환
ListIterator listIterator() / listIterator(int index) List의 객체를 탐색할 수 있는ListIterator 반환 / 주어진 index부터 탐색할 수 있는 ListIterator 반환
List subList(int fromIndex, int toIndex) fromIndex부터 toIndex에 있는 객체를 반환

 

(3) 객체 삭제

Object remove(int index) 주어진 인덱스에 저장된 객체를 삭제하고 삭제된 객체를 반환
boolean remove(Object o) 주어진 객체를 삭제

 

(4)객체 정렬

void sort(Comparator c) 주어진 비교자(comparator)로 List를 정렬

 

1-2. List 인터페이스를 구현한 클래스들

(1)  ArrayList

- 배열과 달리 이것은 저장 용량을 초과하여 객체들이 추가되면, 자동으로 저장용량이 늘어나게 된다.

- 데이터가 연속적으로 존재하는 특성을 가짐

- 객체를 추가하면 인덱스 0부터 차례대로 저장됩니다. 그리고 특정 인덱스의 객체를 제거하면, 바로 뒤 인덱스부터 마지막 인덱스까지 모두 앞으로 1씩 당겨집니다.

- 빈번한 객체 삭제와 삽입이 일어나는 곳에서는 ArrayList보다는 이후에 배우게 되는 LinkedList를 사용하는 것이 좋다.

List<타입 매개변수> 객체명 = new ArrayList<타입 매개변수>(초기 저장 용량);

List<String> container1 = new ArrayList<String>();
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량이 인자로 전달되지 않으면 기본적으로 10으로 지정됩니다. 

List<String> container2 = new ArrayList<String>(30);
// String 타입의 객체를 저장하는 ArrayList 생성
// 초기 용량을 30으로 지정하였습니다.
ArrayList<Integer> integers1 = new ArrayList<Integer>(); // 타입 지정
ArrayList<Integer> integers2 = new ArrayList<>(); // 타입 생략 가능
ArrayList<Integer> integers3 = new ArrayList<>(10); // 초기 용량(Capacity) 설정
ArrayList<Integer> integers4 = new ArrayList<>(integers1); // 다른 Collection값으로 초기화
ArrayList<Integer> integers5 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); // Arrays.asList()

ArrayList를 생성할 때 Set이나 다른 ArrayList를 전달하면 해당 Collections의 값들로 초기화됩니다.

마지막으로 가변 인자를 전달받는 Arrays.asList()를 사용하면 기본 값들로 생성 가능합니다.

 

(2) LinkedList

- 데이터를 효율적으로 추가, 삭제, 변경하기 위해 사용

- Data 검색할 때에는 시작 인덱스에서부터 찾고자하는 데이터까지 순차적으로 각 노드에 접근해야 하기 때문에 데이터 검색에 있어서는 ArrayList보다 상대적으로 속도가 느립니다.

- LinkedList에는 불연속적으로 존재하며, 이 데이터는 서로 연결(link)되어 있다.

- 양방향 연결 리스트(Doubly Linked List)로 구현

양방향 연결리스트

데이터 삭제시 해당 노드의 이전 노드 위치정보와, 다음 노드 위치정보를 기억했다가 다음노드의 이전 노드의 위치정보, 이전 노드의 다음노드의 위치정보에 각각 넣어주면 된다. 이렇게 링크를 끊는 방식이다. 배열이나 위의 ArrayList와 같이 데이터 삭제시 하나씩 이동시키는 방식이 아니라서 처리속도가 훨씬 빠르다

 

데이터들 사이에 추가할때도 비슷한 방식으로 진행한다. 그냥 추가할때는 뒤에 추가시켜주는 것이다.

 

따라서 데이터의 잦은 변경이 예상된다면 LinkedList를, 데이터의 개수가 변하지 않는다면 ArrayList를 사용하라.

List<String> list = new LinkedList<>();

2. Set<E>

- 인터페이스이다.

- 요소의 중복을 허용하지 않고, 저장 순서를 유지하지 않는 컬렉션

- Iterable인터페이스 구현이 되어있다.

2 - 1. 메서드

(1) 객체 추가

boolean add(Object o) 주어진 객체를 추가하고, 성공하면 true를, 중복 객체면 false를 반환합니다.

(2) 객체 검색

boolean contains(Object o) 주어진 객체가 Set에 존재하는지 확인합니다.
boolean isEmpty() Set이 비어있는지 확인합니다.
Iterator Iterator() 저장된 객체를 하나씩 읽어오는 반복자를 리턴합니다.
int size() 저장되어 있는 전체 객체의 수를 리턴합니다.

 

(3) 객체 삭제

void clear() Set에 저장되어져 있는 모든 객체를 삭제합니다.
boolean remove(Object o) 주어진 객체를 삭제합니다.

 

2 - 2. Set 인터페이스를 구현한 클래스들

(1)  HashSet

- 중복된 값을 허용하지 않으며, 저장 순서를 유지하지 않는다.

- HashSet.iterator()는 Iterator 객체를 리턴

 

중복을 걸러내는 과정

  1. add(Object o)를 통해 객체를 저장하고자 합니다.
  2. 이 때, 저장하고자 하는 객체의 해시코드를 hashCode() 메서드를 통해 얻어냅니다.
  3. Set이 저장하고 있는 모든 객체들의 해시코드를 hashCode() 메서드로 얻어냅니다.
  4. 저장하고자 하는 객체의 해시코드와, Set에 이미 저장되어져 있던 객체들의 해시코드를 비교하여, 같은 해시코드가 있는지 검사합니다.
    1. 이 때, 만약 같은 해시코드를 가진 객체가 존재한다면 저장하고자 했던 객체가 중복 객체로 간주되어 Set에 추가되지 않으며, add(Object o) 메서드가 false를 리턴합니다.
    2. 같은 해시코드를 가진 객체가 존재하지 않는다면, Set에 객체가 추가되며 add(Object o) 메서드가 true를 리턴합니다.
  5. equals() 메서드를 통해 객체를 비교합니다.
    1. true가 리턴된다면 중복 객체로 간주되어 Set에 추가되지 않으며, add(Object o)가 false를 리턴합니다.
    2. false가 리턴된다면 Set에 객체가 추가되며, add(Object o) 메서드가 true를 리턴합니다.
HashSet<String > languages = new HashSet<String>();

 

(2)  TreeSet

- 이진 탐색 트리 형태로 데이터를 저장

- 데이터의 중복 저장을 허용하지 않고 저장 순서를 유지하지 않는다.

- TreeSet의 기본 정렬 방식이 오름차순이다.

- TreeSet.iterator()는 Iterator 객체를 리턴

 

이진탐색트리

2022.01.26 - [자료구조] - [자료구조] 트리 (Tree) 구조 -Data Structure with 파이썬

TreeSet<String> workers = new TreeSet<>();

3. Map<E>

- 인터페이스이다.

- 키(key)와 값(value)으로 구성된 객체를 저장하는 구조

- Entry 객체는 키와 값을 각각 Key 객체와 Value 객체로 저장

- 키는 중복 저장될 수 없지만, 값은 중복 저장이 가능하다 (키의 역할이 값을 식별하는 것)

- 만약 기존에 저장된 키와 동일한 키로 값을 저장하면, 기존의 값이 새로운 값으로 변경된다.

- Map은 키(key)로 객체들을 관리

- Iterable인터페이스 구현이 안되어있어 iterator를 별도로 만들어주어야한다.

- Entry 객체는 키와 값을 각각 Key 객체와 Value 객체로 저장

 

 

3 - 1. 메서드

(1) 객체 추가

Object put(Object key, Object value) 주어진 키로 값을 저장 해당 키가 새로운 키일 경우 null을 리턴하지만, 동일한 키가 있을 경우에는 기존의 값을 대체하고 대체되기 이전의 값을 리턴합니다.

 

(2) 객체 검색

boolean containsKey(Object key) 주어진 키가 있으면 true, 없으면 false를 리턴합니다.
boolean containsValue(Object value) 주어진 값이 있으면 true, 없으면 false를 리턴합니다.
Set entrySet() 키와 값의 쌍으로 구성된 모든 Map.Entry 객체를 Set에 담아서 리턴합니다.
Object get(Object key) 주어진 키에 해당하는 값을 리턴합니다.
boolean isEmpty() 컬렉션이 비어 있는지 확인합니다.
Set keySet() 모든 키를 Set 객체에 담아서 리턴합니다.
int size() 저장된 Entry 객체의 총 갯수를 리턴합니다.
Collection values() 저장된 모든 값을 Collection에 담아서 리턴합니다.

(3) 객체 삭제

void clear() 모든 Map.Entry(키와 값)을 삭제합니다.
Object remove(Object key) 주어진 키와 일치하는 Map.Entry를 삭제하고 값을 리턴합니다.

 

3 - 2. Map 인터페이스를 구현한 클래스들

(1)  HashMap

- Map 인터페이스를 구현한 대표적인 클래스

- 키와 값으로 구성된 Entry 객체를 저장

- 해시 함수를 통해 '키'와 '값'이 저장되는 위치를 결정하므로, 사용자는 그 위치를 알 수 없고, 삽입되는 순서와 위치 또한 관계가 없다

- 해싱(Hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 가진다.

 

HashMap을 생성할 때에는 아래와 같이 키와 값의 타입을 따로 지정해야한다

HashMap<String, Integer> hashmap = new HashMap<>();

- Map은 키와 값을 쌍으로 저장하기 때문에 iterator()를 직접 호출할 수 없습니다. 그 대신 keySet() 이나 entrySet() 메서드를 이용해 Set 형태로 반환된 컬렉션에 iterator()를 호출하여 반복자를 만든 후, 반복자를 통해 순회할 수 있다,

public class Example{
	public static void main(){
            HashMap<String, Integer> map = new HashMap<>();
            Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
            Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();

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

 

(2)  HashTable

-  hashMap과 사용법이 거의 동일

- Hashtable은 Thread-safe하고, HashMap은 Thread-safe하지 않다는 특징을 가지고 있습니다. 그렇기에 멀티스레드 환경이 아니라면 Hashtable은 HashMap 보다 성능이 떨어진다는 단점을 가지고 있습니다.

- Hashtable은 key에 null을 허용하지 않지만, HashMap은 key에 null을 허용합니다.

- HashMap은 보조해시를 사용하기 때문에 보조 해시 함수를 사용하지 않는 Hashtable에 비하여 해시 충돌(hash collision)이 덜 발생할 수 있어 상대적으로 성능상 이점이 있습니다

HashTable<String, String> map = new Hashtable<String, String>();

Extra Iterator

- 컬렉션에 저장된 요소들을 순차적으로 읽어오는 역할

- Collection 인터페이스에는 Iterator 인터페이스를 구현한 클래스의 인스턴스를 반환하는 메서드인 iterator()가 정의가 되어있는데 정의가 된 것은 Set 과 List, Queue 인터페이스들로 이것들을 구현한 HashSet, ArrayList, Linkedlist, PriorityQueue ... 등들이 있다.

 

 메서드

메서드 설명
hasNext() 읽어올 객체가 남아 있으면 true를 리턴하고, 없으면 false를 리턴합니다.
next() 컬렉션에서 하나의 객체를 읽어옵니다. 이 때, next()를 호출하기 전에 hasNext()를 통해 읽어올 다음 요소가 있는지 먼저 확인해야 합니다.
remove() next()를 통해 읽어온 객체를 삭제합니다. next()를 호출한 다음에 remove()를 호출해야 합니다.
List<String> list = ...;
Iterator<String> iterator = list.iterator();

while(iterator.hasNext()) {     // 읽어올 다음 객체가 있다면 
	String str = iterator.next(); // next()를 통해 다음 객체를 읽어옵니다.
        if(str.equals("str과 같은 단어")){ // 조건에 부합한다면
            iterator.remove();            // 해당 객체를 컬렉션에서 제거합니다. 
	}
}

 

 

 


오늘 학습 내용 중 새롭게 배운 내용은 무엇인가요?
       -  추상클래스와 인터페이스 간 사용시기

오늘 새롭게 학습한 내용을 다른 사람에게 설명할 수 있나요?
      - 네

오늘 학습한 내용 중 아직 이해되지 않은 불확실한 내용은 무엇인가요?
     -  없습니다.

이해되지 않은, 불확실한 내용을 보완하기 위해서 나는 무엇을 할 수 있을까요?
     -  구글링을 해본다.

나의 오늘 학습 만족도는 몇 점인가요?
     - 90점 (아는 내용들이라고 설렁설렁 넘어간 것들이 있는 것같다.)
반응형
Comments