캐시 무효화 Cache-Control:no-cache,no-store,must-revalidate pragma:no-cache no-cache vs must-revalidate
no-cache 원서버 접근 불가면 에러 or 200 ok
must.. 원서버 접근 불가면 무조건 504
토이 프로젝트 - 점심
오늘 플러터 셋팅하고 폴더 구조 어떻게 하는지 봄
회 고
- HTTP 강의 완강 (완료) - 지피티로 플러터 앞단 만들기(ing)
와 처음으로.. 강의 완강 해본 듯 ..? 강의 마지막에 다음에 어떻게 해야하는지 로드맵 보여주시는데 스프링 입문 강의 -> jpa 실무 루트로 가라고 하셔서 내일부터는 스프링 기본 강의 입문편 들어 볼까 생각중입니다.
http 캐시 부분 배웠는데 가끔 개발자도구에서 request 봤을때 cache-control:no-cache 종종 보고 오 노캐시군 이렇게 넘어갔는데 오늘 보니깐 전혀 다른얘기라,, 앗차차 했다.
오늘 플러터로 앞단 만드려고 했는데 환경셋팅하고 폴더구조 어떻게 하는지 보다가 끝남 ㅋ 그리고 좀 에바인게 플러터 크로스 플랫폼이라고 해서 선택했는데.. 알고보니.. 아이폰 앱 빌드 할 때는 무조건 맥에서 해야하는...지.. 오늘 알았습니다 ㅋ 일단 고 하고 후에 알아봐야겠다...
내부구조 : ArrayList는 내부적으로 배열을 사용해서 데이터를 저장한다. 초기 크기를 설정할수 있지만 데이터가 추가되면 크키가 자동으로 증가된다.
특징
https://poiemaweb.com/js-array-is-not-arrray
- 인덱스를 통한 접근 속도 : 내부적으로 배열으로 되어 있기 때문에 인덱스를 통해 데이터를 접근하기 때문에 굉장히 빠르다.O(1)
- 삽입/삭제 속도 : 배열의 특정 위치에 삽입하거나 삭제하려면 해당 위치 요소를 다 이동해야 하므로 성능이 떨어진다. O(n)
- 메모리 사용효율 : 배열은 메모리가 할당될때 연속적으로 할당되므로 LinkedList보다 메모리 사용이 효율적이다.
LinkedList
노드를 사용해서 데이터를 저장하는 리스트 자료구조
public class Node {
Object item;
Node next;
public Node(Object item){
this.item = item;
}
}
public class MyLinkedListV3<E> {
private Node<E> first;
private int size = 0;
...
}
내부구조 : LinkedList 는 내부적으로 노드들로 이어져 있다. 노드는 데이터와 다음, 이전 노드를 가리키는 참조로 되어 있다.
특징
- 삽입/삭제 속도 : 리스트의 처음이나 중간에 데이터를 삽입하거나 삭제하는 경우, 노드의 이전 이후 참조만 변경하면 되므로 빠르다 O(1)
- 인덱스를 통한 접근 속도 : 인덱스를 통해 접근하려면 첫 노드부터 순차적으로 탐색 해야하므로 속도가 느리다. O(n)
- 메모리 사용 효율 : 각 노드가 데이터와 두개의 참조(다음,이전)을 가지고 있기 때문에, ArrayList 보다 메모리를 더 많이 사용한다.
ArrayList vs LinkedList 성능 비교
package collection.list;
public class MyListPerformanceTest {
public static void main(String[] args) {
int size = 50_000;
System.out.println("== MyArrayList 추가 =="); // 크기만큼 데이터 추가
addFirst(new MyArrayList<>(),size);
addMid(new MyArrayList<>(),size);
MyArrayList<Integer> arrayList = new MyArrayList<>();
addLast(arrayList,size);
System.out.println("== MyLinkedList 추가 ==");
addFirst(new MyLinkedList<>(),size);
addMid(new MyLinkedList<>(),size);
MyLinkedList<Integer> linkedList = new MyLinkedList<>();
addLast(linkedList,size);
int loop = 10000;
System.out.println("==MyArrayList 조회=="); // 루프만큼 인덱스로 조회
getIndex(arrayList, loop, 0);
getIndex(arrayList, loop, size / 2);
getIndex(arrayList, loop, size - 1);
System.out.println("==MyLinkedList 조회==");
getIndex(linkedList, loop, 0);
getIndex(linkedList, loop, size / 2);
getIndex(linkedList, loop, size - 1);
System.out.println("==MyArrayList 검색==");
search(arrayList, loop, 0);
search(arrayList, loop, size / 2);
search(arrayList, loop, size - 1);
System.out.println("==MyLinkedList 검색==");
search(linkedList, loop, 0);
search(linkedList, loop, size / 2);
search(linkedList, loop, size - 1);
}
private static void addFirst(MyList<Integer> list, int size){
long startTime = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
list.add(0,i);
}
long endTime = System.currentTimeMillis();
System.out.println("앞에 추가 - 크기: " + size + ", 계산 시간: " + (endTime - startTime) + "ms");
}
private static void addMid(MyList<Integer>list, int size){
long startTime = System.currentTimeMillis();
for (int i = 0; i <size ; i++) {
list.add(i/2,i);
}
long endTime = System.currentTimeMillis();
System.out.println("평균 추가 - 크기: " + size + ", 계산 시간: " + (endTime - startTime) + "ms");
}
private static void addLast(MyList<Integer>list, int size){
long startTime = System.currentTimeMillis();
for (int i = 0; i < size ; i++) {
list.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("뒤에 추가 - 크기: " + size + ", 계산 시간: " + (endTime - startTime) + "ms");
}
private static void getIndex(MyList<Integer> list, int loop, int index){
long startTime = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
list.get(index);
}
long endTime = System.currentTimeMillis();
System.out.println("index: " + index + ", 반복: " + loop + ", 계산 시간: " + (endTime - startTime) + "ms");
}
private static void search(MyList<Integer> list, int loop ,int findValue){
long startTime = System.currentTimeMillis();
for (int i = 0; i < loop; i++) {
list.indexOf(findValue);
}
long endTime = System.currentTimeMillis();
System.out.println("findValue: " + findValue + ", 반복: " + loop + ", 계산 시간: " + (endTime - startTime) + "ms");
}
}
== MyArrayList 추가 ==
앞에 추가 - 크기: 50000, 계산 시간: 2051ms
평균 추가 - 크기: 50000, 계산 시간: 1018ms
뒤에 추가 - 크기: 50000, 계산 시간: 3ms
== MyLinkedList 추가 ==
앞에 추가 - 크기: 50000, 계산 시간: 4ms
평균 추가 - 크기: 50000, 계산 시간: 1316ms
뒤에 추가 - 크기: 50000, 계산 시간: 1626ms
==MyArrayList 조회==
index: 0, 반복: 10000, 계산 시간: 0ms
index: 25000, 반복: 10000, 계산 시간: 1ms
index: 49999, 반복: 10000, 계산 시간: 0ms
==MyLinkedList 조회==
index: 0, 반복: 10000, 계산 시간: 0ms
index: 25000, 반복: 10000, 계산 시간: 313ms
index: 49999, 반복: 10000, 계산 시간: 624ms
==MyArrayList 검색==
findValue: 0, 반복: 10000, 계산 시간: 1ms
findValue: 25000, 반복: 10000, 계산 시간: 123ms
findValue: 49999, 반복: 10000, 계산 시간: 231ms
==MyLinkedList 검색==
findValue: 0, 반복: 10000, 계산 시간: 1ms
findValue: 25000, 반복: 10000, 계산 시간: 447ms
findValue: 49999, 반복: 10000, 계산 시간: 904ms
위에서 구현한 배열리스트와 연결리스트 성능
기능
배열 리스트
연결 리스트
앞에 데이터 추가(삭제)
O(n) - 2051ms (크기만큼 뒤로 밈)
O(1) - 4ms (first정보가 있음)
평균 데이터 추가(삭제)
O(n) - 1018ms (인덱스로 한번에 찾지만 데이터를 뒤로 밈)
O(n) - 1316ms(루프만큼 돌아서 인덱스를 찾음)
뒤에 데이터 추가(삭제)
O(1) - 3ms (인덱스로 한번에 찾고 뒤로 밀 데이터 없음)
O(n) - 1626ms (루프만큼 돌아서 인덱스를 찾음)
인덱스 조회
O(1) - 1ms (내부가 배열로 되어 있음 한버넹 찾음)
O(n) - (평균) 313ms
검색
O(n) - (평균) 123ms
O(n) - (평균) 447ms
자바에서 제공하는 배열리스트와 연결리스트 성능 비교
(※자바에서 제공하는 연결리스트는 이중연결리스트이다.)
(왼) 자바 제공 (오) 구현한 연결리스트
기능
배열 리스트
연결 리스트
앞에 데이터 추가(삭제)
O(n) - 106ms
O(1) - 2ms
평균 데이터 추가(삭제)
O(n) - 49ms
O(n) - 1116ms
뒤에 데이터 추가(삭제)
O(1) - 1ms
O(n) - 2ms
인덱스 조회
O(1) - 1ms
O(n) - (평균) 312ms
검색
O(n) - (평균) 104ms
O(n) - (평균) 427ms
실제 성능
- 이론적으로 LinkedList의 중간 삽입 연산은 ArrayList보다 빠를 수 있다.(왜냐면 순차로 찾아서 중간에 싸악 삽입 또는 삭제해주면 되니깐 ArraylList는 뒤로 데이터를 다 밀어줘야해서 더 늦다.) 그러나 실제적 접근 속도, 메모리 할당 및 해체 비용, cpu 캐시 활용도 등 다양한 요소에 의해 영향 받는다.
추가로 'ArrayList' 는 데이터를 한 칸씩 직접 이동하지 않고, 대신에 메모리 고속 복사(자바에서 System.arrayCopy)를 사용한다.
'ArrayList' 는 요소들이 메모리 상에서 연속적으로 위치하여 CPU 캐시 효율이 좋고, 메모리 접근 속도가 빠르다.
반면, 'LinkedList' 는 각 요소가 별도의 객체로 존재하고 다음 요소의 참조를 저장하기 때문에 CPU 캐시 효율 이 떨어지고, 메모리 접근 속도가 상대적으로 느려질 수 있다
정리하면 이론적으로 'LinkedList' 가 중간 삽입에 있어 더 효율적일 수 있지만, 현대 컴퓨터 시스템의 메모리 접근 패 턴, CPU 캐시 최적화, 메모리 고속 복사 등을 고려할 때 'ArrayList'가 실제 사용 환경에서 더 나은 성능을 보여주는 경우가 많다.
배열 리스트 vs 연결 리스트
대부분의 경우 배열 리스트가 성능상 유리하다. 이런 이유로 실무에서는 주로 배열 리스트를 기본으로 사용한다. 만약 데이터를 앞쪽에서 자주 추가하거나 삭제할 일이 있다면 연결 리스트를 고려하자.