[java] 리스트 List

Published: by Creative Commons Licence

참고

생활코딩-리스트

리스트

배열은 인덱스가 있어 데이터를 매우 빠르게 검색할 수 있다. 인덱스를 사용해야 하기 때문에 인덱스값은 유일한 값으로 고정되어 있다. 이러한 이유로 어떤 요소를 삭제하게 되면 삭제된 자리는 비워둔 채로 둬야 하므로 메모리 낭비가 발생한다. 삭제가 가능하기 떄문에 요소가 있는지 없는지를 항상 확인해야 한다.
그에 반해 리스트는 배열보다는 빠르게 데이터 조회가 되지 않지만 빈 공간을 두지 않는 장점이 있다. 따라서 리스트에서는 인덱스보다는 요소간 순서가 중요하다.

리스트의 기능

리스트는 순서가 있는 컬렉션이고 비어 있는 요소를 허용하지 않고, 중복된 데이터를 허용하는 데이터 스트럭쳐다.

  • 어느 곳이든 요소를 추가/삭제를 할 수 있다.
  • 리스트에 데이터가 있는지 확인할 수 있다.
  • 리스트의 모든 데이터에 접근 가능하다.

내장된 리스트

리스트에는 ArrayListLinkedList로 2개의 내장된 리스트가 있다. 데이터를 빈번히 조회한다면 ArrayList가, 데이터의 추가/삭제가 빈번하다면 LinkedList가 훨씬 효과적이다. 두 리스트는 실행결과는 비슷할 수 있지만 구현 방법은 아주 다르다.

ArrayList days = new ArrayList();
days.add("월");
days.add("화");
days.add("수");
days.add("목");
days.add("금");
days.add("토");
days.add("일");
days.remove(3);
logger.debug("{}", days); // [월, 화, 수, 금, 토, 일]
LinkedList days = new LinkedList();
days.add("월");
days.add("화");
days.add("수");
days.add("목");
days.add("금");
days.add("토");
days.add("일");
days.remove(3);
logger.debug("{}", days); // [월, 화, 수, 금, 토, 일]

ArrayList

ArrayList는 이름에서 유추할 수 있듯이 배열로 구현한 리스트다. 따라서 인덱스를 이용해 데이터 접근한다. 따라서 데이터 조회할때는 빠르지만 데이터 추가/삭제는 느리다.

생성

ArrayList 객체를 만들어야 한다.

ArrayList<데이터 타입> 리스트명 = new ArrayList<>();

ArrayList<String> days = new ArrayList<>();

데이터 추가

.add() 메서드를 이용해 데이터를 리스트의 맨 뒤에 덧붙일 수 있다. 인수로 index가 넘어오면 해당 index 위치에 데이터를 추가 한다. 리스트는 비어 있는 공간을 허용하지 않기 때문에 처음이나 중간에 요소를 추가하면 해당 위치 이후 요소들은 한 칸씩 뒤로 물러나야 한다. 배열은 크기가 고정되어 있지만 데이터가 추가되어 기존 크기를 벗어나게 되면 자동으로 기존 배열의 2배 긴 새 배열을 만들어 기존 데이터를 새로운 배열로 복제한다.

boolean add(E e)
void add(int index, E element)
boolean addAll(Collection<? extends E> c)
boolean addAll(int index, Collection<? extends E> c)
days.add("월");
days.add("화");
days.add("수");
days.add("금");
days.add("토");
days.add("일");
days.add(3, "목");

데이터 삭제

.remove()로 특정 인덱스의 요소를 삭제한다. 리스트는 비어 있는 공간을 허용하지 않기 때문에 리스트의 처음이나 중간에 요소를 삭제하면 해당 위치 이후 요소들은 한 칸씩 앞으로 당겨야 한다.

E remove(int index)
boolean remove(Object o)
void removeRange(int fromIndex, int toIndex)
boolean removeAll(Collection<?> c)
days.remove(4);
logger.debug("{}", days); // [월, 화, 수, 목, 토, 일]
boolean b = days.remove("가"); // 리스트에 해당하는 객체가 없으면 삭제는 무시하고 변경되지 않는다.
logger.debug("{}", b); // false

데이터 가져오기

.get() 메서드로 요소 값을 가져올 수 있다. 리스트는 메모리 주소(인덱스)를 정확하게 참조해서 데이터를 가져오기 때문에 아주 빠르다.

E get(int index)

days.get(1); // 화

데이터 반복

Iterator를 이용해 반복한다.

Iterator<E> iterator()

Iterator<String> it = days.iterator();

while(it.hasNext()) { // 다음요소가 있는지 체크 true/false
	logger.debug("{}", it.next()); // 요소를 순서대로 리턴
}

또 다른 반복 방법으로는 for-each를 사용할 수 있다. 물론 일반 for문도 가능하다.

for (String s : days) {
			logger.debug("{}", s); // 월 화 수 목 토 일
}

데이터 크기

.size()를 이용해 리스트 요소의 개수를 확인한다.

int size

logger.debug("{}", days.size()); // 6

데이터 위치 찾기

.indexOf()를 이용해 원하는 요소의 위치를 인덱스로 리턴한다.

int indexOf(Object o)

logger.debug("{}", days.indexOf("일")); // 5번째 인덱스에 있다.

LinkedList

노드(=element) 간 연결을 이용해서 리스트를 구현한다. 노드는 데이터필드와 링크 필드로 이뤄져 있고 보통 data, next 변수를 사용한다. data에는 노드의 값이 저장되고, next에는 다음 노드의 포인터나 참조값을 저장하여 노드 간 연결을 한다.
첫번째 노드 정보를 가지고 있는 건 head라고 한다.
개념 이해를 돕기위해 다음 사이트를 방문해 보라. https://visualgo.net/en/list

LikedList 선언

LinkedList<Integer> lnList = new LinkedList<>();

데이터(값) 추가

객체 타입의 데이터를 추가할 수 있고 index로 추가하기 원하는 곳을 지정할 수 있다.

boolean add(E e)
void add(int index, E element)
void addFirst(E e)
void addLast(E e)
boolean addAll(Collection<? extends E> c)
boolean addAll(int index, Collection<? extends E> c)
LinkedList<Integer> lnList = new LinkedList<>();

lnList.add(3);
lnList.add(2);
lnList.add(5);
lnList.add(5);
lnList.add(6);
lnList.add(1, 4);
lnList.addFirst(12);
lnList.addLast(1);
logger.debug("{}", lnList); // [12, 3, 4, 2, 5, 5, 6, 1]

데이터(값) 삭제

지정된 데이터가 없다면 첫 요소부터 삭제, index가 있으면 해당 위치의 데이터를 삭제한다.

E remove()
E remove(int index)
E removeFirst()
E removeLast()
boolean remove(Object o)
boolean removeFirstOccurrence(Object o)
boolean removeLastOccurrence(Object o)
void clear()
lnList.remove(); // 첫 노드를 삭제
logger.debug("{}", lnList); // [3, 4, 2, 5, 5, 6, 1]
lnList.remove(1); // 주어진 인덱스의 노드를 삭제
logger.debug("{}", lnList); // [3, 2, 5, 5, 6, 1]
lnList.removeFirst(); // 첫 노드를 삭제
logger.debug("{}", lnList); // [2, 5, 5, 6, 1]
lnList.removeLast(); // 마지막 노드를 삭제
logger.debug("{}", lnList); // [2, 5, 5, 6]
lnList.removeFirstOccurrence(new Integer(5)); // 주어진 객체와 동일한 첫 노드를 삭제, 없으면 무시
logger.debug("{}", lnList); // [2, 5, 6]
lnList.removeLastOccurrence(new Integer(3)); // 주어진 객체와 동일한 마지막 노드를 삭제, 없으면 무시
logger.debug("{}", lnList); // [2, 5, 6]
logger.debug("{}", lnList); // []

데이터 가져오기

.get() 메서드로 데이터를 가져올 수 있다. index가 인수로 들어로면 해당 인덱스의 값을 리턴한다. 하지만 ArrayList와 달리 인덱스 기준으로 빠르게 데이터에 접근할 수 없고 내부적으로 노드의 연결점을 찾아 가야 하기 때문에 시간이 오래 걸린다.

E getFirst()
E getLast()
E get(int index)
// 데이터 가져오기
lnList.add(3);
lnList.add(2);
lnList.add(5);
lnList.add(6);

logger.debug("{}", lnList.get(2)); // 5
logger.debug("{}", lnList.getFirst()); // 3
logger.debug("{}", lnList.getLast()); // 6

데이터 반복

Iterator를 이용해 반복한다.

Iterator<E> iterator()

LinkedList<Integer> lnList = new LinkedList<>(Arrays.asList(3,2,5,6));

// 데이터 반복
Iterator<Integer> it = lnList.iterator(); // AbstractSequentialList.iterator()를 확장한다.

while(it.hasNext()) { // 다음요소가 있는지 체크 true/false
  logger.debug("{}", it.next()); // 요소를 순서대로 리턴  3 2 5 6
}

또 다른 반복 방법으로는 for-each를 사용할 수 있다. 물론 일반 for문도 가능하다.

for (Integer i : lnList) {
  logger.debug("{}", i); // 3 2 5 6
}

데이터 크기

.size()를 이용해 리스트 요소의 개수를 확인한다.

int size

logger.debug("{}", lnList.size()); // 4

데이터 위치 찾기

.indexOf()를 이용해 원하는 요소의 위치를 인덱스로 리턴한다.

int indexOf(Object o)

logger.debug("{}", lnList.indexOf(5)); // 2번째 인덱스에 있다.