일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 다들 안잊어서
- 수제비2022 정리
- 아싸의 생일
- 얘들아 잘 지내니
- 레이튼 교수와 이상한 마을
- 2022 정보처리기사
- 정보처리기사 2022
- 생일축하해 나 자신
- pem키 분실
- 대학생
- 교수님 과제 이제 그만..
- N-Queen
- 수제비 2022
- 모바일 청첩장
- 교육봉사
- FLUTTER
- 자가격리
- 개강해짐
- 대외활동
- 다행이야...ㅎ
- 플러터
- 뽀모도로 타이머
- 지독한 컨셉충
- CRUDS
- 재택치료
- 확진
- 다음에 또 만나자
- 스프링 MVC
- AWS
- 정보처리기사2022
- Today
- Total
Rei’s Tech diary
List - LinkedList 이론과 구현 본문
📌ArrayList vs. LinkedList
1) 공통점
- 동일한 타입의 객체 수집
- 메모리 동적 할당
- 데이터의 추가, 변경, 삭제 등의 메서드
2) 차이점
- 생성자로 저장공간의 크기 지정 불가
List<Integer> aLinkedList - new LinkedList<Integer>(); //O
List<Integer> aLinkedList - new LinkedList<Integer>(20); //X 불가!
- 데이터의 내부 저장 방식이 index기반이 아닌, 앞뒤 객체의 위치 정보를 저장
📌ArrayList<E> vs. LinkedList<E>의 데이터 추가 매커니즘
구분 | ArrayList | LinkedList |
추가, 삭제 (add, remove) | 속도 늦음 | 속도 빠름 |
검색 (get) | 속도 빠름 | 속도 늦음 |
#1. ArrayList의 데이터 추가
- 다음과 같이 데이터 삽입시, 삽입된 뒷부분 데이터의 index값을 전부 변경해야한다.
- 검색시, 인덱스를 통해 바로 접근, 특정 인덱스의 데이터를 O(1) 시간에 바로 검색
aList.add(2, '아');
#2. LinkedList의 데이터 추가
- 데이터 삽입시, 앞뒤 데이터의 위치정보만 변경
- 검색 시 O(n), 원하는 노드를 찾기 위해 처음부터 끝까지 순차적으로 검색
aLinkedList.add(2, '아');
📌List<E> 인터페이스 주요 메서드
- LinkedList도 마찬가지로 List<E> 인터페이스의 구현 클래스이기 때문에, 아래와같은 메서드를 그대로 사용할 수 있다.
구분 | 리턴타입 | 메서드 이름 | 기능 |
데이터 추가 |
boolean | add(E element) | 매개변수로 입력된 원소를 리스트 마지막에 추가 |
void | add(int index, E element) | index 위치에 입력된 원소 추가 | |
boolean | addAll(Collection<? Extends E> c) | 매개변수로 입력된 컬렉션 전체를 마지막에 추가 | |
boolean | addAll(int index, Collection <? Extends E c) | index 위치에 입력된 컬렉션 전체를 추가 | |
데이터 변경 | E | set(int index, E element) | index 위치의 원소값을 입력된 원소로 변경 |
데이터 삭제 | E | remove(in index) | index 위치의 원소값 삭제 |
boolean | remove(Object o) | 원소 중 매개변수입력과 동일한 객체 삭제 | |
void | clear() | 전체 원소 삭제 | |
리스트 데이터 정보추출 |
E | get(int index) | index 위치의 원소값을 꺼내어 리턴 |
int | size() | 리스트 객체 내에 포함된 원소 갯수 | |
boolean | isEmpty() | 리스트의 원소가 하나도 없는지 여부 리턴 | |
리스트 배열 변환 |
Object[] | toArray() | 리스트를 Object 배열로 변환 |
T[] | toArray(T[] t) | 입력매개변수로 전달한 타입의 배열로 변환 |
예시코드)
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class LinkedListMethod {
public static void main(String[] args) {
List<Integer> linkedlist1 = new LinkedList<>();
//#1. add(E element)
linkedlist1.add(3);
linkedlist1.add(4);
linkedlist1.add(5);
System.out.println(linkedlist1.toString()); // System.out.println(aList1);과 동일 : System.out.println시 자동으로 toString 호출
//#2. add(int index, E element)
linkedlist1.add(1, 6); //원소 삽입 : 나머지 원소는 뒤로 밀린다.
System.out.println(linkedlist1);
//#3. addAll(또다른 리스트 객체)
List<Integer> linkedlist2 = new LinkedList<>();
linkedlist2.add(1);
linkedlist2.add(2);
System.out.println(linkedlist2);
linkedlist2.addAll(linkedlist1);
System.out.println(linkedlist2);
//#4. addAll(int index, 또 다른 리스트 객체)
List<Integer> linkedlist3 = new LinkedList<>();
linkedlist3.add(1);
linkedlist3.add(2); //[1,2]
linkedlist3.addAll(1, linkedlist3); //[1,1,2,2]
System.out.println(linkedlist3);
//#5. set(int index, E element)
linkedlist3.set(1, 5); //[1,5,2,2]
linkedlist3.set(3, 6); //[1,5,2,6]
// aList3.set(4, 7); //예외발생 indexOutofBoundsException
System.out.println(linkedlist3);
//#6. remove(int index)
linkedlist3.remove(1); // 1번 index 삭제 [1,2,6]
System.out.println(linkedlist3);
//#7. remove(Object o)
linkedlist3.remove(new Integer(1)); //객체를 생성하여 삭제 요청
System.out.println(linkedlist3);
//#8. clear()
linkedlist3.clear();
System.out.println(linkedlist3);
//#9. isEmpty()
System.out.println(linkedlist3.isEmpty()); //true
System.out.println(linkedlist3.isEmpty()); //false
//#10 size()
System.out.println(linkedlist3.size());
linkedlist3.add(1);
linkedlist3.add(2);
linkedlist3.add(3);
System.out.println(linkedlist3.size());
//#11 get(int index)
System.out.println("0번째 : " + linkedlist3.get(0));
System.out.println("1번째 : " + linkedlist3.get(1));
System.out.println("2번째 : " + linkedlist3.get(2));
for(int i : linkedlist3){
System.out.println(i);
}
for(int i = 0; i < linkedlist3.size(); i++){
System.out.println(i + "번째 : " + linkedlist3.get(i));
}
//#12. toArray() List --> Array
Object[] object = linkedlist3.toArray();
System.out.println(Arrays.toString(object)); //[1, 2, 3]
//#13. toArray(T[] t)
Integer[] integer1 = linkedlist3.toArray(new Integer[1]); // [1, 2, 3]
System.out.println(Arrays.toString(integer1));
Integer[] integer2 = linkedlist3.toArray(new Integer[5]); // [1, 2, 3, null, null]
System.out.println(Arrays.toString(integer2));
}
}
📌ArrayList vs. LinkedList의 성능 비교
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ArrayListVsLinkedList {
public static void main(String[] args){
//#1. 데이터 추가 시간 비교
List<Integer> aList = new ArrayList<>();
List<Integer> lList = new LinkedList<>();
long startTime = 0, endTime = 0;
//@1-1 ArrayList 데이터 추가 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
aList.add(0, i);
}
endTime = System.nanoTime();
System.out.println("ArrayList 데이터 추가 시간 = " + (endTime - startTime) + " ns");
//@1-2 LinkedList 데이터 추가 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
lList.add(0, i);
}
endTime = System.nanoTime();
System.out.println("LinkedList 데이터 추가 시간 = " + (endTime - startTime) + " ns");
System.out.println("========================");
//@2-1 ArrayList 데이터 검색 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
aList.get(i);
}
endTime = System.nanoTime();
System.out.println("ArrayList 데이터 검색 시간 = " + (endTime - startTime) + " ns");
//@2-2 LinkedList 데이터 검색 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
lList.get(i);
}
endTime = System.nanoTime();
System.out.println("LinkedList 데이터 검색 시간 = " + (endTime - startTime) + " ns");
System.out.println("========================");
//@3-1 ArrayList 데이터 삭제 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
aList.remove(0);
}
endTime = System.nanoTime();
System.out.println("ArrayList 데이터 삭제 시간 = " + (endTime - startTime) + " ns");
//@2-2 LinkedList 데이터 삭제 시간
startTime = System.nanoTime();
for(int i = 0; i < 100000; i++){
lList.remove(0);
}
endTime = System.nanoTime();
System.out.println("LinkedList 데이터 삭제 시간 = " + (endTime - startTime) + " ns");
}
}
= 결과 =
데이터 추가/삭제에는 LinkedList가 우수.
데이터 검색에는 ArrayList가 우수하다.
결론적으로 정렬을 자주하는 경우엔 ArrayList를 사용하고 데이터의 추가/삭제가 많은 경우 LinkedList를 사용하는 것을 권장
💡LinkedList 구현
LinkedList를 다음과 같이 구현하였다.
public class MyLinkedList<E>{
private static class Node<E> {
E data;
Node<E> next;
Node(E data){
this.data = data;
this.next = null;
}
}
private Node<E> head;
private int size;
public MyLinkedList(){
this.head = null;
this.size = 0;
}
//삽입하는 메서드
public void add(E data) {
Node<E> newNode = new Node<>(data);
if(head == null){
head = newNode;
}else{
Node<E> current = head;
while(current.next != null){
current = current.next;
}
current.next = newNode;
}
size++;
}
//리스트의 특정 위치에 삽입하는 메서드
public void add(int index, E data){
if(index < 0 || index > size){
throw new IndexOutOfBoundsException();
}
Node<E> newNode = new Node<>(data);
if(index == 0){
newNode.next = head;
head = newNode;
}else{
Node<E> current = head;
for(int i = 0; i < index - 1; i++){
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
}
size++;
}
//특정 위치의 요소를 삭제하는 메서드
public void remove(int index) {
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
Node<E> removeNode;
if(index == 0){
removeNode = head;
head = head.next;
}else{
Node<E> current = head;
for(int i = 0; i < index - 1; i++){
current = current.next;
}
removeNode = current.next;
current.next = removeNode.next;
}
size--;
}
public E get(int index) {
if(index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
Node<E> current = head;
for(int i = 0; i < index; i++){
current = current.next;
}
return current.data;
}
public int size() {
return size;
}
}
정적 이너 클래스 Node<E>
- 연결 리스트의 노드를 나타내는 정적 이너 클래스 정의
- E data : 노드가 저장할 실제 data 값
- Node<E> next : 다음 노드를 가리키는 참조 필드, 초기값은 null
private static class Node<E> {
E data;
Node<E> next;
Node(E data){
this.data = data;
this.next = null;
}
}
필드 및 생성자
- head : 리스트의 첫번째 노드를 가리킴, 초기값 null
- size : 리스트의 현재 크기 저장, 초기값 0
private Node<E> head;
private int size;
public MyLinkedList(){
this.head = null;
this.size = 0;
}
데이터 추가 (add)
1) add (E data)
public void add(E data) { // 새로운 데이터를 리스트의 끝에 추가하는 메서드.
Node<E> newNode = new Node<>(data); // 새로운 노드를 생성하고 데이터 설정.
if(head == null){ // 만약 리스트가 비어 있으면, 새로운 노드를 head로 설정.
head = newNode;
}else{
Node<E> current = head; // 리스트의 첫 번째 노드를 current로 설정.
while(current.next != null){ // current의 next가 null이 아닐 때까지 계속 이동.
current = current.next;
}
current.next = newNode; // 마지막 노드의 next를 새로운 노드로 설정.
}
size++; // 리스트 크기 증가.
}
2) add(int index, E data)
// 특정 위치에 데이터 추가하는 메서드
public void add(int index, E data){ // 주어진 index 위치에 데이터를 삽입하는 메서드.
if(index < 0 || index > size){ // 유효하지 않은 인덱스 처리.
throw new IndexOutOfBoundsException(); // 인덱스가 범위를 벗어나면 예외 발생.
}
Node<E> newNode = new Node<>(data); // 새로운 노드 생성.
if(index == 0){ // 만약 인덱스가 0이면, 새로운 노드를 head 앞에 삽입.
newNode.next = head;
head = newNode; // head를 새 노드로 변경.
}else{
Node<E> current = head; // current를 head로 설정.
for(int i = 0; i < index - 1; i++){ // index-1 위치까지 current를 이동.
current = current.next;
}
newNode.next = current.next; // 새 노드의 next를 current의 next로 설정.
current.next = newNode; // current의 next를 새 노드로 설정.
}
size++; // 리스트 크기 증가.
}
데이터 삭제 (remove)
public void remove(int index) { // 주어진 index 위치의 요소를 삭제하는 메서드.
if(index < 0 || index >= size){ // 유효하지 않은 인덱스 처리.
throw new IndexOutOfBoundsException(); // 인덱스가 범위를 벗어나면 예외 발생.
}
Node<E> removeNode; // 삭제할 노드를 담을 변수.
if(index == 0){ // 만약 인덱스가 0이면, head를 삭제.
removeNode = head;
head = head.next; // head를 다음 노드로 변경.
}else{
Node<E> current = head; // current를 head로 설정.
for(int i = 0; i < index - 1; i++){ // index-1 위치까지 current를 이동.
current = current.next;
}
removeNode = current.next; // 삭제할 노드를 current의 next로 설정.
current.next = removeNode.next; // current의 next를 삭제된 노드의 next로 설정.
}
size--; // 리스트 크기 감소.
}
데이터 검색 (get)
public E get(int index) { // 주어진 index 위치의 데이터를 반환하는 메서드.
if(index < 0 || index >= size){ // 유효하지 않은 인덱스 처리.
throw new IndexOutOfBoundsException(); // 인덱스가 범위를 벗어나면 예외 발생.
}
Node<E> current = head; // current를 head로 설정.
for(int i = 0; i < index; i++){ // 인덱스까지 current를 이동.
current = current.next;
}
return current.data; // 해당 노드의 데이터를 반환.
}
리스트 크기 반환 (size)
public int size() { // 리스트의 크기를 반환하는 메서드.
return size;
}
'프로그래밍 > Data Structure' 카테고리의 다른 글
Set - TreeSet 이론과 구현 (0) | 2024.12.28 |
---|---|
Set - LinkedHashSet 이론과 구현 (2) | 2024.12.27 |
Set - HashSet 이론과 구현 (2) | 2024.12.27 |
List - Vector 이론과 구현 (2) | 2024.12.24 |
List - ArrayList 이론과 구현 (4) | 2024.12.24 |