본문 바로가기

신세게 - Java 공부

5주차 배운점 느낀점 - List, ArrayList, Vector, LinkedList, Set, HashMap

728x90
반응형

5주차

 

오늘 진도를 많이 나갔다.
리스트(링크드리스트, 어레이리스트, 벡터), Map, Set 과 같은 컬렉션 프레임워크를 배웠다.
진도 양이 많다. 갑자기 많이 나가니 어질어질하다.
강사님이 코딩테스트를 준비할 때 알아두어야한다고 하셨다.
어제 Board (게시판 프로그램) 에관에 잠시 얘기해주셨다.
데이터객체(VO)는 Board class 이고 데이터처리객체(Dao)는 Board에 관한 데이터처리 메소드들을 가지고있다. 원하는 객체를 불러서 프로그램 시작 객체BoardExample이다. 작지만 이게 MVC구조라고 하셨다.

 

배운점

 

컬렉션 프레임워크


자료구조를 바탕으로 객체들을 효율적으로 추가, 삭제, 검색할 수 있도록 관련된 인터페이스와 클래스를
java.util 페키지에서 제공한다.

 

주요인터페이스와 구현클래스


List - ArrayList, Vector, LinkedList (특징: 순서 유지 저장, 중복 저장 가능)
Set - HashSet, TreeSet (특징: 순서유지 않고 저장, 중복 저장 안됨)
Map - HashMap, HashTable, TreeMap, Properties (특징: 키와 값으로 구성된 엔트리 저장, 키는 중복 저장 안됨)

 

List,Set은 객체를 추가, 삭제, 검색 방법의 공통점이 있기 때문에 공통된 메소드를 따로 모아서 Collection 인터페이스로 정의하고 이를 상속하고 있다.

 

Map은 키와 값을 하나의 쌍으로 묶어서 관리하는 구조(List, Set과는 사용방법이 다르다)

 

List 컬렉션


객체를 인덱스로 관리한다.
객체를 저장하면 인덱스가 부여되고, 인덱스로 검색, 삭제 기능 제공
List<> list = new ArrayList(); //모든 타입 객체 저장가능

 

필수적 기능


객체 추가

  • boolean add(E e), void add(int index, E e)
  • set()(index, E e)
    객체 검색
  • boolean contains(Object o) 어떤객체든 담을 수 있다. 업캐스팅됨
  • boolean check = lis.contains(employee);
  • get(int index);
  • isEmpty()
  • int size()
    객체 삭제
    void clear()
    E remove(index)
    boolean remove(E)

일반 배열과 차이점은 ArrayList 는 객체를 길이 제한 없이 추가할 수 있다.
ArrayList에 객체를 추가하면 내부 배열에 객체가 저장되고 제한 없이 객체를 추가할 수 있음
객체의 번지를 저장. 동일한 객체를 중복 저장 시 동일한 번지가 저장. null 저장 가능
ArrayList 컬렉션에 객체를 추가 시 인덱스 0번부터 차례대로 저장
특정 인덱스의 객체를 제거하거나 삽입하면 전체가 앞/뒤로 1씩 당겨지거나 밀림
빈번한 객체 삭제와 삽입이 일어나는 곳에선 바람직하지 않음, 차례차례 보관용

 

 

List는 인터페이스, ArrayList는 클래스
다른 리스트 쓸 수 있음, 구현상의 유연성
List<Integer> list = new ArrayList<>();
list = new LinkedList\();

 

이제 int 말고 Integer 써라!!

 

3차원배열 읽기
콤마로 구분, 11출력

Integer[][][] data = {
            {
                    {1, 2, 3},
                    {4, 5, 6}
            },
            {
                    {7, 8, 9},
                    {10, 11, 12}
            }

    };
    
    System.out.println(data[1][1][1]); //11출력

 

정수형 타입으로 2행 5열 배열 생성

 

Integer idata[][] = {{1,2,3,4,5},{6,7,8,9,10}}; // 자동생성

 

조회하기


data - String배열
"i" 검색 , 몇개인가?

Integer count = 0;
    for(Integer item=0; item<data.length; item++){
      if(data[item].indexOf("i")>=0){
        count++;
      }
    }

 

Vector


동기화된 메소드로 구성되어 있어 멀티 스레드가 동시에 Vector() 메소드를 실행할 수 없음
멀티 스레드 환경에서는 안전하게 객체를 추가 또는 삭제할 수 있음

 

 

list1은 Integer 만 저장할 수 있다.
list2는 모든 타입 객체 저장할 수 있다.

 

List<Integer> list1 = new Vector<>(); // list1은 Integer 객체만 저장할 수 있다.
List list2 = new Vector(); // 모든 타입 객체 저장할 수 있다.

//    list1.add("M"); 정수값만 넣을 수 있다.
    list1.add(30);
    list2.add("M");
    list2.add(50);
    list2.add(30.4);
    System.out.println(list2.get(0));

 

Vector는 ArrayList와 동일한 내부 구조를 가지고 있다.
Vector는 동기화 메소드로 구성되어 있어 멀티스레드가 동시에 Vector()메소드를 실행 할 수 없다.
스레드는 인터럽트 없으면 끝까지 해냄

 

thread


두 개의 스레드 각각 방해안받고 끝까지 실행됨
join 실행 시 이 join으로 호출된 스레드(threadA,threadB)가 실행완료 될때까지 더이상 join을 호출한 스레드(메인)가 진행되지 않는다. => 메인은 더이상진행하지 않는다
join한 메소드가 끝나야 다음 스레드를 start()할 수 있다.

 

join이 시작되기 전에 threadA, threadB start를 둘다해주니 섞인거임, 스A.start(), 스A.join() , 스B.start , 스B.join 해주면 A끝나고 B나옴

 

List<Board> boards = new Vector<>();
    //작업 스레드A
    Thread threadA = new Thread(){
      public void run(){
        for(int i = 1 ; i<= 1000; i++){
          boards.add(new Board("제목"+i,"내용"+i,"작성자"+i));
        }
      }
    };
    Thread threadB = new Thread(){
      public void run(){
        for(int i = 1001 ; i<= 2000; i++){
          boards.add(new Board("제목"+i,"내용"+i,"작성자"+i));
        }
      }
    };
    //작업스레드 실행
    threadA.start();
    threadB.start();
    //작업 스레드들이 모두 종료 될때까지는 메인스레드를 기다려!
    try{
      threadA.join(); // 메인스레드가 종료안되도록 기다리게함
      threadB.join();
    }catch (Exception e){
      e.printStackTrace();
    }
    int size = boards.size();
    System.out.println("총 글의 수 :" + size);

    for(Board board : boards){
      System.out.printf("%s   , %s   ,   %s\n",board.getWriter(),board.getSubject(),board.getContent());
    }

 

List<Board> boards = new ArrayList<>();로 하면 다름, 다 섞임, 글자 수가 다 들어오지 않음, 할당 되지 못함, 동시충돌되서 할당 못받음
벡터는 동기화가 되어있다. 한번에 하나의 스레드만 쓸 수 있다. 경합하지 않아. 안전하게 2000개의 쓰기 작업 이뤄짐

 

LinkedList


인접 객체를 체인처럼 연결해서 관리. 객체 삭제와 삽입이 빈번한 곳에서 ArrayList보다 유리
add삽입됐을때 공간이 만들어짐, 인덱스가 만들어지긴하는데 , 서로가 자신의 위치를 다음객체에 연결해 놓음, 서로의 위치를 알고 있다. 위치값을 공유한다.
노드: 데이터 저장단위
포인터: 다른 노드들의 연결정보(주소값) 가지는 변수

 

 

add가 됨에 따라 공간이 만들어짐과 만들어 져있는거

 

메모리생성 시간
Arraylist, Vector가 메모리 생성할때는 하나하나 조회하면서 생성하고, 미리 capacity를 만들고 부족하면 더 늘려가는 식으로 동작한다. -> 느림
링크드리스트는 헤드에서 다음 노드 생성해서 이어주기만 하면 되기 때문에 -> 빠름
Arraylist, Vector는 index가 정해져있으므로 조회가 빠름
LinkedList 조회는 찾을 data로 검색해서 .. 하나하나 쫒차가야하므로 느림

 

  public static void main(String[] args) {
    // ArrayList
    List<String> list1 = new ArrayList<String>();

    // LinkedList 
    List<String> list2 = new LinkedList<String>();

 

결과:
ArrayList 걸린 시간: 6869500 ns
LinkedList 걸린 시간: 993600 ns

 

< T >에다가 Integer나 사용자 정의 타입 넣을 수 있음

 

 

SingleLinkedList< T> class


addNode 처음 만들어 질때 헤드값이 null이면 최초생성,
최초 생성되는 노드의 주소값 헤드에 넣음, 알아야 찾아 들어 갈 수 있으니깐!
serch head가 null이 아닐 때 찾는 data값이 같을때까지 돌리다가 그 노드 반환
addNodeInside isData매개변수 뒤에 다가 새 노드를 넣을거임, serch통해 찾다가 반환 받고 그 노드의 앞뒤를 연결해준다. (7,10) 이면 7이 없기때문에 serch에서 return null 이라 뒤에 붙임
싱글은 next만 가지고 있고 더블은 prev , next 가지고 있다.

 

public class LinkedList<T> {
  public Node<T> head = null;

  public class Node<T> {
    T data;
    Node<T> next = null;

    public Node(T data) {
      this.data = data;
    }
  }

  public void addNode(T data) {
    if (head == null) {
      // 처음 만들어 질때 헤드값이 널이라면
      //최초 생성되는 노드의 주소값 헤드에 넣음
      // 알아야 찾아들어갈 수 있다.
      head = new Node<T>(data);
    } else {
      Node<T> node = this.head;
      while (node.next != null) {
        node = node.next;
        // 다음노드 next에 넣음
      }
      node.next = new Node<T>(data);
    }
  }

  public void printAll() {
    if (head != null) {
      Node<T> node = this.head;
      System.out.println(node.data);
      while (node.next != null) {
        node = node.next;
        System.out.println(node.data);
      }
    }
  }

  public Node<T> search(T data) {
    if (this.head == null) {
      return null;
    } else {
      Node<T> node = this.head;
      while(node != null) {
        if (node.data == data) {
          return node;
        } else {
          node = node.next;
        }
      }
      return null;
    }
  }

public void addNodeInside(T data, T isData) {
    Node<T> searchedNode = this.search(isData);
    //search를 통해 찾고 반환시킴 // isData뒤에 넣을 거다.
    // 중간에 낑겨놓고 끼워놓은 노드 연결해줌
//    MyLinkedList.addNodeInside(5, 1);
//    MyLinkedList.printAll();
    //1 5 2 3 원래 123이었음
    if (searchedNode == null) {
      this.addNode(data); // 없으면 에드시킴
    } else {
      Node<T> nextNode = searchedNode.next;
      searchedNode.next = new Node<T>(data); //새로생성
      searchedNode.next.next = nextNode;
      //next 의 next는 '5'의 다음 주소변수
    }
  }

 

장단점


배열은 미리 데이터 공간 할당해야하는데 미리 데이터공간을 할당하지 않아도된다.
linkedlist는 미리 공간 확보되어잇어야한다. head 와 next 필요하다. -> 저장 공간 효율 높지않음
조회기능할때는 어레이리스트가 유리하다. 인덱스를 통해 참조하기때문
연결정보 순차적인 주소값 sort 되어 있지않아서 오래걸림

 

Set 컬렉션


Set 컬렉션은 저장 순서가 유지되지 않음
객체를 중복해서 저장할 수 없고, 하나의 null만 저장할 수 있음(수학의 집합 개념

 

 

HashSet


동등 객체를 중복 저장하지 않음
다른 객체라도 hashCode() 메소드의 리턴값이 같고, equals() 메소드가 true를 리턴하면 동일한 객체라고 판단하고 중복 저장하지 않음
hashCode와, equals를 재정의 해줘야함
equals만 재정의해주면 같은 문자열 두 객체를 다르다고 판단.

 

 

iterator() 메소드: 반복자를 얻어 Set 컬렉션의 객체를 하나씩 가져옴

 

 

같은 "신세계 자바 과정3"이 들어가자 add의 반환값이 false가 나온다.
size도 2뿐이다.

 

 Set<String> set = new HashSet<>();
    boolean check = false;
    check = set.add("신세계 자바 과정1");
    System.out.println(check);
    check = set.add("신세계 자바 과정3");
    System.out.println(check);
    check = set.add("신세계 자바 과정3");
    System.out.println(check);

    int size = set.size();
    System.out.println(size);

 

Main class


Member를 담는 Set 선언하고 add로 추가해준다.
Iterator를 만들어서 하나씩 조회한다.

 

public static void main(String[] args) {

    Set<Member> members = new HashSet<Member>();

    members.add(new Member("최문석",33));
    members.add(new Member("윤여빈",21));
    members.add(new Member("윤여빈",21));

    System.out.println(members.size());
    //객체를 하나씩 가져와서 처리
    Iterator<Member> iterator = members.iterator();
    // iterator 선언
    while(iterator.hasNext()){ //hasNext로 하나씩 접근
      Member member = iterator.next();
      System.out.println(member.age+" "+member.name);
      if(member.name.equals("최문석")){
        iterator.remove();
      }
    }

    for(Member member : members) //전체 출력
      System.out.println(member.age + " "+member.name);
  }

 

Member

 

public class Member {
  public String name;
  public Integer age;
  public Member(String name, Integer age){
    this.name = name;
    this.age = age;
  }
  public int hashCode(){
    return name.hashCode() + age;
  }

  public boolean equals(Object o){
    if(o instanceof Member m){
      return m.name.equals(name) && (m.age == age);
    }else {
      return false;
    }
  }
}

 

Map 컬렉션


키와 값으로 구성된 엔트리 객체를 저장 : Map.Entry
키는 중복 저장할 수 없지만 값은 중복 저장할 수 있음. 기존에 저장된 키와 동일한 키로 값을 저장하면 새로운 값으로 대치
맵에 들어가는 객체: 엔트리

 

 

HashMap


키로 사용할 객체가 hashCode() 메소드의 리턴값이 같고 equals() 메소드가 true를 리턴할 경우 동일 키로 보고 중복 저장을 허용하지 않음

 

 

HashMap 이터레이터 사용할때 Set밖에 안됨
set은 순서가 없기때문에 Iterator 사용해야된다.

 

public static void main(String[] args) {
    Map<String, Integer> scores = new HashMap<>();
    //Map map2 = new HashMap(); // 모든 타입의 객체의 키와 값을 저장, 거의 없다.

    //이름을 Key, 점수를 Value 저장하는 map 구현
    scores.put("김지영", 100); // hashmap에 데이터 넣기
    scores.put("김하나", 90);
    scores.put("이지은", 80);
    scores.put("김진석", 80);
    scores.put("김진석", 80);

    System.out.println("총 Entry 수 "+scores.size());
    System.out.println();
    String key = "김진석";
    Integer value = scores.get(key); // key를 이용해 값 받기
    System.out.println(key + "학생의 점수는" + value);

    //키 Set 컬렉션을 얻어와, 반복자를 통해 키와 값을 출력하기
    Set<String> keySet = scores.keySet();
    // Map에서 Iterator을 사용할 수 없다. Set을 만들고 Iterator를만든다.
    Iterator<String> keyIterator = keySet.iterator();
    // Iterator 만들기
    while(keyIterator.hasNext()){
      String k = keyIterator.next(); // 키 구하기
      Integer v = scores.get(k);  // 키로 값 얻기
      System.out.println(k+" : "+v);
    }

    //엔트리 Set컬렉션을 얻어, 반복하여 키와값을 출력하기
    Set<Map.Entry<String , Integer>>  entrySet = scores.entrySet();
    //map은 엔트리에 저장함, Set으로 감싸줬다.
    Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
    //Iterator는 set만 받아, 위에서 만든 set으로 Iterator 만들기
    while(entryIterator.hasNext()){ // 그 entryIterator로 while
      Map.Entry<String, Integer> entry = entryIterator.next(); 
      // 새거 만들어서 next담기
      String k = entry.getKey(); // 키 뽑기
      Integer v = entry.getValue(); // 값 뽑기
      System.out.println(k+" : "+v);
    }
    System.out.println();
    System.out.println("변경전 "+scores.size());
    scores.remove("김하나");
    System.out.println("변경후 "+ scores.size());

    scores.clear();
    System.out.println("변경후 "+ scores.size());

  }

 

Hashtable


동기화된 메소드로 구성되어 있어 멀티 스레드가 동시에 Hashtable의 메소드들을 실행 불가
멀티 스레드 환경에서도 안전하게 객체를 추가, 삭제할 수 있다.

 

회고

 

컬렉션 프레임워크에대해 빨리 배워보고 싶었던 마음이 있었다. 자세히 파보니 생각보다 복잡하다. 취소다. 그래도 같은 List여도 백터 , 어레이, 링크드 리스트의 차이를 정확히 알지 못했는데 알게되서 좋았다. thread개념과 같이 익히니 이해가 잘되었다.
Map까지 어느정도 나갔는데 그래도 내일은 진도 천천히나갔으면 좋겠다.

반응형