์ „์ฒด ๊ธ€ (145)

๋ฐ˜์‘ํ˜•
08
25

https://school.programmers.co.kr/learn/challenges?tab=algorithm_practice_kit

 

ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค

์ฝ”๋“œ ์ค‘์‹ฌ์˜ ๊ฐœ๋ฐœ์ž ์ฑ„์šฉ. ์Šคํƒ ๊ธฐ๋ฐ˜์˜ ํฌ์ง€์…˜ ๋งค์นญ. ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค์˜ ๊ฐœ๋ฐœ์ž ๋งž์ถคํ˜• ํ”„๋กœํ•„์„ ๋“ฑ๋กํ•˜๊ณ , ๋‚˜์™€ ๊ธฐ์ˆ  ๊ถํ•ฉ์ด ์ž˜ ๋งž๋Š” ๊ธฐ์—…๋“ค์„ ๋งค์นญ ๋ฐ›์œผ์„ธ์š”.

programmers.co.kr

ํ‘ธ๋Š” ๋™์•ˆ ์‚ฌ๊ณ ๊ณผ์ • ๋ฐ ๊ฐœ์„ ์ 

์ฒ˜์Œ์— ์ง„์งœ ๊ฐ์ด ์•ˆ์žกํ˜”๋‹ค. ์ ‘๋‘์–ด ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์„œ contains๋ฅผ ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™์€๋ฐ, ์ด๊ฒŒ Hash๊ฐ€ ๋งž๋‚˜?? ์ด๋Ÿฐ ์ƒ๊ฐ์ด ๋จธ๋ฆฌ ์†์— ๋งด๋Œ์•˜๋‹ค.

import java.util.*;

class Solution {
    public boolean solution(String[] phone_book) {
        boolean answer = true;
        TreeSet<String> ts = new TreeSet();
        for(String phone: phone_book){
            ts.add(phone);
        }
        
        // ๊ฐ ์ „ํ™”๋ฒˆํ˜ธ์˜ ๋ชจ๋“  ์ ‘๋‘์–ด๋ฅผ ํ™•์ธ
        for (String phone : phone_book) {
            for (int i = 1; i < phone.length(); i++) { // ๊ธธ์ด๋Š” 1์ด์ƒ
                String prefix = phone.substring(0, i);
                if (ts.contains(prefix)) {
                    answer = false;
                }
            }
        }
        
        return answer;
    }
}

2์ค‘ ํฌ๋ฌธ์€ ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ๊ทธ๋ž˜๋„ contains๋กœ ํ•ด์„œ ์กฐํšŒ ๋•Œ O(1)์„ ํ•ด์ค˜์„œ ์ข€ ๋น ๋ฅธ ๊ฒŒ ์•„๋‹๊นŒ

์ค‘๋ณต๋œ ๋ฒˆํ˜ธ๊ฐ€ ์—†์œผ๋‹ˆ๊นŒ HashSet์— ๋„ฃ์–ด์„œ ์กฐํšŒ ์„ฑ๋Šฅ์„ ์˜ฌ๋ ค์ฃผ๊ณ , ๊ฐ ๋ฒˆํ˜ธ๋ฅผ ๋Œ๋ฉด์„œ substring์„ ์ž˜๋ผ ์ด๊ฒŒ HashSet์— ์กด์žฌํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์ฐพ๋Š”๋‹ค. ์ด๋Ÿฌ๋ฉด ์กด์žฌํ•  ๊ฒฝ์šฐ ์ ‘๋‘์–ด๊ฐ€ ์žˆ๋Š” Set์ด ๋œ๋‹ค.


# ์ถ”๊ฐ€ ๊ณต๋ถ€

substring์€ ` [ startIndex, endIndex ) ` ๋‹ค. ์ด์ƒ, ๋ฏธ๋งŒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์˜ํ•ด์•ผ๋œ๋‹ค.

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
08
25

https://school.programmers.co.kr/learn/challenges?tab=algorithm_practice_kit

 

ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค

์ฝ”๋“œ ์ค‘์‹ฌ์˜ ๊ฐœ๋ฐœ์ž ์ฑ„์šฉ. ์Šคํƒ ๊ธฐ๋ฐ˜์˜ ํฌ์ง€์…˜ ๋งค์นญ. ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค์˜ ๊ฐœ๋ฐœ์ž ๋งž์ถคํ˜• ํ”„๋กœํ•„์„ ๋“ฑ๋กํ•˜๊ณ , ๋‚˜์™€ ๊ธฐ์ˆ  ๊ถํ•ฉ์ด ์ž˜ ๋งž๋Š” ๊ธฐ์—…๋“ค์„ ๋งค์นญ ๋ฐ›์œผ์„ธ์š”.

programmers.co.kr

ํ‘ธ๋Š” ๋™์•ˆ ์‚ฌ๊ณ ๊ณผ์ • ๋ฐ ๊ฐœ์„ ์ 

์ฒ˜์Œ์—๋Š” ์ง„์งœ ๋‘ ๋ฐฐ์—ด์„ ์ •๋ ฌํ•ด์„œ ๋น„๊ตํ•˜๋ฉด ๋˜๊ฒ ์ง€๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ๊ทผ๋ฐ ์ž…๋ ฅ์ด 10๋งŒ์ด๋ผ ์•„๋งˆ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ์œผ๋ฉด ํšจ์œจ์„ฑ ๊ฒ€์‚ฌ์—์„œ ๊ฑธ๋ฆฌ์ง€์•Š์•˜์„๊นŒ..
๋™๋ช…์ด์ธ์ด ์žˆ๋Š” ์ƒํƒœ๋ผ Set์€ ๋ชป์“ฐ๊ณ , ๊ทธ๋ž˜์„œ Map์„ ์ผ๋‹ค.

import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        String answer = "";
        TreeMap<String, Integer> tm = new TreeMap();
        for(String name: participant){
            tm.put(name, tm.getOrDefault(name, 0)+1);
        } // ํŠธ๋ฆฌ๋งต์— ์ €์žฅํ•ด๋‘๊ธฐ -> ๊ตณ์ด TreeMap์ผ ํ•„์š”์—†์Œ
        // ์ด์ œ completion์„ ๋Œ๋ฉด์„œ ์ฒ˜๋ฆฌํ•˜์ž
        for(String name: completion){
            tm.put(name, tm.get(name)-1);
        } // 0์ด๋ฉด ์™„์ฃผํ–ˆ๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋  ๊ฒƒ
        
        Iterator<String> keys = tm.keySet().iterator();
        while(keys.hasNext()){
            String key = keys.next();
            if(tm.get(key) != 0){
                answer = key;
                break;
            }
        }
        
        return answer;
    }
}

TreeMap์„ ์“ฐ๊ณ , key๋ฅผ ์ด๋ฆ„, value๋ฅผ ์ธ์›์ˆ˜๋กœ ์„ค์ •ํ–ˆ๋‹ค. ์ •๋ ฌ์ด ํ•„์š”์—†๊ธฐ๋•Œ๋ฌธ์— HashMap์„ ์“ฐ๋Š” ๊ฒŒ ํšจ์œจ์ ์ด์ง€๋งŒ ๊ทธ๋ƒฅ ์ˆ™๋ จ์„ ์œ„ํ•ด TreeMap์„ ์„ ํƒํ–ˆ๋‹ค.

์ดˆ๊ธฐํ™”ํ•  ๋•Œ getOrDefault๋ฅผ ์“ด ์ด์œ ๋Š” value๊ฐ€ ์—†์„ ๋•Œ null ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฑธ ๋ง‰๊ธฐ ์œ„ํ•จ์ด๋‹ค.

๊ทธ๋ ‡๊ฒŒ ๋‹ค ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ๋‚˜์„œ, ์ธ์›์ˆ˜๊ฐ€ 1์ธ key๋ฅผ ์ฐพ์•„์•ผ ํ•˜๋Š” ๊ฒŒ ๋ฌธ์ œ์˜€๋‹ค. ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ keySet์„ ์‚ฌ์šฉํ•ด๋„ ๋˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ ์กฐํšŒํ•ด์•ผ๋˜๋Š” ๊ฐ’์ด ์—„์ฒญ ๋งŽ๋‹ค๋ฉด entrySet์— ๋น„ํ•ด ์—„์ฒญ ๋Š๋ฆฌ๋‹ค๊ณ  ํ–ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  entrySet์„ ์“ฐ๋ฉด iterator๋ฅผ ์•ˆ์จ๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์ฒ˜๋Ÿผ ํ•ด๋ณด๋Š” ๊ฑด ์–ด๋–จ๊นŒ

import java.util.*;

class Solution {
    public String solution(String[] participant, String[] completion) {
        String answer = "";
        TreeMap<String, Integer> tm = new TreeMap();
        for(String name: participant){
            tm.put(name, tm.getOrDefault(name, 0)+1);
        } // ํŠธ๋ฆฌ๋งต์— ์ €์žฅํ•ด๋‘๊ธฐ -> ๊ตณ์ด TreeMap์ผ ํ•„์š”์—†์Œ
        // ์ด์ œ completion์„ ๋Œ๋ฉด์„œ ์ฒ˜๋ฆฌํ•˜์ž
        for(String name: completion){
            tm.put(name, tm.get(name)-1);
        } // 0์ด๋ฉด ์™„์ฃผํ–ˆ๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋  ๊ฒƒ
        
        for(Map.Entry<String, Integer> item : tm.entrySet()){
            if(item.getValue() != 0){
                answer = item.getKey();
                break;
            }
        }
        
        return answer;
    }
}

# ์ถ”๊ฐ€ ๊ณต๋ถ€

TreeMap ์•Œ์•„๋ณด๊ธฐ

// entrySet()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ํ•ญ๋ชฉ ์ˆœํšŒ
for (Map.Entry<String, Integer> entry : tm.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + ": " + value);
}

// ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ํ•ญ๋ชฉ ์ฐพ๊ธฐ
for (Map.Entry<String, Integer> entry : tm.entrySet()) {
    if (entry.getValue() > 2) {
        System.out.println(entry.getKey());
    }
}

// ๋งต ์ˆ˜์ •ํ•˜๊ธฐ
for (Map.Entry<String, Integer> entry : tm.entrySet()) {
    entry.setValue(entry.getValue() * 2);
}

Tree ์‹œ๋ฆฌ์ฆˆ ๋‹ต๊ฒŒ ๋ ˆ๋“œ๋ธ”๋ž™ํŠธ๋ฆฌ๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๊ณ , SortedMap์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋‹ค.  ํ‚ค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ๋œ ์ƒํƒœ(๊ธฐ๋ณธ์€ ์˜ค๋ฆ„์ฐจ์ˆœ)์ด๋ฉฐ entrySet์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งต์˜ ๋ชจ๋“  key-value ์Œ์„ `Set<Map.Entry<K,V>>` ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

set์ด add, remove์˜€๋‹ค๋ฉด map์€ put, get, remove(key๋กœ ์‚ญ์ œ, entrySet์—์„œ ์‚ญ์ œ, key-value๋กœ ์‚ญ์ œ)๊ฐ€ ์žˆ๋‹ค.

 

์‚ญ์ œ๋งŒ ์ข€ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

// 1. ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ ์‚ญ์ œ
tm.remove("Apple");

// 2. ํ‚ค์™€ ๊ฐ’์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•œ ์‚ญ์ œ
tm.remove("Banana", 2);

์‹ ๊ธฐํ•œ ๊ฑด removeIf๊ฐ€ ์žˆ๋Š”๋ฐ ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•œ๋‹ค. ๋žŒ๋‹ค์‹์„ ์“ฐ๋Š”๊ฑด๋ฐ ๊ทธ๋ƒฅ ์ด๋Ÿฐ๊ฒŒ ์žˆ๋‹ค ์ •๋„๋งŒ ๊ธฐ์–ตํ•˜๊ณ  ๋„˜์–ด๊ฐ€์•ผ ๋  ๊ฒƒ ๊ฐ™๋‹ค.

// ์ง์ˆ˜๋งŒ ์ œ๊ฑฐํ•˜๊ธฐ
tm.entrySet().removeIf(entry -> entry.getValue() % 2 == 0);

 

putIfAbsent๋„ ์žˆ์–ด์„œ ์ด๊ฑธ ํ™œ์šฉํ•˜๋ฉด value์— Collection๋„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋‹ค.

tm.putIfAbsent("A", 1);

๊ธฐ์กด์˜ put์ด ๋ฌด์กฐ๊ฑด ํ‚ค๋ฅผ ๋ฎ์–ด์ผ๋‹ค๋ฉด putIfAbsent๋Š” ํ‚ค๊ฐ€ ์—†์„ ๋•Œ๋งŒ ๋™์ž‘ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ Collection๊ฐ™์€ ๊ฑธ new๋กœ ๋„ฃ์–ด์ค„ ๋•Œ ์“ฐ๋ฉด ๋˜๊ฒ ๋‹ค.

 

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
08
25

https://school.programmers.co.kr/learn/challenges?tab=algorithm_practice_kit

 

ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค

์ฝ”๋“œ ์ค‘์‹ฌ์˜ ๊ฐœ๋ฐœ์ž ์ฑ„์šฉ. ์Šคํƒ ๊ธฐ๋ฐ˜์˜ ํฌ์ง€์…˜ ๋งค์นญ. ํ”„๋กœ๊ทธ๋ž˜๋จธ์Šค์˜ ๊ฐœ๋ฐœ์ž ๋งž์ถคํ˜• ํ”„๋กœํ•„์„ ๋“ฑ๋กํ•˜๊ณ , ๋‚˜์™€ ๊ธฐ์ˆ  ๊ถํ•ฉ์ด ์ž˜ ๋งž๋Š” ๊ธฐ์—…๋“ค์„ ๋งค์นญ ๋ฐ›์œผ์„ธ์š”.

programmers.co.kr

ํ‘ธ๋Š” ๋™์•ˆ ์‚ฌ๊ณ ๊ณผ์ • ๋ฐ ๊ฐœ์„ ์ 

import java.util.*;

class Solution {
    public int solution(int[] nums) {
        int answer = 0;
        TreeSet<Integer> ts = new TreeSet();
        for(int i: nums){
            ts.add(i); // ์ผ๋‹จ ์ด๋Ÿฌ๋ฉด ์ค‘๋ณต์ œ๊ฑฐ๊ฐ€ ๋œ๋‹ค
        }
        if(ts.size() >= nums.length/2){
            answer = nums.length/2;
        }else{
            answer = ts.size();
        }
        
        return answer;
    }
}

๋ถ„๋ฅ˜๋Š” Hash๋‹ค. TreeSet์œผ๋กœ ์ค‘๋ณต์ œ๊ฑฐํ•ด์„œ ์ตœ๋Œ€ ๊ฐ€์ง€์ˆ˜๋ฅผ ๋ฝ‘์•˜๋‹ค.

ํ•ด์‹œ๋กœ ๋ถ„๋ฅ˜๋œ ์ด์œ ๋Š” Set ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์ด์šฉํ•ด์„œ ํ‘ธ๋Š” ๊ฒƒ ๋•Œ๋ฌธ์ผ ๊ฒƒ์œผ๋กœ ์ถ”์ •๋œ๋‹ค..


# ์ถ”๊ฐ€ ๊ณต๋ถ€

TreeSet ์ž๋ฃŒ๊ตฌ์กฐ

import java.util.*;

TreeSet<Integer> ts = new TreeSet<Integer>();
TreeSet<Integer> tsInit = new TreeSet<>(Arrays.asList(1,2,3,3,3));

ts.add(1);
ts.add(2);
ts.add(2);
ts.add(3);
ts.remove(2);
ts.size(); // ํฌ๊ธฐ
ts.clear(); // ์ „์ฒด ๋‚ ๋ฆฌ๊ธฐ

Treeset์€ ๊ธฐ๋ณธ์œผ๋กœ ๋ ˆ๋“œ๋ธ”๋ž™ํŠธ๋ฆฌ๊ฐ€ ์ ์šฉ๋˜์–ด์žˆ๋Š” Set ์ž๋ฃŒ๊ตฌ์กฐ๋‹ค. 

https://ko.wikipedia.org/wiki/%EB%A0%88%EB%93%9C-%EB%B8%94%EB%9E%99_%ED%8A%B8%EB%A6%AC

 

๋ ˆ๋“œ-๋ธ”๋ž™ ํŠธ๋ฆฌ - ์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „

์œ„ํ‚ค๋ฐฑ๊ณผ, ์šฐ๋ฆฌ ๋ชจ๋‘์˜ ๋ฐฑ๊ณผ์‚ฌ์ „. ๋ ˆ๋“œ-๋ธ”๋ž™ ํŠธ๋ฆฌ(red-black tree)๋Š” ์ž๊ฐ€ ๊ท ํ˜• ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ(self-balancing binary search tree)๋กœ์„œ, ๋Œ€ํ‘œ์ ์œผ๋กœ๋Š” ์—ฐ๊ด€ ๋ฐฐ์—ด ๋“ฑ์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์“ฐ์ด๋Š” ์ž๋ฃŒ๊ตฌ์กฐ๋‹ค. 1978๋…„

ko.wikipedia.org

๋ ˆ๋“œ๋ธ”๋ž™ํŠธ๋ฆฌ๋Š” ๋ถ€๋ชจ๋…ธ๋“œ ๊ธฐ์ค€์œผ๋กœ ์ž‘์€๊ฒŒ ์™ผ์ชฝ์œผ๋กœ, ํฐ๊ฒŒ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ๊ฐ„๋‹ค. ๊ทธ๋ž˜์„œ ts.first()๋Š” ํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’, ts.last()๋Š” ํŠธ๋ฆฌ์—์„œ ๊ฐ€์žฅ ํฐ ๊ฐ’์„ ์ถœ๋ ฅํ•œ๋‹ค. ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ์ด ๊ธฐ๋ณธ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ new ๋กœ ๋งŒ๋“œ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ Collections.reverseOrder() ํ•ด์ฃผ๋ฉด ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์ด ๋˜๋Š” TreeSet์ด ์ƒ์„ฑ๋œ๋‹ค.

TreeSet<Integer> ts = new TreeSet(Comparator.reverseOrder());

Collection์ธ๋ฐ, Set๊ตฌ์กฐ๋ผ ์ˆœํšŒ๊ฐ€ ์ข€ ๋ถˆํŽธํ•˜๋‹ค. Iterator๋กœ ๊ฐ์‹ธ์„œ ์ˆœํšŒํ•ด์•ผ๋œ๋‹ค.

Iterator it = ts.iterator();
while(iter.hasNext()){
    System.out.print(it.next() + "\n");	
}

์ •๋ ฌ๋œ ์ˆœ์„œ๋Œ€๋กœ iterator๊ฐ€ ์ˆœํšŒํ•œ๋‹ค.

 

HashSet์„ ์“ฐ๋ฉด ์š”์†Œ๊ฐ€ ์ •๋ ฌ๋˜์ง€์•Š์•„์žˆ์ง€๋งŒ ํ•ด์‹œํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•ด์„œ O(1)์˜ ์†๋„๋ฅผ ์ž๋ž‘ํ•œ๋‹ค. ๊ทผ๋ฐ TreeSet์„ ์“ฐ๋ฉด ์ •๋ ฌ๋˜์–ด์žˆ์–ด์„œ ๋งŒ์•ฝ ๋ฒ”์œ„๊ฒ€์ƒ‰ ๊ฐ™์€ ๊ฒŒ ํ•„์š”ํ•˜๋ฉด ์ด๊ฑธ ์จ์•ผ๋œ๋‹ค. O(logN)์ด๊ฑฐ๋‚˜ amortized O(1)์ด๊ธฐ ๋•Œ๋ฌธ์— ์ž…๋ ฅ์ด ์ž…๋ ฅ์ด ์—„์ฒญ ์ปค์„œ ์กฐํšŒ์— ์‹œ๊ฐ„์ด ๋ง‰ ๊ฑธ๋ฆฌ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด TreeSet์„ ์จ๋„ ๋  ๊ฒƒ ๊ฐ™๋‹ค.

 

๋ฒ”์œ„๊ฒ€์ƒ‰์€ headSet, tailSet์„ ์“ด๋‹ค. ์ด๊ฒŒ ์‹ ์„ธ๊ณ„๋‹ค.

TreeSet<Integer> numbers = new TreeSet<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);

// headSet() ์˜ˆ์‹œ
System.out.println("headSet(4): " + numbers.headSet(4));
System.out.println("headSet(4, true): " + numbers.headSet(4, true));

// tailSet() ์˜ˆ์‹œ
System.out.println("tailSet(4): " + numbers.tailSet(4));
System.out.println("tailSet(4, false): " + numbers.tailSet(4, false));

headSet์€ ์ฃผ์–ด์ง„ ์š”์†Œ๋ณด๋‹ค ์ž‘์€ ๋ฒ”์œ„์˜ ๋ถ€๋ถ„์ง‘ํ•ฉ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์‹ถ์œผ๋ฉด true๋ฅผ ๊ฐ™์ด ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

tailSet์€ ์ฃผ์–ด์ง„ ์š”์†Œ๋ณด๊ฐ€ ํฌ๊ฑฐ๋‚˜ ๊ฐ™์€ ๋ฒ”์œ„์˜ ๋ถ€๋ถ„์ง‘ํ•ฉ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์š”์†Œ๊ฐ€ ํฌํ•จ๋˜์–ด์žˆ๋Š”๋ฐ, false๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ์‹œ์ž‘ ์ ์ด ์ œ์™ธ๋œ๋‹ค.

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT
 
08
16

@Composable ๊ณผ Recomposition, ๊ทธ๋ฆฌ๊ณ  Modifier

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

์ปดํฌ์ €๋ธ” ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•จ์ˆ˜๋Š” UI๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๊ฐ€ ๋œ๋‹ค. ์ด ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•จ์ˆ˜๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋”ฐ๋กœ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ , ๋Ÿฐํƒ€์ž„๋•Œ ํ†ตํ•ฉํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ๊ฐ€ ๋”ฐ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ์ค‘์š”ํ•œ ์ ์€ UI๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, returnํ˜•์‹์ด ์•„๋‹Œ emit ํ˜•์‹์ด์–ด์„œ ๋ฐ˜ํ™˜๊ฐ’์ด ๋”ฐ๋กœ ์—†๋‹ค๋Š” ์ ์ด๋‹ค. 

UI๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ์ด๊ธฐ ๋•Œ๋ฌธ์— Recomposition์— ๋Œ€ํ•œ ๊ฐœ๋…๋„ ์—ฎ์—ฌ์žˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด Compose๋Š” ์ƒˆ ๋ฐ์ดํ„ฐ๋กœ Composableํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜์—ฌ ์—…๋ฐ์ดํŠธ๋œ UI๋ฅผ ๋งŒ๋“ ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ UI ์—…๋ฐ์ดํŠธํ•ด์„œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ด์ฃผ๋Š”๋ฐ, ๊ฐ์ฒด์˜ reference๋งŒ ๋น„๊ตํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ๋‚ด์šฉ๊นŒ์ง€ ๋น„๊ตํ•ด์„œ ๋‚ด์šฉ์ด ๊ฐ™์€๊ฒฝ์šฐ ๋”ฐ๋กœ ์žฌ๊ตฌ์„ฑ์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿผ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์–ด๋–ป๊ฒŒ ๊ฐ์ง€ํ• ๊นŒ? ๊ทธ๊ฑด ์ƒํƒœ ๊ฐ์ฒด์— ๋‹ฌ๋ ค์žˆ๋‹ค. MutableState, State๋กœ ๊ฐ์‹ธ์ง„ ์ƒํƒœ๊ฐ์ฒด๊ฐ€ ์žˆ๋Š”๋ฐ, ์–˜๋„ค๊ฐ€ UI recomposition์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ๊ฐ์ฒด๋“ค์ด๋‹ค.

 

์ด ๊ฐ์ฒด๋“ค์€ ๊ฐ’ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•  ๋•Œ Compose ์‹œ์Šคํ…œ์ด ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋˜ remeber๊ฐ€ ๋ถ™์€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด์ „ ์—ฐ์‚ฐ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์ปดํฌ์ง€์…˜์„ ๋ฐฉ์ง€ํ•˜๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค.

Column, Row, Box ์ปดํฌ์ €๋ธ”์€ ๋ชจ๋‘ ์ปจํ…Œ์ด๋„ˆ๋‹ค.

@Preview(showBackground = true)
@Composable
fun UserProfile() {
    Column {
        Text("์œ ์ €")
        Text("abcd@a.b")
        var likes by remember { mutableStateOf(0) }
        Text("Likes: $likes")
        Button(onClick = { likes++ }) {
            Text("Like")
        }
    }
}

@Preview๋Š” ์ •์  ํ™”๋ฉด์„ ๋ฏธ๋ฆฌ ๋ณด์—ฌ์ฃผ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ, ์‹ค ๊ธฐ๊ธฐ ํ™”๋ฉด์ฒ˜๋Ÿผ ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

@Preview(showBackground = true, widthDp = 320)

์ฝ”๋“œ๋กœ ๋Œ์•„์™€์„œ, ์žฌ๊ตฌ์„ฑ์ด ๋˜๋Š” ๋ถ€๋ถ„์€ `Text("Likes: $likes")` ์ด๋ถ€๋ถ„์ด๋‹ค. Text๋กœ Like๊ฐ€ ์ ํ˜€์žˆ๋Š” Button์„ ๋ˆ„๋ฅด๋ฉด likes๊ฐ’์ด ์ฆ๊ฐ€ํ•ด์„œ ์ด TextComposable์ด ์žฌ๊ตฌ์„ฑ๋œ๋‹ค. UserProfile Composable ์ „์ฒด๊ฐ€ ์žฌ๊ตฌ์„ฑ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋”ฐ๋กœ ์•ˆ๋งŒ๋“ค์—ˆ์ง€๋งŒ, like๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋Š”๋ฐ ์ด์ „๊ณผ ๊ฐ’์ด ๊ฐ™๋‹ค๋ฉด ์žฌ๊ตฌ์„ฑ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

`var likes by remember { mutableStateOf(0) }`

์ด ๋ถ€๋ถ„์ด ์ด๋ฒˆ Composable์˜ ํ•ต์‹ฌ์ด๋‹ค. by๋กœ ์œ„์ž„์‹œํ‚จ๊ฒƒ๋ถ€ํ„ฐ ๋ด์•ผ๋œ๋‹ค.

`val likes = remember { mutableStateOf(0) }`

์ด๊ฒŒ ์œ„์ž„์‹œํ‚ค๊ธฐ ์ „ ํ˜•ํƒœ๋‹ค.  MutableState๊ฐ’์„ remember๋กœ ๊ฐ์‹ธ์„œ likes ์— ํ• ๋‹นํ•œ ํ˜•ํƒœ๋กœ, ๊ฐ’์— ์ ‘๊ทผํ• ๋•Œ๋งˆ๋‹ค .value ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋œ๋‹ค. get, set์ด MutableState ๊ฐ์ฒด ์•ˆ์— ์žˆ๋Š”๋ฐ ๊ฑฐ๊ธฐ๊ฒŒ value ํ”„๋กœํผํ‹ฐ๋กœ ์ ‘๊ทผํ•œ๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. LiveData๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ดค๋˜ ๊ฒฝ์šฐ๋‹ค. remember๋กœ ๊ฐ์‹ธ๊ฒŒ ๋˜๋ฉด,  recomposition ์‚ฌ์ด์—์„œ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ดˆ๊ธฐํ™” ํ•  ๋•Œ๋งŒ ๋žŒ๋‹ค ๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ์žฌ๊ตฌ์„ฑ์ด ๋ฐœ์ƒํ•  ๋•Œ๋Š” ์ €์žฅ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ˜•ํƒœ๋‹ค. ๋”ฐ๋ผ์„œ ๋งค๋ฒˆ MutableState๊ฐ์ฒด๋ฅผ ๋ฐฐ์ •ํ•ด์„œ ์“ฐ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ remeber๋œ ๊ฑธ ๊บผ๋‚ด์„œ ์“ฐ๊ฒŒ ๋œ๋‹ค.

 

by๋กœ ์œ„์ž„ํ•˜๊ฒŒ ๋˜๋ฉด, get, set์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด value ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ๊ท€์ฐฎ์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๋ฐ”๋กœ likes๋กœ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.  `likes.value = likes.value+1` ์ด `likes++` ๋กœ ๋ฐ”๋€Œ์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์ฒด๊ฐ๋  ๊ฒƒ์ด๋‹ค.

# Modifier

`Modifier`๋Š” Compose์—์„œ ๊ฑฐ์˜ xml์—์„œ ํ• ์ˆ˜์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ๋“ค์„ ์ •์˜ํ•˜๋Š” ๊ฐ์ฒด์ด๋ฉฐ, ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€์•Š์œผ๋ฉด ๊ธฐ๋ณธ modifier๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค. ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ฒด์ด๋‹์„ ์“ด๋‹ค.

Modifier
    .padding(16.dp)
    .background(Color.Gray)
    .clickable { /* ํด๋ฆญ ๋™์ž‘ */ }

์ปดํฌ์ฆˆ์—์„œ๋Š” margin์„ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  padding๋งŒ ์ง€์ •ํ•œ๋‹ค. margin์„ ๋ช…์‹œ์ ์œผ๋กœ ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ offset์œผ๋กœ ์œ„์น˜ ์กฐ์ •๋งŒ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๊ฒ ๋‹ค. match_parent๊ฐ™์€ ๊ฑด `fillMaxWidth, fillMaxHeight, fillMaxSize` ๊ฐ™์€๊ฑธ๋กœ ์ ‘๊ทผํ•˜๋ฉด ๋œ๋‹ค. ํด๋ฆญ๋ฆฌ์Šค๋„ˆ๋„ ์—ฌ๊ธฐ์„œ ๋‹ฌ๋ฉด ๋œ๋‹ค.

๊ทผ๋ฐ xml๊ณผ ์•„์ฃผ ๋‹ค๋ฅธ์ ์ด ์กด์žฌํ•œ๋‹ค. ์ฒด์ด๋‹ ์ˆœ์„œ๊ฐ€ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ๋งŒ๋“ ๋‹ค๋Š” ์ ์ด๋‹ค.

@Preview(showBackground = true)
@Composable
fun TestBox() {
    Column {
        Box(modifier = Modifier
            .size(200.dp)
            .padding(40.dp)
            .background(Color.Red)) {
        }
        Box(modifier = Modifier.size(0.dp, 20.dp))
        Box(modifier = Modifier
            .padding(40.dp)
            .size(200.dp)
            .background(Color.Red)) {
        }
    }
}

์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ•˜๊ณ  ํŒจ๋”ฉ์„ ์ค€๊ฒƒ๊ณผ, ํŒจ๋”ฉ์„ ๋จผ์ € ์ฃผ๊ณ  ์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ•œ ๊ฒƒ์€ ์•„์˜ˆ ๋‹ค๋ฅด๋‹ค. ์ „์ž๋Š” ์ปจํ…์ธ  ํฌ๊ธฐ๊ฐ€ 160, ํ›„์ž๋Š” 200์ด๋‹ค.

then์„ ์‚ฌ์šฉํ•ด์„œ ์กฐ๊ฑด๋ถ€๋กœ ๋‹ค๋ฅธ Modifier๋ฅผ ๋ถ™์ด๋Š” ์ž‘์—…๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

@Preview(showBackground = true)
@Composable
fun TestBox() {
    var isSelected by remember { mutableStateOf(false) }

    Column {
        Box(
            modifier = Modifier
                .size(200.dp)
                .padding(40.dp)
                .background(Color.Red)
        ) {
        }
        Box(modifier = Modifier.size(0.dp, 20.dp))
        Box(
            modifier = Modifier.padding(40.dp)
                .size(200.dp)
                .then(
                    if (isSelected) Modifier
                        .background(Color.Blue)
                    else Modifier
                        .background(Color.Red)
                )
        )
        Button(onClick = { isSelected = !isSelected }) {
            Text(if (isSelected) "์„ ํƒ๋จ" else "์„ ํƒ ์•ˆ๋จ")
        }
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ๋•Œ, isSelected๊ฐ’์ด ํ† ๊ธ€๋˜๋ฉด์„œ ํ•˜๋‹จ Box์˜ Modifier๊ฐ€ ๊ฐ™์ด ํ† ๊ธ€๋œ๋‹ค.

# Surface 

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Surface(color = MaterialTheme.colorScheme.primary) {
        Text(
            text = "Hello $name!",
            modifier = modifier
        )
    }
}

์ปดํฌ์ฆˆ์—์„œ Surface๋Š” ์ปดํฌ์ €๋ธ” ์ปจํ…Œ์ด๋„ˆ ์ค‘ ํ•˜๋‚˜๋‹ค. ๊ทผ๋ฐ ์ด์ œ Material Design์„ ๊ณ๋“ค์ธ ํ˜•ํƒœ๋กœ, ์•Œ์•„์„œ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด ๋งŽ์€ ์ปดํฌ๋„ŒํŠธ๋‹ค. ๋จธํ„ฐ๋ฆฌ์–ผ ๋””์ž์ธ์—์„œ ๋‚˜์˜จ ์ปดํฌ๋„ŒํŠธ ๋‹ต๊ฒŒ, color์— primary๋กœ ์ง€์ •ํ•˜๋ฉด, colorScheme์— ์กด์žฌํ•˜๋Š” primary ๋Œ€์‘ํ•˜๋Š” onPrimary์ƒ‰๊น”๋กœ ์ปดํฌ๋„ŒํŠธ ๋Œ€๋น„๋ฅผ ์ด๋ค„์„œ ๊ฐ€๋…์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค. 

# ํ˜ธ์ด์ŠคํŒ…๊ณผ ์ƒํƒœ๊ด€๋ฆฌ

์›น ๊ฐœ๋ฐœ์—์„œ ์ฒ˜์Œ ์ ‘ํ•œ ๋‹จ์–ด์ธ๋ฐ, ์ปดํฌ์ฆˆ์—๋„ ๋“ฑ์žฅํ•œ๋‹ค.

Composable ํ•จ์ˆ˜์—์„œ ์—ฌ๋Ÿฌ ๊ณณ์— ์“ฐ์ด๋Š” ์ƒํƒœ๋Š” ๊ณตํ†ต ์ƒ์œ„ํ•ญ๋ชฉ์— ์œ„์น˜ํ•ด์•ผ๋œ๋‹ค. ์ด๊ฑธ ์ƒํƒœ ํ˜ธ์ด์ŠคํŒ…์ด๋ผ๊ณ  ๋งํ•˜๋ฉฐ ํ•˜์œ„ ์ปดํฌ์ €๋ธ”์—์„œ ์ƒํƒœ ์žฌ์‚ฌ์šฉ์ด ๊ฐ„ํŽธํ•ด์„œ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค. Datasource๊ฐ€ ์ด๋Ÿฐ ๊ณณ์— ์†ํ•œ๋‹ค.

flowchart LR
    B(์ƒํƒœ์ œ๊ณต - MutableStateOf ๊ฐ์ฒด)
    B --> D[A]
    B --> E[B]
    B --> F[C]

์ƒํƒœ๊ฐ’์„ ์ƒ์œ„์š”์†Œ๋ž‘ ๊ณต์œ ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ƒํƒœ๊ฐ’์— ์•ก์„ธ์Šค ํ•ด์•ผ๋˜๋Š” ๊ณตํ†ต ์ƒ์œ„ ์š”์†Œ ์ชฝ์œผ๋กœ ์ƒํƒœ๊ฐ’์„ ์ด๋™์‹œํ‚ค๋ฉด ๋œ๋‹ค.

์ฆ‰ ๋‹จ๋ฐฉํ–ฅ์„ฑ์œผ๋กœ ์ƒ์œ„ ์š”์†Œ๋Š” ํ•˜์œ„ ์š”์†Œ๋กœ ์ƒํƒœ๋ฅผ ์ „๋‹ฌํ•˜๊ณ , ํ•˜์œ„ ์š”์†Œ๋Š” ๊ทธ๊ฑธ ๋ฐ›์•„ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์œ„ ์š”์†Œ๋กœ ์ œ๊ณตํ•˜๋Š”, ๊ฑฐ์˜ ์›น ๊ฐœ๋ฐœ์—์„œ์˜ ํ˜ธ์ด์ŠคํŒ…๊ณผ ๋™์ผํ•œ ํ˜•ํƒœ๋‹ค. ๊ฐ„๋‹จํ•œ ์นด์šดํ„ฐ ์ฝ”๋“œ๋ž‘ ๊ฐ™์ด ๋ณด์ž.

@Composable
fun ParentComponent() {
    var count by remember { mutableStateOf(0) }
    
    HoistedCounter(
        count = count,
        onIncrement = { count++ }
    )
}

์ตœ์ƒ์œ„ ์ปดํฌ์ €๋ธ”์ด๋‹ค. ์—ฌ๊ธฐ์„œ count๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.

@Composable
fun HoistedCounter(
    count: Int,
    onIncrement: () -> Unit
) {
    Column {
        Button(onClick = onIncrement) {
            Text("์ฆ๊ฐ€์ฆ๊ฐ€")
        }
        Text("๊ฐœ์ˆ˜: $count")
    }
}

์ƒํƒœ๋ฅผ Parentํ•œํ…Œ ๋ฐ›๊ณ , ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•  ๋™์ž‘์„ ๋žŒ๋‹ค๋กœ ๋„˜๊ฒจ ๋ฐ›๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ด ํ˜ธ์ด์ŠคํŒ… ๋ฐ›๋Š” ์ปดํฌ์ €๋ธ”์—์„œ๋Š” ํด๋ฆญ์ด๋ฒคํŠธ๋งŒ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค. ์–ด๋”˜๊ฐ€ ์ต์ˆ™ํ•˜๊ฒŒ ๋Š๊ปด์ง„๋‹ค๋ฉด, recyclerview adapter๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํด๋ฆญ์ด๋ฒคํŠธ๋ฅผ ์ด๋ ‡๊ฒŒ ๋„˜๊ธด ํ˜•ํƒœ๊ฐ€ ์žˆ์–ด์„œ๋‹ค. ํ˜ธ์ด์ŠคํŒ…์ด ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๋‹จ๋ฐฉํ–ฅ์œผ๋กœ ํ•ด์ฃผ๊ธด ํ•˜์ง€๋งŒ, ๋„ˆ๋ฌด depth๊ฐ€ ๊นŠ์–ด์ง€๋ฉด ๋ฆฌ์ปดํฌ์ง€์…˜์ด ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ์–ด์„œ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ํŒŒํŽธํ™”ํ•ด์„œ ์ƒํƒœ๋ฅผ ์ข€ ๋” ์ž‘์€ ๋‹จ์œ„๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ์ˆ ์ด ํ•„์š”ํ•˜๋‹ค.

 

๋ฆฌ์ปดํฌ์ง€์…˜์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ด์ „์— ์‚ฌ์šฉํ–ˆ์—ˆ๋˜ remember๋ฅผ ์„ ํƒํ•  ์ˆ˜๋„ ์žˆ๊ณ , key๋‚˜ derivedStateOf๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์บ์‹ฑํ•ด๋‘๋Š” ๋ฐฉ์‹์œผ๋กœ ์ข€ ๋” ์„ฑ๋Šฅ์„ ๋Œ์–ด์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค. remeber๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์€, ์ปดํฌ์ €๋ธ”์ด ์ปดํฌ์ง€์…˜์— ์œ ์ง€๋˜๋Š” ๋™์•ˆ์—๋งŒ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ํ™”๋ฉดํšŒ์ „๊ณผ ๊ฐ™์€ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋‚ ์•„๊ฐ„๋‹ค. ๊ทธ๋•Œ๋Š” rememberSaveable์„ ์จ์ฃผ๋ฉด ๋๋‚œ๋‹ค.

val processedItems by remember(items) {
    derivedStateOf { items.map { it.uppercase() } }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด items์—์„œ ๋ณ€๊ฒฝ์ด ์žˆ์„๋•Œ๋งŒ map ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๋žŒ๋‹ค์— remember๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งค ๋ฆฌ์ปดํฌ์ง€์…˜ ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋žŒ๋‹ค๊ฐ€ ์ƒ์„ฑ๋˜๋Š”๊ฑธ ๋ง‰๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ตœ์ ํ™”๋ฅผ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

key๊ฐ€ ์ค‘์š”ํ•œ๋ฐ, ์ปดํฌ์ฆˆ๋Š” key ๊ฐ’์œผ๋กœ ์ด์ „ ์ปดํฌ์ง€์…˜, ์ƒˆ ์ปดํฌ์ง€์…˜์˜ ์ฐจ์ด๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๋™์ผํ•œ ํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ์žฌ์‚ฌ์šฉ๋˜๊ณ , ์•„๋‹ˆ๋ผ๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ฃผ์–ด์งˆ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค. ๋งŒ์•ฝ ๋งค๋ฒˆ ๋ฆฌ์ŠคํŠธ ๋‚ด์˜ ์ˆœ์„œ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์„ฑ๋Šฅ์ด ๋–จ์–ด์งˆ ์ˆ˜ ๋„ ์žˆ๋‹ค. key๋ฅผ ์“ฐ๋ฉด ๊ทธ๊ฒƒ๋งŒ ์—…๋ฐ์ดํŠธ ํ•˜๋ฉด ๋ผ์„œ ๋ฆฌ์ปดํฌ์ง€์…˜ ์—†๋Š” ํšจ์œจ์ ์ธ ์ž‘์—…์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

@Composable
fun KeyTestList(items: List<String>) {
    Column {
        items.forEachIndexed { index, item ->
            key(index) {
                Text(item)
            }
        }
    }
}

@Composable
fun MultipleKeysExample(items: List<Item>) {
    Column {
        items.forEach { item ->
            key(item.id, item.category) {
                ItemComponent(item)
            }
        }
    }
}

ํ‚ค๋ฅผ ๋ช‡๊ฐœ ์กฐํ•ฉํ•ด์„œ ๋ณตํ•ฉํ‚ค๋กœ ์“ธ์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ์ƒํƒœ๊ด€๋ฆฌ๋Š” ๋Œ€๊ทœ๋ชจ ๋ฆฌ์ŠคํŠธ ๊ฐ™์€ ๊ณณ์—์„œ ๋น›์„ ๋ฐœํ•˜๋Š”๋ฐ, ์ง€๊ธˆ๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž. 

 

# `LazyColumn, LazyRow, LazyVerticalGrid...`

์ด ์นœ๊ตฌ๋“ค์€ ์™„๋ฒฝํ•˜๊ฒŒ RecyclerView์˜ ์ƒ์œ„ํ˜ธํ™˜์ด๋‹ค. ์ฝ”๋“œ ๋ช‡์ค„๋กœ ๊ธฐ์กด์˜ RecyclerViewAdapter๋ฅผ ์™„์ „ํžˆ ๋Œ€์ฒดํ•ด์ค€๋‹ค.

@Composable
private fun Greetings(
    modifier: Modifier = Modifier,
    names: List<String> = List(1000) { "$it" }
) {
    LazyColumn(modifier = modifier.padding(vertical = 4.dp)) {
        items(items = names) { name ->
            Greeting(name = name)
        }
    }
}

์•„์ดํ…œ 1000๊ฐœ์งœ๋ฆฌ ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ๊ฐ€ ์™„์„ฑ๋๋‹ค. RecycerView๋ž‘ ๋™์ž‘๋ฐฉ์‹์ด ์ข€ ์ฐจ์ด๊ฐ€ ์žˆ๋Š”๋ฐ, RecyclerView๊ฐ€ ์•„์ดํ…œ ๋ทฐ๋ฅผ ๊ณ„์† ์žฌํ™œ์šฉํ•˜๋Š” ๋ฐ˜๋ฉด Lazy ์‹œ๋ฆฌ์ฆˆ๋“ค์€ ๊ทธ๋ƒฅ ์ƒˆ ์•„์ดํ…œ ์ปดํฌ์ €๋ธ”์„ emitํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์•„์ดํ…œ์˜ ์ƒํƒœ๋„ ๊ณ ์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ธ๋ฑ์Šค์™€ ํ•ด๋‹น ์•„์ดํ…œ์˜ ์ƒํƒœ๊ฐ’์„ rememberSaveable๋กœ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ•˜๋ฉด ๋œ๋‹ค.

 

์•„๊นŒ key๋ฅผ ์—ฌ๊ธฐ์— ์ ์šฉํ•˜๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

@Composable
fun UserList(users: List<User>) {
    LazyColumn {
        items(users, key = { user -> user.id }) { user ->
            UserItem(user)
        }
    }
}

๊ฐ ํ•ญ๋ชฉ์˜ ๊ณ ์œ  ํ‚ค๋ฅผ user์˜ id๋กœ ์ง€์ •ํ•ด์ฃผ๋Š” ์ฝ”๋“œ๋‹ค.

@Composable
fun LazyColumnTest(items: List<String>) {
    LazyColumn(
        contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
        verticalArrangement = Arrangement.spacedBy(4.dp)
    ) {
        item {
            Text("Header")
        }
        
        items(items) { item ->
            Text(text = item)
        }
        
        itemsIndexed(items) { index, item ->
            Text("Item at $index is $item")
        }
        
        item {
            Text("Footer")
        }
    }
}

items์— ์•„์ดํ…œ๋“ค์ด ๋“ค์–ด๊ฐ€๊ณ , ๊ทธ์ „์— ์žˆ๋Š” item์€ ํ—ค๋”, ๊ทธ ๋‹ค์Œ์— ์žˆ๋Š” item์€ ํ‘ธํ„ฐ๋‹ค. ์‹ฌ์ง€์–ด items๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ๋„ฃ์œผ๋ฉด ์ž๋™์œผ๋กœ concat recycerview๋‹ค. itemsIndexed๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธ๋ฑ์Šค๋ฒˆํ˜ธ์™€ ๊ฐ™์ด ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด์„œ ์กฐ๊ธˆ ๋” ๊ฐœ๋ฐœ ์นœํ™”์ ์ด๋‹ค. 

์ข€ ๋” ์ถฉ๊ฒฉ์ ์ธ๊ฑด, LazyListState๋ฅผ ์จ์„œ list state๋ฅผ ๋ฐ›๊ณ , ๊ทธ๊ฑธ ๊ธฐ์ค€์œผ๋กœ ํ„ฐ๋ฌด๋‹ˆ ์—†์ด ์‰ฝ๊ฒŒ ๋ฌดํ•œ์Šคํฌ๋กค์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

val listState = rememberLazyListState()

LazyColumn(state = listState) {
	...
}
LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .collect { index ->
            if (index > items.size - 10) {
                items += List(20) { "์ƒˆ ์•„์ดํ…œ ${items.size + it}" }
            }
        }
}

์ด๋ ‡๊ฒŒ ๋ฌดํ•œ์Šคํฌ๋กค ๊ตฌํ˜„์ด ๋๋‚ฌ๋‹ค. ์ง€๊ธˆ์€ ๊ทธ๋ƒฅ ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ์•„์ดํ…œ์„ ๋Š˜๋ ค์คฌ์ง€๋งŒ, ๋žŒ๋‹ค๋กœ ์ƒ์œ„ ์ปดํฌ์ €๋ธ”์—์„œ ์•„์ดํ…œ๋“ค์„ ๋ฐ›์•„์˜ค๋Š” ํ˜•ํƒœ๊ฐ€ ์ž์ฃผ ์“ฐ์ธ๋‹ค๊ณ  ํ•œ๋‹ค.

 

๋„์›€์ด ๋๋‹ค๋ฉด ๋Œ“๊ธ€์ด๋‚˜ ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์„ธ์š”!

 

๋ฐ˜์‘ํ˜•
COMMENT