-- INDEX --
1. ArrayList 생성 | 2. ArrayList 메소드 | 3. 배열 변경 |
import java.util.ArrayList; | add(); / addAll(); clear(); / clone(); contains(); / forEach(); / get(); |
toArray(); |
ArrayList arrayList = new ArrayList(); |
indexOf(); / lastIndexOf(); size(); / isEmpty() iterator(); / remove(); removeAll(); -- retainAll(); |
asList(); |
ArrayList <T> arrayList = new ArrayList <>(); |
set(); / sort(); / toArray(); | stream(); |
1.ArrayList 생성
- import java.util.ArrayList;
- ArrayList<T> arrayList = new ArrayList<>();
- new ArrayList() : 기본 크기가 10인 배열 생성
- new ArrayList(기본크기) : 기본 크기를 지정 (배열이 다 차면 기본크기만큼 사이즈가 증가함)
- new ArrayList<제네릭>() : 배열 값의 타입을 지정
2. 메소드
2-1 : add()
- ArrayList.add(E e) - 리스트의 마지막에 인자로 전달된 아이템을 추가한다.
- ArrayList.add(int index, E e) - 인자로 전달된 인덱스의 위치에 아이템을 추가한다.
2-2 : addAll()
- 두 컬렉션을 합친다.
- ArrayList.addAll(Collection c)
- - 인자로 전달되는 Collection 객체의 모든 아이템을 리스트에 추가한다.
- ArrayList.addAll(int index, Collection c)
- - 리스트의 몇 번째 인덱스부터 아이템을 추가할지 지정가능하다.
- 인자로 인덱스가 전달되는데, 이 인덱스부터 아이템이 추가된다.
2-3 : clear()
- 내부의 배열을 모두 null로 초기화하고 size를 0으로 설정한다.
- 이 메소드는 이전에 무슨 작업을 하건 말건 상관없이 무조건 리스트를 비워줍니다.
2-3 : clone()
- ArrayList<Integer> numbers = new ArrayList<>();
- ArrayList<Integer> newNumbers = (ArrayList<Integer>) numbers.clone();
- 인자는 없고, ArrayList의 복사본을 리턴한다.
- 리스트의 아이템들을 깊은 복사를 하지 않고 얕은 복사(shallow copy)로 새로운 ArrayList에 set
2-4 : contains()
- 리스트 안에 어떤 객체가 있는지 확인하는 데 사용하는 메소드
- contains(Object o) - 객체를 인자로 전달받는다.
- 리스트에 그 객체가 존재하면 true / 없으면 false를 return 한다.
- boolean을 리턴하기 때문에, if와 함께 사용할 수 있다.
containsAll()
- argument로 제공한 컬렉션의 모든 값이 포함되어 있는지 여부를 true / false로 반환한다.
2-5 : forEach()
- forEach()는 리스트를 순회(iterate)하는데 사용되는 메소드.
- forEach(Consumer<? super E> action) - Consumer 객체를 인자로 받는다.
- forEach는 리스트의 모든 아이템에 대해서 Consumer.accept가 수행되도록 한다.
- 또한, 인자는 람다로 표현할 수 있어 코드를 더욱 간단하게 만든다.
String[] fruitsArray = {"apple", "banana", "kiwi", "mango", "blackberry"};
ArrayList<String> fruits = new ArrayList<>(Arrays.asList(fruitsArray));
Consumer<String> lambda= item -> System.out.println("item : " + item);
fruits.forEach(lambda);
System.out.println("");
fruits.forEach(item -> System.out.println("item : " + item));
//출력 결과
item : apple
item : banana
item : kiwi
item : mango
item : blackberry
item : apple
item : banana
item : kiwi
item : mango
item : blackberry
2-6 : get()
- ArrayList 내부의 엘리먼트를 가져올 수 있다.
- get(int index)은 인자로 인덱스를 받는다. 이 인덱스 위치에 있는 객체를 return 한다.
- 리스트 크기보다 큰 인덱스를 인자로 전달받으면 IndexOutOfBoundsException 발생한다.
2-7 : indexOf()
- ArrayList 안에 있는 엘리먼트가 어디에 있는지 찾기 위해 사용하는 메소드
- 리스트의 앞쪽부터 인자와 동일한 객체가 있는지 찾으며, 존재한다면 그 인덱스를 return 한다.
- 리스트에 동일한 객체가 2개 이상 존재할 때, 가장 앞에 위치한 객체의 인덱스를 return 한다.
- 존재하지 않는다면 -1을 return 한다.
- indexOf(Object value, int startIndex)
- - 지정된 인덱스부터 마지막 요소까지 탐색. - value가 있으면 전체 ArrayList에서 맨 처음 발견되는 값의 0부터 시작하는 인덱스 return 한다.( 지정 범위 내에서의 index가 아닌 전체 ArrayList에서의 해당 index ) value 없으면 -1 return 한다.
- IndexOf(Object value, int startIndex, int count)
- - startIndex에서 시작하여 count개의 요소를 포함하는 ArrayList의 요소 범위에 value가 있으면 처음으로 검색한 개체의 인덱스(0부터 시작)이고, 그렇지 않으면 -1입니다
2-8 : lastIndexOf()
- 리스트의 뒤쪽부터 인자와 동일한 객체가 있는지 찾으며, 존재한다면 그 인덱스를 return 한다.
- 리스트에 동일한 객체가 2개 이상 존재할 때, 가장 뒤쪽에 위치한 객체의 인덱스를 return 한다.
- 존재하지 않는다면 -1을 return 한다.
- lastIndexOf(Object value, int endIndex)
- - 역방향 첫 번째 요소부터 endIndex까지 탐색 - value가 있으면 전체 ArrayList에서 맨 처음 발견되는 값의 0부터 시작하는 인덱스 return 한다. value 없으면 -1 return 한다.
- lastIndexOf(Object value, int endIndex, int count)
- - 역방향 첫 번째 요소부터에서 시작하여 endIndex까지 count개의 요소를 포함하는 value가 있으면 처음으로 검색한 개체의 인덱스(0부터 시작)이고, 그렇지 않으면 -1 return 한다.
2-9 : size()
- ArrayList의 엘리먼트의 수를 알고 싶을 때 사용
- size가 0이라면 리스트가 비어있다고 확인할 수 있다. ( = Null 체크 가능 )
- 여기서 주의할 점은 length()는 배열의 전체 크기를 나타내므로 혼동하지 않도록 주의
2-10 : isEmpty()
- ArrayList에 엘리먼트들이 있는지 확인하는 메소드
- 리스트에 저장된 요소가 하나도 없을 때 true를 return
- 따라서, 리스트가 비어있는지 확인할 수 있다.
- isEmpty()로 Null체크가 가능
+@ 객체가 비어있는지 null인지 체크 주의
- arrayList.isEmpty() 또는 ArrayList.size() 메소드만으로 리스트가 비어있는지 확인할 수 있지만,
- ArrayList 객체가 null일 때 이 메소드를 호출하면 NullPointerException이 발생
- 따라서, 아래와 같이 먼저 null check를 하고 그다음에 isEmpty()로 리스트가 비어있는지 확인해야 합니다.
if (list == null || list.isEmpty()) {
// list is null or empty
}
2-11 : iterator()
- 반복을 통해 순회하면서 탐색할 때 사용
- 자바의 컬렉션 프레임워크에서 컬렉션에 저장되어 있는 요소들을 읽어오는 방법을 표준화하였는데 그중 하나가 Iterator
- 보통 객체 지향 시 주로 사용하게 되는 기법이며 iterator()를 사용하기 위해선 객체를 먼저 생성해주어야 함
기본적으로 ArrayList는 순환 중 CRUD가 불가능 하지만 Iterator를 통해서 유일하게 안전한 방법으로 순환 중 다룰 수 있다.
+@ Iterator 인터페이스는 아래와 같은 메소드를 지원
- hasNext() : 다음 엘리먼트가 있는지 확인합니다. 즉, 현재 위치에서 다음 위치로 이동할 수 있는지 판단.
- next() : 다음 엘리먼트를 가져오는 역할, 있으면 true, 없으면 false
- remove() : next()로 가져온 엘리먼트를 삭제
Iterator<Integer> itr = list.iterator();
while (itr.hasNext()) {
list.get(itr.next());
}
2-12 : remove()
- 삭제하는 역할을 하는 메소드. index를 통해서 해당 엘리먼트를 삭제.
- ArrayList.remove(int index) -인자로 전달된 인덱스 위치의 아이템을 리스트에서 삭제되고, 그 객체를 리턴
- ArrayList.remove(Object o) - 인자로 삭제하려는 아이템을 전달하고, 리스트에 그 아이템이 존재하여 삭제되는 경우 true를 리턴
- index 뿐만 아니라 엘리먼트(객체) 자체를 삭제할 수도 있습니다.
2-13 : removeAll() <-> retainAll()
- ArrayList.removeAll(Collection<?> c)은 인자로 Collection을 받는다.
- 이 Collection이 포함하고 있는 객체를 해당 ArrayList에서 삭제
//아래 코드는 movies에서 marvel에 해당하는 항목을 모두 삭제하는 예제
ArrayList<String> marvel = new ArrayList<>();
marvel.add("Iron man");
marvel.add("Hulk");
marvel.add("Captain america");
System.out.println("marvel: " + marvel.toString());
ArrayList<String> movies = new ArrayList<>();
movies.add("Untouchable");
movies.add("Spiderman");
movies.add("Captain america");
movies.add("Hulk");
System.out.println("movies: " + movies.toString());
movies.removeAll(marvel);
System.out.println("movies - removeAll(marvel): " + movies.toString());
//출력결과
marvel: [Iron man, Hulk, Captain america]
movies: [Untouchable, Spiderman, Captain america, Hulk]
movies - removeAll(marvel): [Untouchable, Spiderman]
retainAll()
- argument로 제공한 컬렉션 내에 들어있는 값을 제외하고 모두 지워줌
3-14 : set()
- ArrayList에 들어있는 엘리먼트를 교체하고 싶을 때 사용
- arrayList.set(int index, E element)
- 파라미터로 전달한 index 위치의 값을, 2번째 파라미터(element)로 변경해 줌
- 변경되기 전 값을 return
3-15 : sort()
- ArrayList를 sorting 하기 위해서 Collections의 sort() 메소드를 사용할 수 있습니다.
- ArrayList를 sorting 하기 위해서 List의 sort() 메소드를 사용할 수 있습니다.(Java 8 이후)
3-15-1 : Collections.sort()
#1. Collections.sort(list);
- ArrayList를 오름차순으로 정렬
#2. Collections.sort(list, Collections.reverseOrder());
- Collections.sort()의 2번째 파라미터로 내림차순 정렬을 나타내는 Comparator를 전달해서, ArrayList를 내림차순으로 정렬
#3. Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
- String.CASE_INSENSITIVE_ORDER 를 전달하면, 대소문자 구분 없이 오름차순으로 정렬됩니다.
- 여기서 'a'와 'A'는 같은 순위로 취급되므로, 원래의 순서를 유지합니다.
#4. Collections.sort(list, Collections.reverseOrder(String.CASE_INSENSITIVE_ORDER));
- 대소문자 구분 없이, 내림차순으로 정렬합니다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class SortArrayList {
public static void main(String[] args) {
// ArrayList 준비
ArrayList<String> list = new ArrayList<>(Arrays.asList("C", "A", "B", "a"));
System.out.println("원본 : " + list); // [C, A, B, a]
// 오름차순으로 정렬
Collections.sort(list);
System.out.println("오름차순 : " + list); // [A, B, C, a]
// 내림차순으로 정렬
Collections.sort(list, Collections.reverseOrder());
System.out.println("내림차순 : " + list); // [a, C, B, A]
// 대소문자 구분없이 오름차순
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
System.out.println("대소문자 구분없이 오름차순 : " + list); // [a, A, B, C]
// 대소문자 구분없이 내림차순
Collections.sort(list, Collections.reverseOrder(String.CASE_INSENSITIVE_ORDER));
System.out.println("대소문자 구분없이 내림차순 : " + list); // [C, B, a, A]
}
}
3-15-2 : List.sort() - Java 8 이후
- default void sort(Comparator<? super E> c)
- ava 8 이후부터는 List에서는 sort() 메소드를 호출하여 정렬할 수 있다.
- Collections 객체를 사용하는 대신 List객체의 sort() 메서드를 사용하여 정렬
- sort()의 파라미터로 Comparator를 넘겨주는데, 앞의 예제와 달리
- Comparator 객체에서 Comparator를 가져와서 넘겨주었다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class SortArrayList {
public static void main(String[] args) {
// ArrayList 준비
ArrayList<String> list = new ArrayList<>(Arrays.asList("C", "A", "B", "a"));
System.out.println("원본 : " + list); // [C, A, B, a]
// 오름차순으로 정렬
list.sort(Comparator.naturalOrder());
System.out.println("오름차순 : " + list); // [A, B, C, a]
// 내림차순으로 정렬
list.sort(Comparator.reverseOrder());
System.out.println("내림차순 : " + list); // [a, C, B, A]
// 대소문자 구분없이 오름차순 정렬
list.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println("대소문자 구분없이 오름차순 : " + list); // [a, A, B, C]
// 대소문자 구분없이 내림차순 정렬
list.sort(Collections.reverseOrder(String.CASE_INSENSITIVE_ORDER));
System.out.println("대소문자 구분없이 내림차순 : " + list); // [C, B, a, A]
}
}
4. ArrayList ↔ List 변경
4-1 : ArrayList에서 List로 변경
4-1-1 : toArray()
- ArrayList를 array로 변환해야 할 때 사용하는 메소드이다.
- ArrayList.toArray() Method의 경우, Object array를 리턴한다.
- 이럴 경우, 적절히 타입 캐스팅을 해서 사용을 해야 하는 번거로움이 있다.
- Integer[] array = numbers.toArray(new Integer[numbers.size()]);
- 위와 같이, 리턴될 array의 타입을 지정해 준다면 타입 캐스팅을 할 필요가 없다.
- 정리하자면, ArrayList에 여러 가지 class의 object가 있는 경우는, ArrayList.toArray()를 사용하는 것이 편하고,
- 동일한 class의 object가 있는 경우는 ArrayList.toArray(T[] t)를 사용하시는 것이 편하다.
# arrayList.toArray()
- ArrayList의 요소를 새 Object 배열에 복사한다.
- List 클래스의 인스턴스 메서드인 toArray()는 Object 타입의 배열을 반환한다.
- 타입 변환이 자동으로 이루어지지 않아서 리턴 배열을 활용하기 번거롭다.
# arrayList.toArray(T[ ] a)
- ArrayList의 요소를 지정된 요소 형식의 새 배열에 복사한다
- T 타입 배열을 반환한다.
- T는 값 타입이 될 수 없기 때문에 int, double, float와 같은 타입의 배열은 이 방법으로는 얻을 수 없다.
- 파라미터 a의 길이는 0으로 지정하면 알아서 list의 길이에 맞게 조정되어 arr에 저장된다.
4-1-2 : 배열 length에 따른 size에 따른 차이
- ArrayList를 배열로 변경할 경우 길이 0의 배열 strs.toArray(new String[0]); 을 넘겨주면
- ArrayList 길이만큼 배열을 자동으로 생성하고 데이터를 복사하게 됩니다.
- 메서드 파라미터 a로 넘겨받은 배열의 길이가
- 1. 해당 ArrayList 객체의 size 보다 작은 경우 (a.length < size) : size만큼 배열을 생성하고 ArrayList의 데이터 복사 후 리턴합니다.
- 2.a 배열의 길이가 해당 ArrayList 객체의 size 보다 크거나 같은 경우 (1번 체크 이후. a.length >= size) : 배열 a에 ArrayList 데이터를 복사합니다.
- 3.a 배열의 길이가 > ArrayList 객체 size 경우(a.length > size) : a[size] = null을 할당합니다.
- 참조타입 배열은 초기화 시 null로 초기화되므로 해당 로직이 필요 없을 수 있지만, 초기화한 배열이 아닌 기존에 값이 들어있는 재사용하고자 전달한 경우엔 필요한 로직입니다.
- 그래서 ArrayList를 배열로 변경할 경우 길이 0의 배열 strs.toArray(new String[0]); 을 넘겨주면 ArrayList 길이만큼 배열을 자동으로 생성하고 데이터를 복사하게 됩니다.
4-2 : List에서 ArrayList 로 변경
4-2-1 : asList()
- ArrayList<String> friday = new ArrayList<>(Arrays.asList(array));
- Arrays.asList()는 리스트를 초기화할 때 자주 사용된다.
- List를 ArrayList로 변경하는 이유? - 처음에 다 초기화를 해버리는 Array와 달리 List는 빈 리스트를 만든 후 add를 해주는 식으로만 초기화를 해줄 수 있다는 점이 매우 불편하기 때문이다.
- 그런데, 이 Arrays.asList를 사용할 때에는 주의할 점이 있다.
위 메서드를 사용할 경우, 이를 할당받는 변수는 원래 만들어진 배열의 인스턴스를 가리킨다.
이런 이유 때문에 위의 방법으로 초기화된 리스트는 ArrayList의 특성(변경이 자유로운)을 갖지 못한다. - Arrays.asList를 이용해 변경이 자유로운 ArrayList를 만들고 싶은 경우에는, ArrayList 클래스의 생성자에 만들어진 List 자료구조를 넣어서 새롭게 만들어진 ArrayList 인스턴스를 참조하도록 해줘야 한다.
- Arrays의 private 정적 클래스인 ArrayList를 리턴한다.
java.util.ArrayList 클래스와는 다른 클래스이다. - java.util.Arrays.ArrayList 클래스는 set(), get(), contains() 메서드를 가지고 있지만
원소를 추가하는 메서드는 가지고 있지 않기 때문에 사이즈를 바꿀 수 없다. - list에 담겨있는 데이터를 수정했는데 원본 배열의 데이터까지 변경이 됐다.
List는 내부 구조가 배열로 만들어져 있다. - 따라서 asList()를 사용해서 반환되는 List도 배열을 갖게 된다.
- 이때, asList()를 사용해서 List 객체를 만들 때 새로운 배열 객체를 만드는 것이 아니라,
원본 배열의 주소값을 가져오게 된다.
따라서 asList()를 사용해서 내용을 수정하면 원본 배열도 함께 바뀌게 되고
원본 배열을 수정하면 그 배열로 만들어뒀던 asList()를 이용한 List 내용도 바뀌게 된다.
이러한 이유 때문에 Arrays.asList()로 만든 List에 새로운 원소를 추가하거나 삭제할 수 없다.
따라서 Arrays.asList()는 배열의 내용을 수정하려고 할 때 List로 바꿔서 편리하게 사용하기 위함.
4-2-2 : List 타입의 ArrayList가 아닌 Collection 타입의 ArrayList로 변환하기
- 만약 진짜 ArrayList를 받기 위해서는 다음과 같이 변환하면 된다.
ArrayList 생성자는 java.util.Arrays.ArrayList의 상위(super) 클래스인 Collection Type
도 받아들일 수 있다. - List<String> list = new ArrayList<String>(Arrays.asList(arr));
- 이제는 원본 배열과 list 객체에 담겨있는 배열 데이터는 별개의 주소값이라고 보면 된다.
package Test;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
public class TestArrayAsList {
public static void main(String[] args) {
String[] strs = {"alpha", "beta", "charlie"};
System.out.println(Arrays.toString(strs)); // [alpha, beta, charlie]
List<String> lst = new ArrayList<String>(Arrays.asList(strs));
System.out.println(lst); // [alpha, beta, charlie]
lst.add("ttt"); // 이제는 에러가 나지 않고 데이터를 추가 시킬 수 있다.
// Changes in array or list write thru
strs[0] += "88";
lst.set(2, lst.get(2) + "99"); // 2번째 인덱스 원소에 charlie99 넣음
System.out.println(Arrays.toString(strs)); // [alpha88, beta, charlie]
System.out.println(lst); // [alpha, beta, charlie99, ttt]
// Initialize a list using an array
List<Integer> lstInt = Arrays.asList(22, 44, 11, 33);
System.out.println(lstInt); // [22, 44, 11, 33]
}
}
4-2-3 : stream
- 값 타입 배열을 얻기 위해서는 int 값을 꺼내서 배열로 저장해야 한다. 이때 사용하는 것이 stream이다.
- list.stream(): Stream<Integer>을 반환한다.
- mapToInt(Integer::intValue): Integer의 intValue() 메서드를 참조해서 값 타입인 int로 언박싱한다.
- toArray(): IntStream의 원소를 배열로 변환한다.
List<Integer> list = new ArrayList<>();
// ...
int[] arr = list.stream()
.mapToInt(Integer::intValue)
.toArray();
4-2-4 : stream을 활용하여 primitive 타입 배열 얻기
- String 타입의 List를 배열로 변환할 때는 toArray()를 사용하면 변환할 수 있다.
- 하지만 int형과 같은 primitive 타입은 toArray()를 사용할 수 없다.
- 따라서 int형과 같은 primitive 타입은 아래의 방법을 통해 가능하다.
- primitive 타입 == 기본형 타입( boolean, byte, short, int, long, float, double, char )
public static void main(String args[]) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 방법 1
int[] arr1 = new int[list.size()]
for (int i = 0 ; i < list.size() ; i++) {
arr1[i] = list.get(i).intValue();
// 방법 2
int[] arr2 = list.stream()
.mapToInt(i -> i)
.toArray();
// 방법 3
int[] arr3 = list.stream()
.mapToInt(Integer::intValue)
.toArray();
// 방법 4
int[] arr4 = list.stream()
.filter(i -> i != null)
.mapToInt(i -> i)
.toArray();
}
- 방법 1은 가장 기본적인 방법으로, 반복문을 통해 각 요소별로 접근하여 intValue 메서드를 사용하여 int형으로 만든 후 배열에 넣는 형식이다.
- 방법 2와 3은 리스트를 스트림으로 변환 후, map을 이용해서 intStream을 가져오고, 그 후에 toArray()를 통해 배열로 만드는 방법이다.
- 방법 2와 방법 3의 차이는 map 연산 시 int형으로 변경하는 방법의 차이이다.
- 방법 2는 자바가 자동으로 각 요소의 Integer 요소를 int형으로 unboxing 해준다. (java 5 이상) 하지만 방법 3은 intValue 메서드를 통해 각 요소를 int형으로 변경해 준다.
- 방법 4는 방법 2에서 필터를 추가한 방법이다. 필터를 통해 리스트의 null을 걸러내는 방법이다.
'Programmers > JAVA' 카테고리의 다른 글
[ 프로그래머스 ] 소수찾기 - Java (0) | 2023.01.14 |
---|---|
[ 프로그래머스 ] 최소 직사각형 - Java (0) | 2023.01.14 |
[ 프로그래머스 ] 가장 가까운 같은 글자 - Java (0) | 2023.01.13 |
[ 프로그래머스 ] JadenCase 문자열 만들기 - Java (0) | 2023.01.10 |
[ 프로그래머스 ] 마법의 엘리베이터 - Java (0) | 2023.01.06 |