너무 다양한 List를 만드는 방법들..

devhan·2023년 8월 8일

자바스크립트에서 "[]"로 정적 배열 개념없이 동적 배열만 사용해온 나에게 자바는 너무 다양한 List를 생성하는 방법을 가진 것이 아닌가라는 생각이 든다.


Q.그래서 배열이 필요하면 난 무엇을 사용해야되나?

우선 내 머릿속에 있는 List와 배열의 개념부터 명확하게 할 필요가 있다.
기존에 자바스크립트에 '배열'이란 단어로 퉁치고 있던 "동일한 타입의 목록"을 재정의하자.


1. new int[size]

작은 개념에서 부터 시작하자.
자바에서 배열은 정적 배열을 이야기한다. ( 배열 == 정적 배열 )

int[] arr = new int[3];
arr[0] = 0;
arr[1] = 1;
arr[2] = 2
arr[3] = 3; // error

int[] arr2 = { 1, 2 };

사이즈가 정해져있다. 그래서 여러모로 데이터를 삽입하는 과정에서 불편함을 느낀다.
사이즈를 2배씩 늘려주고 뭐 이런 이야기는 다른 글에서 하는걸로


2. list.toArray()

잘 쓰이지는 않을 것 같지만 List의 인스턴스를 다시 배열로 바꾸는 방법도 있다.
toArray()는 인자를 전달하지 않는 경우 Object[]을 반환하는 것에 주의해야할 것 같다.

  List<String> list = new ArrayList<>();
  list.add("a");
  list.add("b");
  
  Object[] arr = list.toArray();
  arr.length    // 2
  
  String[] sArr = list.toArray(new String[0]); // 0을 넣으면 list의 길이만큼
  sArr.length   // 2
  
  String[] sArr = list.toArray(new String[10]); // 인자에서 길이와 타입을 조절
  sArr.length   // 10

Q.그럼 List는 무엇?

기본적으로 List는 배열에 각종 추가 기능들을 api로 제공하는 자료구조라고 1차적으로 이해할 수 있겠다. 보다 편리하게 배열을 사용할 수 있도록 Wrapping 되어있는 형태.


3. Arrays.asList()

as라는 단어를 보면 casting이 떠오른다.
Arrays.asList는 "Array를 List로 casting 하겠다" 라고 이해하도록 해보겠다.
(근본은 array이지만 list의 api를 사용할 수 있는 상태)


Integer[] arr = {1, 2, 3};
arr[0]   // 1

List<Integer> list = Arrays.asList(arr);
list.get(0)  // 1
			 // 리스트가 제공하는 api사용 가능!
            
arr[0] = 100
list.get(0)   // 100 
              // 복사가 아닌 참조만 하고 있음
              
list.set(0, 50)
list.get(0)   // 50
              // 사이즈 변경없이 요소는 변경이 가능
              

기존에 배열에서는 [idx]를 사용해서 배열의 요소에 접근했는데, 이제는 get메소드를 사용할 수 있게 되었다. get 뿐만아니라 stream 등 다양한 list의 api 사용이 가능하다.


하지만 Array를 List로 casting 했을 뿐이기에 아직 Array가 가지는 한계는 그대로이다.

Integer[] arr = {1, 2, 3};

List<Integer> list = Arrays.asList(arr);
list.add(100) // java.lang.UnsupportedOperationException

위에서 언급했듯이, "배열"은 사이즈를 동적으로 변경할 수 없다. 따라서 사이즈가 3으로 처음에 설정된 상태에서 4번째 인자를 add로 추가하면 UnsupportedOperationException가 발생한다.


또 다른 이상한 동작으로는

int[] arr = {1, 2, 3};
List<int[]> list = Arrays.asList(arr);

list.get(0)     // arr의 주소값
list.get(0)[0]  // 1

char[] cArr = {'a', 'b', 'c'};
List<char[]> cList = Arrays.asList(cArr);

cList.get(0)     // cArr의 주소값
cList.get(0)[0]  // 'a'

Integer가 인자인 배열을 Array.asList에 넣을 경우 List가 반환되지만,
int가 인자인 배열을 넣을 경우 List<int[]>가 반환된다!?

가변인자
이것은 asList(T...args) 가변인자를 사용하기 때문에 발생한다.
가변인자를 사용하면 args는 T 타입의 배열이 된다.
자바가 내부적으로 배열을 생성하기 때문에 성능 이슈가 생길 수 있고
오버라이드와 함께 사용하지 않는 것이 좋다.

자바가 가변인자를 사용하는 경우 원시형[]은 그 자체를 T 타입으로 결정한다.
따라서, int[] 을 인자로 넣는다면 자바가 T = int[ ]로 결정하고 args는 int[ ][ ]가 되기때문에 List<int[]>을 써야하는 것이다.
반대로 참조형[]인 Integer[]의 경우는, 자바가 T = Integer로 결정하고 args는 Integer[]이 되기 때문에 List를 사용할 수 있는 것이다.

자바에서 이렇게 하기로 한 부분이니까 외워야하는 부분!


4. List.of()

위의 2가지 예제와는 다르게, List.of는 바로 List를 생성해준다. (자바9 도입)
하지만 사이즈와 요소 수정이 불가능한 List를 만들어준다.

 List.of(1,2,3);  // get은 가능하지만 사이즈, 요소 둘 다 변경이 불가능
  
 list.get(0)      // 1
 list.add(100)    // UnsupportedOperationException
 list.set(0, 10)  // UnsupportedOperationException

 
 // Collections.unmodifiable() 을 사용해서 같은 효과를 낼 수 있다.
 Integer[] arr = {1, 2, 3};
 List<Integer> list = Arrays.asList(arr);
 List<Integer> unmodifiable = Collections.unmodifiableList(list);
  
 unmodifiable.set(0, 12); // UnsupportedOperationException
  

어디다 쓸까? stream으로 만들어서 사용가능

 List<Integer> list = List.of(1,2,3);
 List<Integer> newList = list.stream().map((i) -> {
	   return i * 2;
 }).collect(Collectors.toList());
			
 newList.get(0);       // 2
 newList.set(0, 10);   // 새로운 배열은 수정이 가능하다.
 newList.get(0);       // 10
 
 list.set(0, 11); // UnsupportedOperationException
		

Q. List도 사이즈 조절은 못하나?

5. new ArrayList<>()

동적배열이자 사이즈 조절이 가능한 List를 생성하는 방법이다.
지금까지 언급된 형태 중에 가장 자유로운 형태이다.

  List<String> list = new ArrayList<>();
  list.add("A");
  list.add("B");
  
  list.set(0,"C");
  list.get(0);
  
  // 모든 동작이 에러없이 실행

위의 List들과 마찬가지로 stream으로 변경, 혹은 unmodifiable하게 수정도 가능하다.


Sumamry

  1. 배열
  • 자바에서 배열과 List는 다르다.
  • 배열은 사이즈를 조절할 수 없는 정적 배열이다.
  • new int[size] 혹은 list.toArray(new Integer[size]) 로 만든다.
  1. List
  • 정적 배열과 동적 배열을 모두 포함하는 개념이다.
  • 기본적으로 배열 + 편의api = List라고 생각할 수 있다.
  • Array.asList(), List.of(), new ArrayList()를 사용해서 만들 수 있다.
  • ArrayList의 경우에는 배열의 사이즈를 수정할 수 있는 동적 배열을 만든다.

profile
한번에 한가지씩

1개의 댓글

comment-user-thumbnail
2023년 8월 8일

잘 봤습니다. 좋은 글 감사합니다.

답글 달기