이 포스팅은 고돈호 님의 이것이 안드로이드다 with 코틀린(한빛미디어)을 기반으로 작성되었습니다.

1.1 배열

배열이란 여러 개의 값을 담을 수 있는 자료형이다. 선언하는 형태는 다음과 같다.

var 변수 = Array(개수)

배열 객체는 자료형에 따라 Int, Double, Char 등의 자료형을 Array뒤에 붙여서 만든다. 각각 다음과 같다.

var IntegerArray = IntArray(5)
var LongArray = LongArray(5)
var CharArray = CharArray(5)
var FloatArray = FloatArray(5)
var DoubleArray = DoubleArray(5)

위의 코드에서 5는 각각 자료형에 따른 공간을 5개 할당하라는 의미이며 이를 표현하면 다음과 같다.

[0] [1] [2] [3] [4]

즉 다른 언어와 마찬가지로 0번째 인덱스부터 시작하며 n-1까지의 공간을 할당하는 것을 알 수 있다.


1.2 문자 배열에 빈 공간 할당하기

String은 기본 타입이 아니므로 배열에서 String을 자료형으로 사용하기 위해서는 다음과 같이 사용해야 한다.

var stringArray = Array(5, {item->""})

1.3 값으로 배열 공간 할당하기

다음처럼 arrayOf 함수를 사용하여 String을 직접 할당할 수도 있다.

var dayArray = arrayOf("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT")

1.4 배열에 값 입력하기

1. 다른 언어와 유사한 방법

배열명[인덱스] = 값

2. set 함수를 사용한 방법

배열명. set(인덱스, 값)

1.5 배열에 있는 값 꺼내기

배열에 값을 입력하는 것 과 마찬가지로 인덱스로 접근하는 법과 함수를 이용한 방법이 있다.

배열명[인덱스]
배열명.get(인덱스)

ex) 배열 intArray의 다섯 번째 값을 fifthValue에 저장 

var fifthValue = intArray[4] // 인덱스의 시작이 0 이므로
var fifthValue2 = intArray.get(4) // 인덱스의 시작이 0 이므로

1.6 전체 예시 코드

package kr.co.kibeom.array

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 1. 기본 타입 배열 선언
        var IntegerArray = IntArray(5)
        var LongArray = LongArray(5)
        var CharArray = CharArray(5)
        var FloatArray = FloatArray(5)
        var DoubleArray = DoubleArray(5)

        // arrayOf 함수를 사용하여 선언과 동시에 값 입력
        var intArray = intArrayOf(1, 2, 3, 4, 5, 6)

        // 2. String형 배열 선언
        var stringArray = Array(10, {item->""})
        // arrayOf 함수를 사용하여 값의 직접 입력을 통한 배열 생성
        var dayArray = arrayOf("SUN", "MON", "TUE", "WED", "THU", "SAT")

        // 3. 앞에서 선언한 배열 IntegerArray 변수에 값 넣기
        // 인덱스를 통해 사용하는 방법
        IntegerArray[0] = 10
        IntegerArray[1] = 20
        IntegerArray[2] = 30
        // set 함수를 사용하는 방법
        IntegerArray.set(3, 40)
        IntegerArray.set(4, 50)

        // 4. 값 변경해보기
        intArray[2] = 30
        intArray.set(3, 40)

        // 5. 배열 값 사용해보기
        var thirdValue = intArray[2] // 인덱스의 시작이 0 이므로
        Log.d("Array", "세번째 intArray의 값은 ${thirdValue} 입니다.")
        var fourthValue = intArray.get(3) // 인덱스의 시작이 0 이므로
        Log.d("Array", "네번째 intArray의 값은 ${fourthValue} 입니다.")

        // 6. 변수에 담지 않고 바로 출력
        Log.d("Array", "첫번째 intArray의 값은 ${intArray[0]} 입니다.")
        Log.d("Array", "두번째 intArray의 값은 ${intArray[1]} 입니다.")
    }
}

2.1 컬렉션

여러 값을 넣을 수 있는 자료형에는 배열과 컬렉션이 존재한다. 이 컬렉션은 동적 배열이라고 불리며 컬렉션의 종류에는 리스트, 맵, 셋이 있다. 각각은 다음과 같다.

 

리스트

저장되는 데이터에 인덱스를 부여한 컬력션이며 중복된 값을 입력할 수 있다. 코틀린에서 동적으로 리스트를 사용하기 위해서는 mutable이라는 접두사를 붙인다.  사용법은 다음과 같다.

 

리스트 생성하기: mutableListOf

위처럼 mutableListOf를 사용하면 동적으로 배열 리스트가 생성된다.

var list = mutableListOf("MON", "TUE", "WED")

리스트에 값 추가하기: add

mutableList.add("THU")

add의 결과는 다음과 같다.

MON TUE WED THU

리스트에서 입력된 값 꺼내기 get 함수

var value = mutable.get(1)

리스트 값 수정하기 set 함수

mutableList.set(1, "값 수정")

리스트에 입력된 값 제거하기 removeAt

mutableList.removeAt(1)

빈 리스트 사용하기

빈 리스트를 사용하는 방법은 다음과 같다.

var 변수명 = mutableListOf<자료형>()
var stringList = mutableListOf<String>()

컬렉션 개수 가져오기: size

size를 통해 컬렉션의 개수를 가져올 수 있다.

mutableList.size

3.1 셋

셋은 우리가 흔히 하는 집합의 구조이며 그에 따라 집합을 허용하지 않는 리스트입니다. 인덱스로 조회할 수 없으며 get 함수를 지원하지 않는다는 특징이 있습니다. 다음과 같이 사용할 수 있습니다.

var set = mutableSetOf<자료형>()

빈 셋으로 초기화하고 값 입력하기

set = mutableSetOf<String>()
set.add("MON")
set.add("TUE")
set.add("WED")
set.add("MON") // 이미 MON이 존재하므로 에러

셋 출력하기

셋은 앞서 말씀드렸듯 인덱스로 조회하는 함수가 없기 때문에 특정 위치의 값을 직접 사용할 수 없다. 따라서 한 번에 출력하기 기능을 사용한다.

Log.d("SET", "Set 전체 출력 = ${set}")

셋 삭제하기

셋은 값이 중복되지 않는 특성 때문에 다른 리스트와 반대로 값을 직접적으로 삭제할 수 있다.

set.remove("MON")

전체 예시 코드

package kr.co.kibeom.collection

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 1. 셋 생성하고 값 추가하기
        var set = mutableSetOf<String>()
        set.add("MON")
        set.add("TUE")
        set.add("WED")
        set.add("MON") // 중복된 값 add 불가

        // 2. 전체 데이터 룰력
        Log.d("Collection", "Set 전체 출력 = ${set}")

        // 3. 특정 값 삭제하기
        set.remove("MON")
        Log.d("Collection", "Set 전체 출력 = ${set}")
    }
}

실행결과는 다음과 같다.


4.1 맵

맵은 다른 언어에서도 사용하는 자료구조 이므로 쉽게 알 수 있습니다. 맵은 키와 값으로 이루어져 있으며 해당하는 키에 값이 정해지는 구조입니다. 키, 값을 모두 String으로 정하면 다음과 같습니다.

var map = mutableMapOf<String, String>()

값 추가하기

var map = mutableMapOf<String, String>()
map.put("key1", "MON")
map.put("key2", "TUE")
map.put("key3", "WED")

맵 수정하기

map.put("key2", "FRI")

맵 삭제하기

map.remove("key2")

이 방식으로 그림으로 표현하면 다음과 같다.

key1/MON key2/TUE key3/WED

 

key1/MON key3/WED

전체 예시

 

 package kr.co.kibeom.collection

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 맵 생성하기
        var map = mutableMapOf<String, String>()
        // 값 추가
        map.put("key1", "MON")
        map.put("key2", "TUE")
        map.put("key3", "WED")

        // 값 사용
        var variable = map.get("key2")
        Log.d("Collection", "key2의 값은 ${variable} 입니다.")

        // 값 수정
        map.put("key2", "FRI")
        Log.d("Collection", "key2의 값은 ${map.get("key2")} 입니다.")

        // 값 삭제
        map.remove("key2")

        // 삭제한뒤 key2  없으므로 null 출력
        Log.d("Collection", "key2의 값은 ${map.get("key2")} 입니다.")

    }
}

 


5.1 이뮤터블 컬렉션

이뮤터블이란 뮤터블과 반대 즉 변경할 수 없는 리스트를 말한다. 값을 변경할 수 없기 때문에 add, set 등의 함수는 사용할 수 없고 최초로 입력된 값만 사용 가능하다. 즉 수정, 추가, 삭제가 불가능하다.

var list = listOf("1", "2")

'안드로이드 앱 개발' 카테고리의 다른 글

6. 함수  (0) 2021.12.20
5. 반복문  (0) 2021.12.19
3. 조건문  (0) 2021.12.12
2. 변수  (0) 2021.12.12
1. 코틀린의 기본적인 문법  (0) 2021.12.12
이 포스팅은 윤성우 님의 열혈 자료구조를 기반으로 합니다.

추상 자료형: Abstract Data Type

추상 자료형은 간단히 ADT라고 불리며 기능만 언급하며 기능의 과정은 포함하지 않는다.
이번에 배울 배열기반 리스트 자료구조의 ADT는 다음과 같다.

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
void ListInit(List * plist); 
// 초기화할 리스트의 주소 값을 인자로 전달한다.
// 리스트 생성 후 제일 먼저 호출되어야 하는 함수이다.
 
void LInsert(List * plist, LData data);
// 리스트에 데이터를 저장한다. 매개변수 data에 전달된 값을 저장한다.
 
int LFirst(List * plist, LData * pdata);
// 첫 번째 데이터가 pdata가 가리키는 메모리에 저장된다.
// 데이터의 참조를 위한 초기화가 진행된다.
// 참조 성공 시 TRUE(1), 실패 시 FALSE(0) 반환
 
int LNext(List * plist, LData * pdata);
// 참조된 데이터의 다음 데이터가 pdata가 가리키는 메모리에 저장된다.
// 순차적인 참조를 위해서 반복 호출이 가능하다.
// 참조를 새로 시작하려면 먼저 LFirst함수를 호출해야 한다.
// 참조 성공 시 TRUE(1), 실패 시 FALSE(0) 반환
 
LData LRemove(List * plist);
// LFirst 또는 LNext 함수의 마지막 반환 데이터를 삭제한다.
// 삭제된 데이터는 반환된다.
// 마지막 반환 데이터를 삭제하므로 연이은 반복 호출을 허용하지 않는다.
 
int LCount(List * plist);
// 리스트에 저장되어 있는 데이터의 수를 반환한다.
cs

배열을 이용한 리스트의 구현

리스트에는 크게 두가지의 종류가 있고 이는 다음과 같다.

  • 순차 리스트
  • 연결 리스트

리스트 자료구조는 데이터를 나란히 저장한다는 점과 중복 데이터의 저장을 허용한다는 큰 특성이 있다.
이제 위의 ADT를 기반으로 main함수를 작성하면 다음과 같다.

<ListMain.c>

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include "ArrayList.h"
 
int main(void)
{
    /*** ArrayList의 생성 및 초기화 ***/
    List list;
    int data;
    ListInit(&list);
 
    /*** 5개의 데이터 저장 ***/
    LInsert(&list, 11);  LInsert(&list, 11);
    LInsert(&list, 22);  LInsert(&list, 22);
    LInsert(&list, 33);
 
    /*** 저장된 데이터의 전체 출력 ***/
    printf("현재 데이터의 수: %d \n", LCount(&list));
 
    if (LFirst(&list, &data))    // 첫 번째 데이터 조회
    {
        printf("%d ", data);
 
        while (LNext(&list, &data))    // 두 번째 이후의 데이터 조회
            printf("%d ", data);
    }
    printf("\n\n");
 
    /*** 숫자 22을 탐색하여 모두 삭제 ***/
    if (LFirst(&list, &data))
    {
        if (data == 22)
            LRemove(&list);
 
        while (LNext(&list, &data))
        {
            if (data == 22)
                LRemove(&list);
        }
    }
 
    /*** 삭제 후 저장된 데이터 전체 출력 ***/
    printf("현재 데이터의 수: %d \n", LCount(&list));
 
    if (LFirst(&list, &data))
    {
        printf("%d ", data);
 
        while (LNext(&list, &data))
            printf("%d ", data);
    }
    printf("\n\n");
    return 0;
}
cs

위의 ListMain.c를 기반으로 하는 ArrayList.h, ArrayList.c도 다음과 같다.
<ArrayList.h>

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
31
#ifndef __ARRAY_LIST_H__
#define __ARRAY_LIST_H__
 
#define TRUE    1
#define FALSE   0
 
/*** ArrayList의 정의 ****/
#define LIST_LEN        100
typedef int LData;
 
typedef struct __ArrayList
{
        LData arr[LIST_LEN];
        int numOfData;
        int curPosition;
} ArrayList;
 
 
/*** ArrayList와 관련된 연산들 ****/
typedef ArrayList List;
 
void ListInit(List* plist);
void LInsert(List* plist, LData data);
 
int LFirst(List* plist, LData* pdata);
int LNext(List* plist, LData* pdata);
 
LData LRemove(List* plist);
int LCount(List* plist);
 
#endif
cs

<ArrayList.c>

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>
#include "ArrayList.h"
 
void ListInit(List* plist)
{
        (plist->numOfData) = 0;
        (plist->curPosition) = -1;
}
 
void LInsert(List* plist, LData data)
{
        if (plist->numOfData > LIST_LEN)
        {
                puts("저장이 불가능합니다.");
                return;
        }
 
        plist->arr[plist->numOfData] = data;
        (plist->numOfData)++;
}
 
int LFirst(List* plist, LData* pdata)
{
        if (plist->numOfData == 0)
                return FALSE;
 
        (plist->curPosition) = 0;
        *pdata = plist->arr[0];
        return TRUE;
}
 
int LNext(List* plist, LData* pdata)
{
        if (plist->curPosition >= (plist->numOfData) - 1)
                return FALSE;
 
        (plist->curPosition)++;
        *pdata = plist->arr[plist->curPosition];
        return TRUE;
}
 
LData LRemove(List* plist)
{
        int rpos = plist->curPosition;
        int num = plist->numOfData;
        int i;
        LData rdata = plist->arr[rpos];
 
        for (i = rpos; i < num - 1; i++)
                plist->arr[i] = plist->arr[i + 1];
 
        (plist->numOfData)--;
        (plist->curPosition)--;
        return rdata;
}
 
int LCount(List* plist)
{
        return plist->numOfData;
}
cs

위의 코드들을 자세히 살펴보면 배열 기반 리스트의 삭제가 복잡한 것을 알 수 있다.


먼저 배열 리스트의 데이터 삭제를 살펴보자
배열의 특성상, 그리고 리스트의 특성상 데이터가 나란히 존재해야 하므로 다음의 그림처럼 되는 것을 확인할 수 있다.

또한 가장 최근에 참조가 이루어진 데이터의 인덱스 정보를 담는 변수 curPosition 역시 참조하던 데이터가 삭제되면
앞의 데이터를 참조해야 한다. 이는 다음의 그림과 같다.


실제로 리스트에는 예시의 정수 이외에 다른 자료들도 들어간다. 이번에는 그렇다면 구조체 변수의 주소 값을 저장하여 보자 구조체는 다음과 같다.

1
2
3
4
typedef struct _point {
    int xpos; // x좌표
    int ypos; // y좌표
} Point;
cs

<Point.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __POINT_H__
#define __POINT_H__
 
typedef struct _point {
    int xpos;
    int ypos;
} Point;
 
// Point 변수의 xpos, ypos 값 설장
void SetPointPos(Point* ppos, int xpos, int ypos);
 
// Point 변수의 xpos, ypos정보 출력
void ShowPosition(Point* ppos);
 
// 두 Point 변수의 비교
int PointComp(Point* pos1, Point* pos2);
 
#endif
cs

<Point.c>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include "Point.h"
 
void SetPointPos(Point* ppos, int xpos, int ypos) {
    ppos->xpos = xpos;
    ppos->ypos = ypos;
}
 
void ShowPosition(Point* ppos) {
    printf("[%d, %d] \n", ppos->xpos, ppos->ypos);
}
 
int PointComp(Point* pos1, Point* pos2) {
    if (pos1->xpos == pos2->xpos && pos1->ypos == pos2->ypos)
        return 0;
    else if (pos1->xpos == pos2->xpos)
        return 1;
    else if (pos1->ypos == pos2->ypos)
        return 2;
    else
        return -1;
}
cs

이 후 ArrayList.h, ArrayList.c를 기반으로 위의 Point 구조체를 저장할 수 있도록 하면 이 과정에서
헤더 파일은 변경이 되어도 되지만 소스파일은 변경이 되면 안 된다.
헤더 파일에서 달라진 점은 typedef int LData; 에서 typedef Point * LData; 으로 변경되었다.
또한 ArrayList.h의 선언문에 #include "Point.h" 를 추가한다. 이제 main함수는 다음과 같다.
<PointListMain.c>

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <stdio.h>
#include <stdlib.h>
#include "ArrayList.h"
#include "Point.h"
 
int main(void) {
    List list;
    Point comPos;
    Point* ppos;
 
    ListInit(&list);
 
    // 4개의 데이터 저장
    ppos = (Point*)malloc(sizeof(Point));
    SetPointPos(ppos, 21);
    LInsert(&list, ppos);
 
    ppos = (Point*)malloc(sizeof(Point));
    SetPointPos(ppos, 22);
    LInsert(&list, ppos);
 
    ppos = (Point*)malloc(sizeof(Point));
    SetPointPos(ppos, 31);
    LInsert(&list, ppos);
 
    ppos = (Point*)malloc(sizeof(Point));
    SetPointPos(ppos, 32);
    LInsert(&list, ppos);
 
    // 저장된 데이터의 출력
    printf("현재 데이터의 수는 : %d \n", LCount(&list));
 
    if (LFirst(&list, &ppos)) {
        ShowPosition(ppos);
 
        while (LNext(&list, &ppos)) {
            ShowPosition(ppos);
        }
    }
    printf("\n");
 
    // xpos가 2인 모든 데이터 삭제
    comPos.xpos = 2;
    comPos.ypos = 0;
 
    if (LFirst(&list, &ppos)) {
        if (PointComp(ppos, &comPos) == 1) {
            ppos = LRemove(&list);
            free(ppos);
        }
 
        while (LNext(&list, &ppos)) {
            if (PointComp(ppos, &comPos) == 1) {
                ppos = LRemove(&list);
                free(ppos);
            }
        }
    }
 
    // 삭제 후 남은 데이터 전체 출력
    printf("현재 데이터의 수는 : %d \n", LCount(&list));
 
    if (LFirst(&list, &ppos)) {
        ShowPosition(ppos);
 
        while (LNext(&list, &ppos)) {
            ShowPosition(ppos);
        }
    }
    printf("\n");
 
    return 0;
}
cs

이제 정리해보자.

  • 배열 기반 리스트의 단점
    • 배열의 길이가 초기에 결정되어야 한다. 또한 변경이 불가능하다.
    • 삭제의 과정에서 데이터의 이동이 일어난다. (마지막 경우가 아닌 경우)
  • 배열 기반 리스트의 장점
    • 데이터의 참조가 쉽다. 인덱스를 기준으로 한 번에 참조가 가능하다.

'자료구조' 카테고리의 다른 글

5. 스택  (0) 2022.01.10
4. 연결 리스트  (0) 2021.07.19
2. 재귀 (Recursion)  (0) 2021.06.27
1. 자료구조와 알고리즘의 이해  (0) 2021.06.27

+ Recent posts