public class ArrayMain1 {
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println("==index 입력: O(1)==");
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
System.out.println(Arrays.toString(arr));
System.out.println("==index 변경: O(1)==");
arr[2] = 10;
System.out.println(Arrays.toString(arr));
System.out.println("==index 조회: O(1)==");
System.out.println("arr[2] = " + arr[2]);
System.out.println("==배열 검색: O(N)==");
int value = 10;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) {
System.out.println(value + "찾음");
break;
}
}
}
}
✔️배열의 첫 번째 위치에 추가하는 경우
✔️배열의 중간 위치에 추가하는 경우
✔️배열의 마지막 위치에 추가하는 경우
public class ArrayMain2 {
public static void main(String[] args) {
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
System.out.println(Arrays.toString(arr));
System.out.println("배열의 첫번째 위치에 3 추가 O(n)");
int newValue = 3;
addFirst(arr, newValue);
System.out.println(Arrays.toString(arr));
System.out.println("배열의 index(2) 위치에 4 추가 O(n)");
int index = 2;
int value = 4;
addAtIndex(arr, index, value);
System.out.println(Arrays.toString(arr));
System.out.println("배열의 마지막 위치에 5 추가 O(1)");
addLast(arr, 5);
System.out.println(Arrays.toString(arr));
}
private static void addFirst(int[] arr, int newValue) {
// 오른쪽에서부터 시작, 왼쪽의 데이터를 오른쪽으로 이동
// 데이터의 유실이 발생하지않음
for (int i = arr.length - 1; i > 0; i--) {
arr[i] = arr[i - 1];
}
arr[0] = newValue;
}
private static void addAtIndex(int[] arr, int index, int newValue) {
for (int i = arr.length - 1; i > index; i--) {
arr[i] = arr[i - 1];
}
arr[index] = newValue;
}
private static void addLast(int[] arr, int newValue) {
arr[arr.length - 1] = newValue;
}
}
배열의 경우 다음 2가지 단점이 존재
배열의 이런 불편함을 해소하고 동적으로 데이터를 추가할 수 있는 자료 구조를 List(리스트)라 한다.
public class MyArrayListV1 {
private static final int DEFAULT_CAPACITY = 5;
private Object[] elementData;
private int size = 0;
public MyArrayListV1() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV1(int capacity) {
elementData = new Object[capacity];
}
public int size() {
return size;
}
public void add(Object o) {
elementData[size] = o;
size++;
}
public Object get(int index) {
return elementData[index];
}
public Object set(int index, Object element) {
Object oldValue = elementData[index];
elementData[index] = element;
return oldValue;
}
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (elementData[i] == o) {
return i;
}
}
return -1;
}
@Override
public String toString() {
return "MyArrayListV1{" +
"elementData=" + Arrays.toString(elementData) +
", size=" + size +
'}';
}
}
public class MyArrayListV1Main {
public static void main(String[] args) {
MyArrayListV1 myArrayListV1 = new MyArrayListV1();
System.out.println("==데이터 추가==");
System.out.println(myArrayListV1);
myArrayListV1.add("a");
System.out.println(myArrayListV1);
myArrayListV1.add("b");
System.out.println(myArrayListV1);
myArrayListV1.add("c");
System.out.println(myArrayListV1);
System.out.println("==기능 사용==");
System.out.println("myArrayListV1.size() " + myArrayListV1.size());
System.out.println("myArrayListV1.get() = " + myArrayListV1.get(2));
System.out.println("myArrayListV1.indexOf() " + myArrayListV1.indexOf("b"));
System.out.println("myArrayListV1.set(), oldValue = " + myArrayListV1.set(1, "f"));
System.out.println(myArrayListV1);
System.out.println("==범위 초과==");
myArrayListV1.add("d");
System.out.println(myArrayListV1);
myArrayListV1.add("e");
System.out.println(myArrayListV1);
// 문제 발생 → Index 5 out of bounds for length 5
myArrayListV1.add("g");
System.out.println(myArrayListV1);
}
}
grow()
호출할 때 배열의 크기는 기존과 비교해서 2배씩 증가한다.public class MyArrayListV2 {
private static final int DEFAULT_CAPACITY = 5;
private Object[] elementData;
private int size = 0;
public MyArrayListV2() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV2(int capacity) {
elementData = new Object[capacity];
}
public int size() {
return size;
}
public void add(Object o) {
if (size == elementData.length) {
grow();
}
elementData[size] = o;
size++;
}
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
public Object get(int index) {
return elementData[index];
}
public Object set(int index, Object element) {
Object oldValue = elementData[index];
elementData[index] = element;
return oldValue;
}
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (elementData[i] == o) {
return i;
}
}
return -1;
}
@Override
public String toString() {
return "MyArrayListV1{" +
"elementData=" + Arrays.toString(elementData) +
", size=" + size +
'}';
}
}
public class MyArrayListV2Main {
public static void main(String[] args) {
MyArrayListV2 myArrayListV2 = new MyArrayListV2();
myArrayListV2.add("a");
myArrayListV2.add(2);
myArrayListV2.add(3);
myArrayListV2.add(4);
myArrayListV2.add(5);
System.out.println(myArrayListV2);
// 용량 다 찬 상태에서 새로운 원소를 추가할 경우
myArrayListV2.add(6);
System.out.println(myArrayListV2);
}
}
add(Index, 데이터)
: index 위치에 데이터를 추가한다.remove(Index)
index 위치의 데이터를 삭제한다.public class MyArrayListV3 {
private static final int DEFAULT_CAPACITY = 5;
private Object[] elementData;
private int size = 0;
public MyArrayListV3() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV3(int capacity) {
elementData = new Object[capacity];
}
public int size() {
return size;
}
public void add(Object o) {
if (size == elementData.length) {
grow();
}
elementData[size] = o;
size++;
}
public void add(int index, Object o) {
if (size == elementData.length) {
grow();
}
shiftRightFrom(index);
elementData[index] = o;
size++;
}
private void shiftRightFrom(int index) {
for (int i = size; i > index; i--) {
elementData[i] = elementData[i-1];
}
}
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
public Object get(int index) {
return elementData[index];
}
public Object set(int index, Object element) {
Object oldValue = elementData[index];
elementData[index] = element;
return oldValue;
}
public int indexOf(Object o) {
for (int i = 0; i < size; i++) {
if (elementData[i] == o) {
return i;
}
}
return -1;
}
public Object remove(int index) {
Object o = get(index);
shiftLeftFrom(index);
size--;
elementData[size] = null;
return o;
}
private void shiftLeftFrom(int index) {
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i+1];
}
}
@Override
public String toString() {
return "MyArrayListV1{" +
"elementData=" + Arrays.toString(elementData) +
", size=" + size +
'}';
}
}
public class MyArrayListV3Main {
public static void main(String[] args) {
MyArrayListV3 myArrayListV3 = new MyArrayListV3();
myArrayListV3.add("a");
myArrayListV3.add("b");
myArrayListV3.add("c");
System.out.println(myArrayListV3);
System.out.println("addLast");
myArrayListV3.add(3, "addLast");
System.out.println(myArrayListV3);
System.out.println("addFirst");
myArrayListV3.add(0, "addFirst");
System.out.println(myArrayListV3);
Object removed1 = myArrayListV3.remove(4);
System.out.println("remove(4) = " + removed1);
System.out.println(myArrayListV3);
Object removed2 = myArrayListV3.remove(0);
System.out.println("remove(0) = " + removed2);
System.out.println(myArrayListV3);
}
}
Object
를 입력받기 때문에 아무 데이터나 입력받을 수 있고 또 결과로 Object
를 반환한다. 따라서 필요한 경우 다운캐스팅을 해야하고, 타입 안전성이 떨어지는 단점이 있다.Object[]
를 그대로 사용하는 이유 : 제네릭은 런타임 시기에 이레이저에 의해 타입 정보가 사라진다. 따라서 런타임에 타입 정보가 필요한 생성자에 사용할 수 없다. 제네릭을 기반으로 배열을 생성하는 다음 코드는 작동하지 않고 컴파일 오류가 발생한다.T[]
와 같은 코드를 작성하면 컴파일러가 이를 인식할 수 없다.public class MyArrayListV4BadMain {
public static void main(String[] args) {
MyArrayListV3 numberList = new MyArrayListV3();
numberList.add(1);
numberList.add(2);
numberList.add(3);
numberList.add("숫자");
System.out.println(numberList);
// Object를 반환하는 get
Integer i0 = (Integer) numberList.get(0);
Integer i1 = (Integer) numberList.get(1);
System.out.println(i0);
System.out.println(i1);
// class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
Integer i3 = (Integer) numberList.get(3);
System.out.println(i3);
}
}
public class MyArrayListV4<E> {
private static final int DEFAULT_CAPACITY = 5;
private Object[] elementData;
private int size = 0;
public MyArrayListV4() {
elementData = new Object[DEFAULT_CAPACITY];
}
public MyArrayListV4(int capacity) {
elementData = new Object[capacity];
}
public int size() {
return size;
}
public void add(E e) {
if (size == elementData.length) {
grow();
}
elementData[size] = e;
size++;
}
public void add(int index, E e) {
if (size == elementData.length) {
grow();
}
shiftRightFrom(index);
elementData[index] = e;
size++;
}
private void shiftRightFrom(int index) {
for (int i = size; i > index; i--) {
elementData[i] = elementData[i-1];
}
}
private void grow() {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity * 2;
elementData = Arrays.copyOf(elementData, newCapacity);
}
@SuppressWarnings("unchecked")
public E get(int index) {
return (E) elementData[index];
}
public E set(int index, E e) {
E oldValue = get(index);
elementData[index] = e;
return oldValue;
}
public int indexOf(E o) {
for (int i = 0; i < size; i++) {
if (elementData[i] == o) {
return i;
}
}
return -1;
}
public E remove(int index) {
E o = get(index);
shiftLeftFrom(index);
size--;
elementData[size] = null;
return o;
}
private void shiftLeftFrom(int index) {
for (int i = index; i < size - 1; i++) {
elementData[i] = elementData[i+1];
}
}
@Override
public String toString() {
return "MyArrayListV1{" +
"elementData=" + Arrays.toString(elementData) +
", size=" + size +
'}';
}
}
public class MyArrayListV4Main {
public static void main(String[] args) {
MyArrayListV4<String> stringList = new MyArrayListV4<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
String string = stringList.get(0);
System.out.println(string);
MyArrayListV4<Integer> integerList = new MyArrayListV4<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
Integer integer = integerList.get(0);
System.out.println(integer);
}
}