Java - 리스트

민찬홍·2023년 10월 4일

Java

목록 보기
22/31
post-thumbnail

🚀 제대로 파는 자바(Java) - by 얄코 강의를 듣고 정리한 내용입니다.

코딩테스트공부 관계로 앞서 공부하던 객체지향보다 우선해서 리스트를 공부하려 한다.

🧩 리스트

🧩 ArrayList

  • 가장 많이 사용되는 컬렉션 클래스
  • 요소들을 들어오는 순서대로 저장
  • 크기가 자유롭게 바뀔 수 있음
  • 배열 기반의 동적 배열
		//  ⭐️ 제네릭을 사용하여 타입 지정
		//  - 붙이지 않을 시 <Object>와 동일
        //    제너릭으로 원시타입은 들어갈 수 없음 
        ArrayList<Integer> ints1 = new ArrayList<>();
        ArrayList<String> strings = new ArrayList<>();
        ArrayList<Number> numbers = new ArrayList<>();
        ArrayList<Knight> knights = new ArrayList<>();
		//  add 메소드로 요소 추가
        ints1.add(11);
        ints1.add(22);
        ints1.add(33);
        ints1.add(44);
        ints1.add(55);
		//  요소 중복 허용
        for (var str : "바니 바니 바니 바니 당근 당근".split(" ")) {
            strings.add(str);
        }
		//  for-each 문 사용 가능
        for (int i : ints1) {
            System.out.println(i);
        }

아래에 있는 메소드들을 잘 외우자.

		int ints1Size = ints1.size(); // 요소 개수
		boolean ints1IsEmpty = ints1.isEmpty(); // size가 0인지 여부 반환
        int ints12nd = ints1.get(1); // 인덱스로 요소 접근
        boolean ints1Con3 = ints1.contains(33); // 포함 여부
        boolean ints1Con6 = ints1.contains(66);
		ints1.set(2, 444); // 인덱스 위치의 요소 수정
        ints1.add(0, 11); // 인덱스 위치에 요소 추가 (다음 요소들 밀어냄)
		//  ⭐️ 간략한 생성 및 초기화 방법들
        ArrayList<Integer> ints2A = new ArrayList<>(
                Arrays.asList(1, 2, 3, 4, 5)
        ); // 💡 Arrays 클래스 : 배열 관련 각종 기능 제공

        ArrayList<Integer> ints2B = new ArrayList<>(
                List.of(1, 2, 3, 4, 5)
        ); // 💡 자바9에서부터 가능

        ArrayList<Integer> ints2C = new ArrayList<>();
        Collections.addAll(ints2C, 1, 2, 3, 4, 5);

이번에는 기존에 존재하는 ArrayList를 가지고 새 ArrayList를 만드는 것을 볼 것이다.

		//  💡 다른 Collection 인스턴스를 사용하여 생성
        ArrayList<Integer> ints3 = new ArrayList<>(ints1);

        //  스스로를 복제하여 반환 (⚠️ 얕은 복사)
        ArrayList<Integer> ints4 = (ArrayList<Integer>) ints3.clone();

이번에는 요소를 지우는 것을 알아보겠다.

		ints3.remove(4); // int: 인덱스로 지우기
        ints3.remove((Integer) 55); // 클래스 자료형: 요소로 지우기 (들어온 값과 같은 첫번째 값을 지움) 

위의 경우는 Integer의 값을 지워주는 경우라 (Integer) 를 붙여주어야 하지만, 다른 종류의 자료형들은 그냥 해당자료형의 값을 넣어주면 되니까 조금 덜 번거로울 것이다.

		ints1.removeAll(ints3); // 주어진 콜렉션에 있는 요소들 지우기

위의 경위와는 반대로 요소를 이어붙일수도 있다.

		ints1.addAll(ints3); // 콜렉션 이어붙이기

이제 배열로 반환하는 것을 알아보겠다.

		//  💡 toArray - Object 배열 반환
        Object[] intsAry2_Obj = ints1.toArray();

        //  ⭐️ 특정 타입의 배열로 반환하려면?
        //  Integer[] ints1Ary1 = (Integer[]) ints1.toArray(); // ⚠️ 이렇게는 불가
        //  💡 인자로 해당 타입 배열의 생성자를 넣어줌
        //  - 다음 섹션에 배울 메소드 참조 사용 (9-3강 수강 후 다시 볼 것)
        Integer[] ints1Ary2 = ints1.toArray(Integer[]::new);

위에서 알 수 있듯이 네번째 줄처럼 특정 타입으로 반환하는 것은 불가능한 것을 알 수 있다. 컴파일단계에서는 오류가 나지 않지만 런을 해보면 오류가 난다.

아래에 Integer[]::new 이 표현은 Integer 의 생성자를 축약해서 표현한 것이다.

아래는 리스트의 요소를 비우는 방법이다.

		ints1.clear(); // 리스트 비움

아래처럼 각 컬렉션에 그에 맞지 않는 자료형을 넣어주면 오류가 나는 것을 알 수 있다.

		//  제네릭 적용
		numbers.add(Integer.valueOf(123));
        numbers.add(3.14);
        numbers.add("Hello"); // ⚠️ 불가

		knights.add(new Swordman(Side.BLUE)); // ⚠️ 불가
		knights.add(new Knight(Side.BLUE));
        knights.add(new MagicKnight(Side.RED));

아래에서는 조금 중요한 개념이 나온다.

		//  ⭐️ 인스턴스 요소를 지울 때는 참조를 기준으로
        //  - 내용이 같다고 같은 인스턴스가 아님
        Knight knight1 = new Knight(Side.RED);
        knights.add(knight1);

        //  요소가 하나 지워졌는지 여부 반환
        boolean removed1 = knights.remove(new Knight(Side.RED));
        boolean removed2 = knights.remove(knight1);

위에 내용을 디버깅해보면 알 수 있지만 참조형 데이터들이 컬렉션 안에 들어있을 때 remove 등에서 그것과 같은 것을 선택하는 기준은 인스턴스의 내용이 아니라 참조값, 즉 같은 주소에 위치하는 것인가를 보는 것이다.

따라서 두번째 remove에서는 knight1을 그대로 넣어줬다. 그러니 제대로 remove가 동작하였다.

  • 배열과 달리 크기가 동적으로 증가 / 각 요소들이 붙어서 메모리상에 위치
    • 저장하지 않을 시 초기공간 10
      • 공간은 요소의 수가 아니다.
    • 배열처럼 메모리상에 연속으로 나열
      • 그래서 Array(배열)List
    • 공간 초과 시 초과공간 확보
      • 더 넓은 공간을 확보한 뒤 요소들을 복사
    • 중간 요소 제거 시 이후 요소들을 당겨옴
  • 용도
    • 장점 : 각 요소들로의 접근이 빠름
    • 단점 : 요소 추가/제거 시 성능 부하, 메모리 더 차지
    • 변경이 드물고 빠른 접근이 필요한 곳에 적합함

🧩 LinkedList

  • 큐를 구현하는 용도로 사용 가능

  • 메모리 이곳저곳에 분산되어 배치

  • 기능상 ArrayList와 대다수 주요 기능 공유

    • 위 코드에서 ArrayList를 LinkedList로 변경해보자.
    • intellij 단어 전부 교체하고 -> cmd + r
  • 요소를 삭제하면 그 다음 요소들의 인덱스가 자동으로 조정되어 삭제한 요소의 다음 요소가 해당 인덱스로 이동한다.

  • 각 요소들이 메모리 이곳저곳에 산재

    • 각각 이전, 다음요소로의 링크가 있음
    • 요소 추가 시 해당 요소의 메모리만 확보 후 링크
    • 요소 제거 시 그 이전 요소와 다음 요소 연결
  • 용도

    • 장점 : 요소의 추가와 제거가 빠름, 메모리 절약
    • 단점 : 요소 접근이 느림
    • 요소들의 추가/제거가 잦은 곳에 적합

위의 예제들에서 ArrayList를 전부 LinkedList로 변경해도 정상작동하는 것을 볼 수 있다.

이제 아래에는 ArrayList와 LinkedList 각각에 고유하게 존재하는 것들이다.

		//  ⭐️ 둘의 차이와 연관지어 생각해 볼 것

        //  💡 ArrayList에만 있는 메소드들 중...
        ArrayList<Attacker> attackers = new ArrayList<>();
        //  자주 쓰이지는 않음
        attackers.ensureCapacity(5); // 자리수 미리 확보
        attackers.trimToSize(); // 남는 자리 없애기 (메모리 회수)
		//  💡 LinkedList에만 있는 메소드들 중...
        LinkedList<Integer> intNums = new LinkedList<>();
        for (var intNum : new int[] {2, 3, 4}) { intNums.add(intNum); };

        intNums.addFirst(1);
        intNums.addFirst(0);
        intNums.addLast(5); // add와 반환값만 다름. 코드에서 확인해 볼 것
        intNums.addLast(6);


		//  💡 앞에서/뒤에서 꺼내지 않고 반환
        //  - peek~ : 비어있을 경우 null 반환
        //  - get~ : 비어있을 경우 익셉션
        int peekedFirst = intNums.peekFirst();
        int gottenFirst = intNums.getFirst();
        int peekedLast = intNums.peekLast();
        int gottenLast = intNums.getLast();
        
        
        
        //  💡 앞에서/뒤에서 꺼내어 반환
        //  - poll~ : 비어있을 경우 null 반환
        //  - remove~ : 비어있을 경우 익셉션
        int polledFirst = intNums.pollFirst();
        int removedSecond = intNums.removeFirst();
        int polledLast = intNums.pollLast();
        int removedLast = intNums.removeLast();

		//  ⭐️ 위의 기능들 활용하여 Stack/Queue 구현
		LinkedList<Character> charLList = new LinkedList<>();

        //  💡 push & pop : 스택 간편하게 구현
        //  - 클래스 코드에서 살펴볼 것

        charLList.push('A');  // addFirst
        charLList.push('B');
        charLList.push('C');
        charLList.push('D');
        charLList.push('E');

        char pop1 = charLList.pop(); // removeFirst
        char pop2 = charLList.pop();
        char pop3 = charLList.pop();
  • ArrayDeque도 알아보고 사용해볼 것
    • 사용법과 주요 메소드들은 LinkedList와 거의 동일
    • 성능이 중요할 시 스택, 큐로의 활용에 보다 적합
      • 맨 앞에서, 뒤에서의 넣고 꺼내기가 잦을 때

🧩 ❗️ Arrays 의 ArrayList

  • Arrays.asList 가 반환하는 ArrayList
  • 오늘 배운 ArrayList와 전혀 다름
    • java.util.Arrays 의 정적 내부 클래스
    • 사이즈 변경 불가 (요소 추가 불가)
		List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
        ArrayList<Integer> list2 = new ArrayList<>(list1);

        var list1Type = list1.getClass().getName();
        var list2Type = list2.getClass().getName();

        list1.add(6); // ⚠️ 런타임 오류

🧩 실무에서는 컬렉션 자료형을 인터페이스로

		List<Integer> intList = new ArrayList<>();
        intList = new LinkedList<>();
        
        Set<String> strSet = new HashSet<>();
        strSet = new TreeSet<>();
        
        Map<Integer, String> intStrMap = new HashMap<>();
        intStrMap = new TreeMap<>();
  • List, Set, Map 등의 인터페이스로 변수,인자,제너릭 등의 자료형 지정
    • 상세구현이 어떤 알고리즘으로 되어있는지는 굳이 드러내지않음
    • 필요에 따라 다른 종류로 교체가 용이
profile
백엔드 개발자를 꿈꿉니다

0개의 댓글