Java/Java 문법

Java 문법 5(배열, 복사, 컬렉션)

열심히 해 2024. 9. 4. 09:02

배열 (Array) :

선언하고 new 명령을 통해서 생성하며, 대괄호[] 안에 크기를 지정한다. Java의 자료구조 중 하나.

  • 한 번에 많은 양의 데이터를 다루거나 계산할 때 사용 -> 일반적으로 반복문과 같이 사용
  • 하나의 배열형 변수에는 같은 타입의 변수만 담을 수 있다
  • 배열은 index가 있고 이는 0번부터 시작함.

1. 선언  : 
`일반적인 선언 방식` 타입[] 변수명;

int[] intArray;    // 정수 배열
long[] longArray;
double[] doubleArray;  // 실수 배열 
char[] charArray;  // 문자 배열
String[] stringArray; // 문자열 배열

 

      타입 변수명[];

int intArray[];    // 정수 배열
long longArray[];
double doubleArray[];  // 실수 배열 
char charArray[];  // 문자 배열
String stringArray[]; // 문자열 배열

 

* new 명령이란 ?

new는 연산자로서 객체를 생성한다(=instance화 명령어).
  클래스를 만든 것만으로는 사용할 수 없고 그 클래스를 가지고 new 연산에 의해 객체를 생성해야만 (객체를) 사용할 수 있다.
  new명령에 의해 객체가 생성되면, heap영역에 객체가 생성되고 메모리 주소를 반환된다. 실제 값을 담지 않고 실제 값의 주소값을 저장하고 있게 된다.

  생성자를 호출하기 위해서, 새로운 객체를 만들기 위해 사용한다.

예를 들어,
String a = "tree";

String b = "tree"; 

위와 같이 코드를 작성하면
1. heap 영역에서 tree를 한 번만 저장하고 
2. heap 영역에서 a, b는 같은 메모리주소를 갖는다.

 

하지만 

String a = new String("tree");

String b = new String("tree"); 

와 같이 코드를 작성하면

1. heap 영역에서 각각의 새로운 String객체가 생성되고

2. a와 b는 다른 메모리주소를 갖는다.

 

2. 생성 : 변수의 할당과 같은 과정.

  • 배열은 생성될 때 각 타입별 초기값으로 초기화되어 채워진다.
  •  int는 0, boolean 은 false, String 은 null("") 값과 같은 초기값이 정해져 있다.

 

배열 생성, 조회, 초기화

더보기
public class arr01 {
    public static void main(String[] args) {
        // 배열 생성
        int[] intArray = new int[3]; //{0, 0, 0}
        boolean[] booArray = new boolean[3]; // {false, false, false}
        String[] stringArray = new String[3]; //{"", "", ""}

        // 배열 선언 -> 초기화
        int[] intArr;
        intArr = new int[3]; // {0, 0, 0}

        // 배열을 `순회` -> 배열의 값을 하나씩 뽑아서 조회한다.
        // 1) 단건 조회
        System.out.println(intArray[1]); // intArray의 두 번째 값을 조회.
        System.out.println("------------");
        // 2) *** 다건 조회 ***
        for(int i=0; i<intArray.length; i++){                          // intArray의 length는 3
            System.out.println(intArray[i]);
        }
    }
}

 

더보기
public class arr02 {
    public static void main(String[] args) {
        // 초기화 1) 배열에 특정값 지정하며 선언
        int[] intArr = {10, 20, 30, 40, 50};
        
        // 초기화 2) 선언 후 하나 하나 할당하기
        long[] longArr = new long[3];
        longArr[0] = 100000L;
        longArr[1] = 200000L;
        longArr[2] = 300000L;
        String[] stringArr = {"a", "b", "c", "d", "e"};
        
        //초기화 3) for문을 통해 특정값을 지정하며 선언
        for(int i=0; i<intArr.length; i++){
            intArr[i] = i;              //해당 배열 변수 뒤에 순번을 대괄호[]로 감싸서 명시하여 조회 가능, 이 경우에서는 모든 순번을 조회한다.
        }
        
        //다건 출력, index 0번부터 배열의 길이까지 모두 출력.
        for(int i=0; i<intArr.length; i++){
            System.out.println(intArr[i]);
        }
        //향상된 for문을 통한 intArr 지정
        System.out.println("--------------------");
        for(int item : intArr){
            System.out.println(item);
        }
        // 배열의 주소를 모두 같은 값으로 초기화
        System.out.println("--------------------");
        Arrays.fill(intArr, 3);
        for(int i=0; i<intArr.length; i++){
            System.out.println(intArr[i]);
        }
    }
}

 

복사

 

1. 얕은 복사 : 참조형 변수처럼 배열은 실제 값이 아닌 실제 값의 주소값을 가진다. 배열 변수 간에 대입 연산자 =를 사용해서 복사를 하게 되면, 주소값만 복사된다고 실제 값은 1개로 유지되는데 이를 '얕은 복사'라고 한다.

 

 

2. 깊은 복사 : 배열의 기본형 값을 꺼내서 복사하여 새로운 배열을 똑같이 만든다. 깊은 복사에는 반복문 for 문 을 통해서 하나씩 꺼내서 복사해 주는 방법과 여러 메서드를 사용하는 방법이 있다. 배열에 있는 순번과 값은 같아도, 두 배열의 주소는 다르다.

 

1) for문을 통한 깊은 복사

 

2) 메서드를 사용한 깊은 복사

 

.clone() 메서드

더보기
// 1. clone() 메서드
int[] a = { 1, 2, 3, 4 };
int[] b = a.clone(); // 가장 간단한 방법. 
// 하지만, clone() 메서드는 2차원 이상 배열(배열 안에 배열,...)에서는 얕은 복사로 동작합니다!!

 

Arrays.copyOf 메서드

더보기
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] a = { 1, 2, 3, 4 };
        int[] b = Arrays.copyOf(a, a.length); // (배열, 길이) 배열과 함께 length값도 같이 넣는다.
    }
}

 

String 배열

활용 예시

더보기
public class arr05 {
    public static void main(String[] args) {
        // 1) length
        String str = "ABCD";
        int strLength = str.length();
        System.out.println(strLength);
        // 2) charAt(int index)
        char strChar = str.charAt(2);
        System.out.println(strChar);
        // 3) substring (int fromInx, int toInx)
        String subStr = str.substring(0,2);
        System.out.println(subStr);
        // 4) equals(String str) boolean형
        String newStr = "ABCC";
        boolean strEqual = newStr.equals(str);
        System.out.println(strEqual);
        // 5) toCharArray() : String -> char[]
        char[] strCharArray = str.toCharArray();
        System.out.println(strCharArray[1]);
        // 6) char[] -> String
        char[] charArray = {'A', 'B','C','D'};
        String charArrayString = new String(charArray);
        System.out.println(charArrayString);
    }
}

다차원 배열

1. 2차원 배열 : 배열 안에 배열이 있음. {{}, {}, {}}

  •   선언 방법
    int[][] array
    int array[][]
    int[] array[]
  •   생성 방법
    int[][] array = new int[][];
  •   초기화 방법
    1. 중괄호를 사용하여 선언과 초기화를 동시에 하기
    int[][] array = {
            {1, 2, 3},
            {4, 5, 6}
    };

    2. 선언, 생성 이후 for문을 사용하기
더보기
public class arr06 {
    public static void main(String[] args) {
        int[][] array = new int[2][3]; // 최초 선언, 길이를 지정.

        for (int i = 0; i < array.length; i++) {   // 0, 1
            for (int j = 0; j < array[i].length; j++) {   // 0, 1, 2
                array[i][j] = 0;  // i, j는 위 노란색 네모박스 안에있는 숫자를 의미하며 인덱스라고 부른다.
            }
        }
    }
}

2. 가변 배열 : 2차원 배열에서 하나의 길이만 지정하고 나머지 하나의 길이는 지정하지 않은 채 배열하는 것. 이를 통해 각기 다른 길이를 가진 배열들의 배열을 선언, 생성할 수 있다.

더보기
public class arr06 {
    public static void main(String[] args) {
        // 생성 선언 초기화
        int[][] array = new int[3][];  // 배열 원소마다 각기 다른 길이로 지정할 수 있다.
        array[0] = new int[3];
        array[1] = new int[4];
        array[2] = new int[2];

        // 중괄호로 초기화 할 때도 가변 배열을 만들 수 있다.
        int[][] array2 = {
                {10, 20, 30},
                {10, 20, 30, 40},
                {10, 20}
        };
    }
}

 System.out.println(array[0][0]);  를 써서 조회 가능. for문을 쓰면 다건 조회도 가능.

 

배열 내에서 최댓값 구하기

더보기
public class arr07 {
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9};
        int max = arr[0];
        for(int i : arr){
            if(max < i){
                max = i;
            }
        }
        System.out.println(max);
    }
}

배열 내에서 최솟값 구하기

더보기
public class arr07 {
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 7, 9};
        int min = arr[0];
        for(int i : arr){
            if(min > i){
                min = i;
            }
        }
        System.out.println(min);
    }
}

컬렉션

 

  • 컬렉션은 다수의 참조형 데이터를 저장하며 배열보다 쉽고 효과적이다. 처음의 길이를 지정하지 않아도 만들 수 있다.
  • 컬렉션의 기능 : 크기 자동 조정/ 추가/ 수정/ 삭제/ 반복/ 순회/ 필터/ 포함 확인 등...
  • 종류 : List, Set , Queue , Map 등이 있다.

    1. List(동적 배열) : 순서가 있는 데이터의 집합 (데이터 중복 허용) - Array(정적 배열)과 비슷
더보기
import java.util.ArrayList;
import java.util.LinkedList;

public class col1 {
    public static void main(String[] args) {
        // 1. ArrayList : 생성 시점에 작은 연속된 공감을 요청해서 참조형 변수를 담는다. 값이 추가될 때 더 큰 공간이 필요하면 더 큰 공간을 받아서 저장한다.
        ArrayList<Integer> intList = new ArrayList<Integer>(); // 선언 및 생성
        intList.add(11); // intList에 11을 추가
        intList.add(22); // intList에 22을 추가
        intList.add(33); // intList에 33을 추가

        System.out.println(intList.get(0)); // 11 출력 메서드(명령어)
        System.out.println(intList.get(1)); // 22
        System.out.println(intList.get(2)); // 33

        intList.set(1, 5); // 1번째 값을 5로 수정한다
        System.out.println(intList.get(1)); // 5

        intList.remove(0); // 0번째 값을 삭제한다
        System.out.println(intList.get(0)); // 0번째 값이 사라지고 1번째 값이 0번째 값이 되어 5
        System.out.println(intList.toString());  // [5, 33]
        intList.clear(); // 전체 삭제
        System.out.println(intList.toString());  // []

        // 2. LinkedList : 남는 메모리 공간 여기 저기에 나누어서 실제 값을 담는다. 즉 실제 값이 있는 주소값으로 목록을 구성하고 저장한다. 조회 속도가 느린 반면, 추가나 삭제할 때 빠르다.
        LinkedList<Integer> linkedList = new LinkedList<Integer>();

        linkedList.add(11); // 추가
        linkedList.add(22);
        linkedList.add(33);

        System.out.println(linkedList.get(0)); // 11
        System.out.println(linkedList.get(1)); // 22
        System.out.println(linkedList.get(2)); // 33
        System.out.println(linkedList.toString()); // [11, 22, 33]

        linkedList.add(2, 9); // 2번 쨰에 9를 추가
        System.out.println(linkedList.toString()); // [11, 22, 9, 33]

        linkedList.set(1, 5); // 1번째 값을 5로 수정
        System.out.println(linkedList.toString()); // [11, 5, 9, 33]

        linkedList.remove(1); // 1번째 값을 삭제
        System.out.println(linkedList.toString()); // [11, 9, 33]

        linkedList.clear();
        System.out.println(linkedList.toString()); // []
    }
}
  • ArrayList는 배열(Array)처럼 일렬로 데이터를 저장하고 조회하여 순번 값(인덱스)로 값을 하나씩 조회
    - 선언 : `ArrayList<Integer> intList` 형태로 선언합니다.
    - 생성 : `new ArrayList<Integer>();` 형태로 생성합니다.
    - 초기화 : 사이즈를 지정하는 것이 없기 때문에 초기화가 필요 없습니다.
    - 값 추가 : `intList.add({추가할 값})` 형태로 값을 추가합니다.
    - 값 수정 : `intList.set({수정할 순번}, {수정할 값})` 형태로 값을 수정합니다.
    - 값 삭제 : `intList.remove({삭제할 순번})` 형태로 값을 삭제합니다.
    - 전체 출력 : `intList.toString()` 형태로 전체 값을 대괄호`[]`로 묶어서 출력합니다.
    - 전체 제거 : `intList.clear()` 형태로 전체 값을 삭제합니다.

  • LinkedList는 메모리에 남는 공간을 요청해서 여기저기 나누어서 실제 값을 담아 놓고, 실제 값이 있는 주소값으로 목록을 구성하고 저장
    - 선언 : `LinkedList<Integer> linkedList` 형태로 선언합니다.
    - 생성 : `new LinkedList<Integer>();` 형태로 생성합니다.
    - 초기화 : 사이즈를 지정하는 것이 없기 때문에 초기화가 필요 없습니다.
    - 값 추가 : `linkedList.add({추가할 값})` 형태로 값을 추가합니다.
    - 값 중간에 추가 : `linkedList.add({추가할 순번}, {추가할 값})` 형태로 값을 중간에 추가합니다.
    - 값 수정 : `linkedList.set({수정할 순번}, {수정할 값})` 형태로 값을 수정합니다.
    - 값 삭제 : `linkedList.remove({삭제할 순번})` 형태로 값을 삭제합니다.
    - 전체 출력 : `linkedList.toString()` 형태로 전체 값을 대괄호`[]`로 묶어서 출력합니다.
    - 전체 제거 : `linkedList.clear()` 형태로 전체 값을 삭제합니다.

  • Stack

 

 

 

2. Queue : 한쪽에서 데이터를 넣고 반대쪽에서 데이터를 뺄 수 있는 집합 
     * First In First Out(Fifo구조) : 먼저 들어간 순서대로 값을 조회할 수 있다.

더보기
import java.util.LinkedList;
import java.util.Queue;

public class col4 {
    public static void main(String[] args) {
        // Queue : 생성자가 없는 인터페이스
        Queue<Integer> intQueue = new LinkedList<Integer>(); // 생성 및 선언
        intQueue.add(8);
        intQueue.add(4);
        intQueue.add(9);

        while(!intQueue.isEmpty()){
            System.out.println(intQueue.poll()); // 8\4\9 처음 넣은 값이 먼저 출력됨
        }
        intQueue.add(8);
        intQueue.add(4);
        intQueue.add(9);
        System.out.println(intQueue.peek()); // 8
        System.out.println(intQueue.size()); // 3
    }
}

- 선언 : `Queue<Integer> intQueue` 형태로 선언합니다.
- 생성 : `new LinkedList<Integer>();` 형태로 생성합니다.
- 추가 : `intQueue.add({추가할 값})` 형태로 값을 맨 위에 추가합니다.
- 조회 : `intQueue.peek()` 형태로 맨 아래 값을 조회합니다.
- 꺼내기 : `intQueue.poll()` 형태로 맨 아래 값을 꺼냅니다. (꺼내고 나면 삭제됨)




3. Stack : 수직으로 데이터를 쌓아놓고 뺀다. First In Last Out 구조. 

더보기
// Stack 
// (사용하기 위해선 import java.util.Stack; 를 추가해야합니다.)
import java.util.Stack;

public class Main {

	public static void main(String[] args) {
		Stack<Integer> intStack = new Stack<Integer>(); // 선언 및 생성
		
		intStack.push(1);
		intStack.push(2);
		intStack.push(3);

		while (!intStack.isEmpty()) { // 다 지워질때까지 출력
		    System.out.println(intStack.pop()); // 3,2,1 출력
		}

		// 다시 추가
		intStack.push(1);
		intStack.push(2);
		intStack.push(3);
		
		// peek()
		System.out.println(intStack.peek()); // 3 출력
		System.out.println(intStack.size()); // 3 출력 (peek() 할때 삭제 안됬음)
		
		// pop()
		System.out.println(intStack.pop()); // 3 출력
		System.out.println(intStack.size()); // 2 출력 (pop() 할때 삭제 됬음)		
		
		System.out.println(intStack.pop()); // 2 출력
		System.out.println(intStack.size()); // 1 출력 (pop() 할때 삭제 됬음)		

		while (!intStack.isEmpty()) { // 다 지워질때까지 출력
		    System.out.println(intStack.pop()); // 1 출력 (마지막 남은거 하나)
		}
	}
}

- 선언 : `Stack<Integer> intStack` 형태로 선언합니다.
- 생성 : `new Stack<Integer>();` 형태로 생성합니다.
- 추가 : `intStack.push({추가할 값})` 형태로 값을 추가합니다.
- 조회 : `intStack.peek()` 형태로 맨 위값을 조회합니다.
- 꺼내기 : `intStack.pop()` 형태로 맨 위값을 꺼냅니다. (꺼내고 나면 삭제됨)

 

 

4. Set : 순서가 없는 데이터의 집합 (데이터 중복 허용 안함) - 순서 없고 중복 없는 배열,
             즉 순서가 보장되지 않는 대신 중복을 허용하지 않도록 하는 프로그램에서 사용할 수 있는 자료구조.

더보기
import java.util.HashSet;
import java.util.Set;

public class col5 {
    public static void main(String[] args) {
        // Set은 집합. 순서 없고 중복 없음. 생성자가 없는 껍데기라서 바로 생성할 수 없음.
        Set<Integer> intSet = new HashSet<Integer>(); // 선언 및 생성

        intSet.add(3);
        intSet.add(6);
        intSet.add(9);
        intSet.add(2);
        intSet.add(5);
        intSet.add(3);
        intSet.add(6);
        intSet.add(9);

        for(Integer value: intSet){
            System.out.println(value);
        }
        System.out.println(intSet.contains(5)); // intSet은 5를 포함하고 있니? true
        System.out.println(intSet.contains(7)); // intSet은 7을 포함하고 있니? false
    }
}
  • - 특징
        - 순서가 보장되지 않는 대신 중복을 허용하지 않도록 유지할 수 있습니다.
        - `Set`은 그냥 `Set`으로 쓸 수도 있지만 `HashSet`, `TreeSet` 등으로 응용하여 사용할 수 있습니다.
        - `Set`는 생성자가 없는 껍데기라서 바로 생성할 수는 없습니다. (껍데기 = 인터페이스)
        - 생성자가 존재하는 클래스인 `HashSet`를 사용하여 `Set`를 생성해서 받을 수 있습니다.
    - 기능
        - 선언 : `Set<Integer> intSet` 형태로 선언합니다.
        - 생성 : `new HashSet<Integer>();` 형태로 생성합니다.
        - 추가 : `intSet.add({추가할 값})` 형태로 값을 맨 위에 추가합니다.
        - 삭제 : `intSet.remove({삭제할 값})` 형태로 삭제할 값을 직접 지정합니다.
        - 포함 확인 : `intSet.contains({포함 확인 할 값})` 형태로 해당 값이 포함되어있는지 boolean 값으로 응답받습니다.
  • HashSet : 가장 빠르며 순서를 전혀 예측할 수 없음
  • TreeSet : 정렬된 순서대로 보관하며 정렬 방법을 지정할 수 있음
  • LinkedHashSet : 추가된 순서, 또는 가장 최근에 접근한 순서대로 접근 가능

즉, 보통 HashSet 을 쓰는데 순서 보장이 필요하면 LinkedHashSet 을 주로 사용합니다.





5. Map : 순서가 없는 (Key, Value) 쌍으로 이루어진 데이터의 집합 (Key값 중복 허용 안함, Key로 Value를 찾음)

 

더보기
import java.util.HashMap;
import java.util.Map;

public class col6 {
    public static void main(String[] args) {
        // map : (Key, Value) 쌍으로 이루어진다는 것이 중요. Key값은 unique(중복 x)
        // HashMap , TreeMap 으로 응용해서 사용.
        Map<String, Integer> intMap = new HashMap<>(); // 선언 및 생성

        intMap.put("일", 1); // (Key, Value) 추가
        intMap.put("이", 2);
        intMap.put("삼", 3);
        intMap.put("삼", 4); // 중복 Key
        intMap.put("삼", 5); // 중복 Key

        for (String key : intMap.keySet()) {     // 값 출력. .keySet()을 통해 value를 빼서 key만 출력
            System.out.println(key); // 이\일\삼
        }

        System.out.println("-----------");
        for (Integer value : intMap.values()) {     // 값 출력. .values()을 통해 key를 빼서 value만 출력
            System.out.println(value); // 2\1\5 -> 마지막 중복key의 마지막 value로 덮어씌워져 저장됨
        }
        System.out.println(intMap.get("삼")); // key를 가지고 value 찾기. key값이 문자열 `삼`인 value를 출력
    }
}

- 선언 : `Map<String, Integer> intMap` 형태로 Key타입과 Value타입을 지정해서 선언합니다.
- 생성 : `new` `HashMap<>();` 형태로 생성합니다.
- 추가 : `intMap.put({추가할 Key값},{추가할 Value값})` 형태로 Key에 Value값을 추가합니다.
- 조회 : `intMap.get({조회할 Key값})` 형태로 Key에 있는 Value값을 조회합니다.
- 전체 key 조회 : `intMap.keySet()` 형태로 전체 key 값들을 조회합니다.
- 전체 value 조회 : `intMap.values()` 형태로 전체 value 값들을 조회합니다.
- 삭제 : `intMap.remove({삭제할 Key값})` 형태로 Key에 있는 Value값을 삭제합니다.

 

  • - 특징
        - key-value 형태로 데이터를 저장하기 때문에 기존에 순번으로만 조회하던 방식에서, key 값을 기준으로 vlaue를 조회할 수 있습니다.
        - key 값 단위로 중복을 허용하지 않는 기능을 가지고 있습니다.
        - `Map` 은 그냥 `Map`으로 쓸 수도 있지만 `HashMap`, `TreeMap`등으로 응용하여 사용할 수 있습니다.
        - `Map`으로 쓸 수도 있지만 `HashSet`, `TreeSet` 등으로 응용하여 사용할 수 있습니다.

  • HashMap : 중복을 허용하지 않고 순서를 보장하지 않음, 키와 값으로 null이 허용
  • TreeMap : key 값을 기준으로 정렬을 할 수 있습니다. 다만, 저장 시 정렬(오름차순)을 하기 때문에 저장시간이 다소 오래 걸림