CHAP08 - Modern-Java-in-Action/Online-Study GitHub Wiki

์ปฌ๋ ‰์…˜ API ๊ฐœ์„ 

  • ์ž‘์€ ๋ฆฌ์ŠคํŠธ, ์ง‘ํ•ฉ, ๋งต์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก ๊ฐœ์„ ๋œ ์ปฌ๋ ‰์…˜ ํŒฉํ† ๋ฆฌ
  • ๋ฆฌ์ŠคํŠธ์™€ ์ง‘ํ•ฉ์—์„œ ์š”์†Œ๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๋ฐ”๊พธ๋Š” ๊ด€์šฉ ํŒจํ„ด ์ ์šฉ ๋ฐฉ๋ฒ•
  • ๋งต ์ž‘์—…๊ณผ ๊ด€๋ จํ•ด ์ถ”๊ฐ€๋œ ์ƒˆ๋กœ์šด ํŽธ๋ฆฌ ๊ธฐ๋Šฅ

โ€‹

๋ฏธ๋ฆฌ ์•Œ์•„๋‘๋ฉด ์ข‹์€ ๊ฒƒ๋“ค

  • ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ : ๊ฐ์ฒด ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ๋ณ„๋„ ํด๋ž˜์Šค/๋ฉ”์„œ๋“œ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ์ฒด ์ƒ์„ฑ์˜ ๋ณ€ํ™”์— ๋Œ€๋น„ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•˜๋‹ค.

  • ๊ฐ์ฒด๋ฅผ final ํ‚ค์›Œ๋“œ๋กœ ์„ ์–ธํ•ด๋„, ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋Š” ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

    final Person p = new Person("Bob", 30);
    //p = new Person("John", 25);
    p.name = "John";
    p.age = 25;
    
    private static class Person{
    	public String name;
    	public int age;
    
    	Person(String name, int age){
    	this.name = name;
    	this.age = age;
    	}
    }

โ€‹

๊ฐœ์š”

  • ์ž๋ฐ” 9๋Š” ์ ์€ ์›์†Œ๋ฅผ ํฌํ•จํ•˜๋ฉฐ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ๋ฆฌ์ŠคํŠธ, ์ง‘ํ•ฉ, ๋งต์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋„๋ก List.of, Set.of, Map.of, Map.ofEntries ๋“ฑ ์ปฌ๋ ‰์…˜ ํŒฉํ† ๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • List ์ธํ„ฐํŽ˜์ด์Šค์—์„œ removeIf, replaceAll, sort ์„ธ ๊ฐ€์ง€ ๋””ํดํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • Set ์ธํ„ฐํŽ˜์ด์Šค๋Š” removeIf ๋””ํดํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • Map ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด๊ณผ ๋ฒ„๊ทธ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์–‘ํ•œ ๋””ํดํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • ConcurrentHashMap์€ Map์—์„œ ์ƒ์†๋ฐ›์€ ์ƒˆ ๋””ํดํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•จ๊ณผ ๋™์‹œ์— ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ๋„ ์ œ๊ณตํ•œ๋‹ค.
๊ตฌ๋ถ„ ๋ฉ”์„œ๋“œ ์„ค๋ช…
๊ณตํ†ต List.of, Set.of, Map.of ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ๋ฆฌ์ŠคํŠธ/์ง‘ํ•ฉ/๋งต ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
๋ฆฌ์ŠคํŠธ/์ง‘ํ•ฉ removeIf ํ”„๋ฆฌ๋””์ผ€์ดํŠธ๋ฅผ ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๋ฅผ ์ œ๊ฑฐ
replaceAll UnaryOperator ํ•จ์ˆ˜๋กœ ์š”์†Œ๋ฅผ ๋ฐ”๊ฟˆ
sort ์ •๋ ฌ
๋งต forEach Biconsumer๋ฅผ ๋„˜๊ฒจ ํ‚ค์™€ ๊ฐ’์„ ๋ฐ˜๋ณตํ•˜์—ฌ ์ฒ˜๋ฆฌ
sorted ์ •๋ ฌ
getOrDefault ์ฐพ์œผ๋ ค๋Š” ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„๋•Œ ๋ฐ˜ํ™˜ํ•  ๊ธฐ๋ณธ๊ฐ’์„ ์ง€์ •
computeIfAbsent ์ œ๊ณต๋œ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ด ์—†์œผ๋ฉด, ํ‚ค๋กœ ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋งต์— ์ถ”๊ฐ€
computeIfPresent ์ œ๊ณต๋œ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ด ์žˆ์œผ๋ฉด, ํ‚ค๋ฅผ ์ด์šฉํ•ด ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋ณ€๊ฒฝ
compute ์ œ๊ณต๋œ ํ‚ค๋กœ ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ์ €์žฅ
remove ํ‚ค/๊ฐ’์„ ์ธ์ž๋กœ ๋ฐ›์•„ ํ•ด๋‹น๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œ
replaceAll BiFunction์„ ์ ์šฉํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐ ํ•ญ๋ชฉ์˜ ๊ฐ’์„ ๊ต์ฒด
replace ํ‚ค๊ฐ€ ์กด์žฌํ•˜๋ฉด ๋งต์˜ ๊ฐ’์„ ๋ฐ”๊ฟˆ
merge ํ‚ค, ๊ฐ’, ํ‚ค ์ค‘๋ณต์‹œ ์ฒ˜๋ฆฌํ•  BiFunctino์„ ์ธ์ž๋กœ ๋„˜๊ธบ
ConcurrentHashMap forEach ํ‚ค/๊ฐ’ ์Œ์— ์ฃผ์–ด์ง„ ์•ก์…˜์„ ์‹คํ–‰
reduce ๋ชจ๋“  ํ‚ค/๊ฐ’ ์Œ์„ ์ œ๊ณต๋œ ๋ฆฌ๋“€์Šค ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ํ•ฉ์นจ
search null ์•„๋‹Œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ํ‚ค/๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์ ์šฉ
mappingCount long์„ ๋ฐ˜ํ™˜(๊ธฐ์กด size๋Š” int)
keySet ํ‚ค๊ฐ’์„ ์ง‘ํ•ฉ์œผ๋กœ ๋ณ€ํ™˜, ์ง‘ํ•ฉ/๋งต์€ ์„œ๋กœ ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ๋™๊ธฐํ™”๋จ

์˜ˆ์ œ ๋ฐ์ดํ„ฐ ๋งŒ๋“ค๊ธฐ

maven ํ”„๋กœ์ ํŠธ์—์„œ pom.xml์— ์•„๋ž˜์™€ ๊ฐ™์ด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.17.4</version>
</dependency>

Faker ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด ์ž„์˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Faker faker = new Faker();
documents = new HashMap<>();

for(int i=0; i<10; i++){
    documents.put(faker.dune().character(), faker.dune().planet());
}

documents.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(System.out::println);
documents.forEach((name, address)->System.out.println(name+" : "+address));
System.out.println(documents.getOrDefault("Chani", "NOT_FOUND_CHANI"));

์ง€์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ์…‹ : ์ฃผ์†Œ, ์ด๋ฆ„, ํ•ญ๊ณต๊ธฐ, ์ฝ”์ธ, ๊ณ ์–‘์ด, ์ƒ‰ ๋“ฑ 82์ข…

DiUS/java-faker: Brings the popular ruby faker gem to Java (github.com)

java faker ๋กœ ํ…Œ์ŠคํŠธ ๋ฐ์ดํƒ€ ๋งŒ๋“ค๊ธฐ (lesstif.com)

](https://www.lesstif.com/java/java-faker-48988763.html)

HashMap<String, String> documents = new HashMap<>();
Faker faker = new Faker();
for(int i=0; i<100000; i++){
    documents.put(faker.name().fullName(), faker.address().fullAddress());
}

//documents.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(System.out::println);
//documents.forEach((name, address)->System.out.println(name+" : "+address));
//System.out.println(documents.getOrDefault("Chani", "NOT_FOUND"));
//documents.entrySet().stream().parallel().sorted(Map.Entry.comparingByKey()).limit(10).forEachOrdered(System.out::println);

System.out.println("# filter startWith A, limit 10");
documents.entrySet().stream().filter(e -> e.getKey().startsWith("A")).limit(10).forEach(System.out::println);

System.out.println("\n# distinct, limit 10");
documents.entrySet().stream().distinct().limit(10).forEach(System.out::println);

System.out.println("\n# map address -> length, limit 10");
documents.entrySet().stream().map(e -> e.getValue().length()).limit(10).forEach(System.out::println);

System.out.println("\n# collect, joining string");
System.out.println(documents.keySet().stream().limit(10).collect(joining(" ")));

System.out.println("\n# collect, reducing for joining string");
System.out.println(documents.keySet().stream().limit(10).collect(reducing("", (o, v)->o+","+v)));

System.out.println("\n# reduce for joining string");
System.out.println(documents.keySet().stream().limit(10).reduce("", (o, v)->o+","+v));

System.out.println("\n# replaceAll name toUpperCase, limit 10");
documents.replaceAll((k,v)->k.toUpperCase());
documents.entrySet().stream().limit(10).forEach(System.out::println);

8.1 ์ปฌ๋ ‰์…˜ ํŒฉํ† ๋ฆฌ

  • Arrays.asList๋กœ ๋งŒ๋“  ๋ฆฌ์ŠคํŠธ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ final ๋ฐฐ์—ด์— ๋‹ด๊ฒจ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†๊ณ , ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋Š” ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ์กด ์ž๋ฐ”์—์„œ ์•„๋ž˜ ์ฝ”๋“œ๋Š” UnsupportedException์„ ๋ฑ‰๋Š”๋‹ค.

List<String> friends2 = Arrays.asList("Rael", "Oli");
friends2.set(0, "Rich");
friends2.add("Thiba"); //์˜ˆ์™ธ๋ฐœ์ƒ

์™œ๋ƒํ•˜๋ฉด Arrays.asList๋Š” ์ž์ฒด ์ •์˜ํ•œ private ArrayList๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ณ ์ •ํฌ๊ธฐ final ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ์ดˆ๊ธฐํ™”ํ•œ ํ›„์—๋Š” ์ƒˆ ๋ฐฐ์—ด๋กœ ๋ฐ”๊พธ์ง€ ๋ชปํ•˜๊ฒŒ ํ–ˆ๊ณ , ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋งŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ AbstractList ์ถ”์ƒํด๋ž˜์Šค๋Š” set, add ๋ชจ๋‘ UnsupportedException ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š”๋ฐ, ๊ทธ์ค‘ set ๋ฉ”์„œ๋“œ๋งŒ ์žฌ์ •์˜ํ•ด๋‘์—ˆ๋‹ค.

private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
		private final E[] a;

		@Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        } 
    }

8.1.1 ๋ฆฌ์ŠคํŠธ ํŒฉํ† ๋ฆฌ

  • List.of๋กœ ๋งŒ๋“  ๋ฆฌ์ŠคํŠธ๋„ ๋ฐ์ดํ„ฐ๊ฐ€ final ๋ฐฐ์—ด์— ๋‹ด๊ฒจ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€/์ˆ˜์ •/์‚ญ์ œํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.
  • ์ƒ์„ฑ์ž์—์„œ 10๊ฐœ ์ดํ•˜ ์ธ์ž๋Š” ๊ณ ์ •ํฌ๊ธฐ ๋ฐฐ์—ด๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š”๋‹ค. ์ธ์ž ๊ฐœ์ˆ˜๊ฐ€ ๊ทธ ์ด์ƒ์ธ ๊ฒฝ์šฐ ๋ฐฐ์—ดํฌ๊ธฐ๋ฅผ ๋Š˜๋ ค๊ฐ€๋ฉฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ณ , ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ๋น„์šฉ์ด ์ถ”๊ฐ€๋กœ ๋“ค์–ด๊ฐ„๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ์—์„œ๋„ add/set ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์‹œ UnsupportedException์„ ๋ฑ‰๋Š”๋‹ค.

List<String> friends = List.of("Rael", "Oli", "Thiba");
friends.add("Taey"); //์˜ˆ์™ธ๋ฐœ์ƒ
friends.set(0, "Taey"); //์˜ˆ์™ธ๋ฐœ์ƒ

List.of๋Š” ImmutableCollections.ListN<>()๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ListN์€ AbstractImmutableList์„ ์ƒ์†ํ•˜๋Š” ์ •์  ํด๋ž˜์Šค์ด๊ณ , AbstractImmutableList๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด final ๋ฐฐ์—ด๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๊ณ , ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ์—์„œ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ๋‹ค.

static final class ListN<E> extends AbstractImmutableList<E>
            implements Serializable {

        static final List<?> EMPTY_LIST = new ListN<>();
    
        @Stable
        private final E[] elements;
    }
    static abstract class AbstractImmutableList<E> extends AbstractImmutableCollection<E>
            implements List<E>, RandomAccess {

        // all mutating methods throw UnsupportedOperationException
        // uoe()๋Š” UnsupportedException์„ ๋˜์ง€๋Š” ๋ฉ”์„œ๋“œ
        @Override public void    add(int index, E element) { throw uoe(); }
        @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
        @Override public E       remove(int index) { throw uoe(); }
        @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
        @Override public E       set(int index, E element) { throw uoe(); }
        @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
    }

8.1.2 ์ง‘ํ•ฉ ํŒฉํ† ๋ฆฌ

  • Set.of๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ์ง‘ํ•ฉ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ธฐ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋˜์ง„๋‹ค.
Set<String> friends = Set.of("Rael", "Oli", "Thiba");
Set<String> friends2 = Set.of("Rael", "Oli", "Thiba", "Rael"); //์˜ˆ์™ธ๋ฐœ์ƒ

//Stream์„ ์‚ฌ์šฉํ•ด ์œ ์ผํ•œ ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜๋Š” ์žˆ์ง€๋งŒ, ๊ณ ์ •ํฌ๊ธฐ๋กœ ๊ฐ€๋ฒผ์šด ์ง‘ํ•ฉ์„ ๋งŒ๋“ค๋ ค๋Š” ์˜๋„์—์„œ ํ•œ์ฐธ ๋ฒ—์–ด๋‚˜ ๋ฒ„๋ฆฐ๋‹ค.
Set<String> friends3 = List.of("Rael", "Oli", "Thiba", "Rael").stream().distinct().collect(toSet());

8.1.3 ๋งต ํŒฉํ† ๋ฆฌ

  • Map.of๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†๋Š” ๋งต์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • entry๋Š” Map.Entry ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ์ด๋ฉฐ, ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค.
Map<String, Integer> ageOfFriends = Map.of("Rael", 30, "Oli", 25, "Thiba", 26);
Map<String, Integer> ageOfFriends2 = Map.ofEntries(
	entry("Rael", 30),
	entry("Oli", 25),
	entry("Thiba", 26)
);

8-1 Quiz

๋‹ค์Œ ์ฝ”๋“œ์˜ ์‹คํ–‰๊ฒฐ๊ณผ๋Š”?

List<String> actors = List.of("Keanu", "Jessica");
actors.set(0, "Brad");
System.out.println(actors);

8.2 ๋ฆฌ์ŠคํŠธ์™€ ์ง‘ํ•ฉ ์ฒ˜๋ฆฌ / map.entrySet()

8.2.1 removeIf

  • removeIf : ํ”„๋ฆฌ๋””์ผ€์ดํŠธ๋ฅผ ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๋ฅผ ์ œ๊ฑฐ, List๋‚˜ Set์„ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ๊ทธ ๊ตฌํ˜„์„ ์ƒ์†๋ฐ›์€ ๋ชจ๋“  ํด๋ž˜์Šค์—์„œ ์ด์šฉ.

removeIf๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ

์ปฌ๋ ‰์…˜์—์„œ ํŠน์ • ๊ฐ์ฒด๋ฅผ ๊ณจ๋ผ์„œ ์‚ญ์ œํ•˜๋ ค๋ฉด? for-each๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Iterator๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๋Š” ๋ฐ Iterator๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๋ฆฌ์ŠคํŠธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๋ฐ ์ปฌ๋ ‰์…˜์œผ๋กœ ์ ‘๊ทผํ•˜๋ฉด ๋‘ ๊ฐ์ฒด๊ฐ„ ๋™๊ธฐํ™”๊ฐ€ ์ด๋ค„์ง€์ง€ ์•Š์•„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์•„๋ž˜ ์ฝ”๋“œ๋Š” ConcurrentModificationException์„ ๋˜์ง„๋‹ค.

TestData td = new TestData();
for(Person p : td.people){
    if(p.age > 50) td.people.remove(p);
}

์œ„ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ•ด์„๋œ๋‹ค.

for(Iterator<Peorson> itr = td.people.iterator(); iterator.hasNext(); ){
    Person p = itr.next();
    /* ์ƒ๋žต */
}

์˜๋„ํ•œ ๋Œ€๋กœ ์ž‘๋™์‹œํ‚ค๋ ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๋ช…์‹œ์ ์œผ๋กœ Iterator๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  remove ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค.

for(Iterator<Peorson> itr = td.people.iterator(); iterator.hasNext(); ){
    Person p = itr.next();
    if(p.age > 50) itr.remove();
}

removeIf๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ

ํ”„๋ฆฌ๋””์ผ€์ดํŠธ๋ฅผ ์ธ์ˆ˜๋กœ ๋„˜๊ฒจ ๊ฐ„๋‹จํžˆ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

td.people.removeIf(p -> p.age>50);

8.2.2 replaceAll

  • replaceAll : ๋ฆฌ์ŠคํŠธ์—์„œ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์œผ๋กœ UnaryOperator ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ์š”์†Œ๋ฅผ ๋ฐ”๊ฟˆ.

replaceAll์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ

์•„๋ž˜์™€ ๊ฐ™์ด ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•ด ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ƒˆ๋กœ์šด ๋ฌธ์ž์—ด ๋ฆฌ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

TestData td = new TestData();
td.people.stream()
         .map(p -> p.name.toLowerCase())
         .collect(Collectors.toList())
         .forEach(System.out::println);

๊ธฐ์กด ์ปฌ๋ ‰์…˜์„ ๋ฐ”๊พธ๋ ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด Iterator์˜ set() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

for(ListIterator<Person> itr = td.people.listIterator(); itr.hasNext(); ){
    Person p = itr.next();
    itr.set(changeName(p));
}

replaceAll์„ ์‚ฌ์šฉํ•  ๋•Œ

td.people.replaceAll(ListSetProcessor::changeName);

8.2.3 sort

td.people.sort((o1, o2) -> Integer.compare(o1.age, o2.age));

8-2 Quiz

์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์งง๊ฒŒ ์ค„์ด๋ ค๋ฉด?

Map<String, Integer> movies = new HashMap<>();
movies.put("JamesBond", 20);
movies.put("Matrix", 15);
movies.put("Harry Potter", 5);
Iterator<Map.Entry<String, Integer>> iterator = movies.entrySet().iterator();
while(iterator.hasNext()){
    Map.Entry<String, Integer> entry = iterator.next();
    if(entry.getValue() < 10){
        iterator.remove();
    }
}
์ •๋‹ต movies.entrySet().removeIf(e -> e.getValue()<10);

โ€‹

8.3 ๋งต ์ฒ˜๋ฆฌ

8.3.1 forEach ๋ฉ”์„œ๋“œ

๋งต์—์„œ ํ‚ค์™€ ๊ฐ’์„ ๋ฐ˜๋ณตํ•˜์—ฌ ํ™•์ธํ•˜๋Š” ์ž‘์—…์€ for๋ฌธ๊ณผ entrySet์„ ์‚ฌ์šฉํ•œ๋‹ค.

Map<String, Integer> ageOfFriends = new HashMap<>();
    ageOfFriends.put("๊น€์ด๋ฆ„", 30);
    ageOfFriends.put("์ด์ต๋ช…", 40);
    ageOfFriends.put("๋ฐ•๋‹‰๋„ค์ž„", 50);

for(Map.Entry<String, Integer> entry : ageOfFriends.entrySet()){
    String name = entry.getKey();
    Integer age = entry.getValue();
    System.out.println(name + " is " + age + " years old.");
}

์ด์ œ๋Š” forEach์— ํ‚ค์™€ ๊ฐ’์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” BiConsumer๋ฅผ ๋„˜๊ฒจ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

ageOfFriends.forEach((name, age)->System.out.println(name+" is "+age+" years old."));

8.3.2 ์ •๋ ฌ ๋ฉ”์„œ๋“œ

HashMap์„ ์ •๋ ฌํ•  ๋•Œ, EntrySet์„ ๋ณ„๋„ Collection ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์„œ Collections.sort๋กœ ์ •๋ ฌํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด sorted ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Map<String, String> favouriteMoview = Map.ofEntries(
        entry("Rahpael", "Star Wars"),
        entry("Cristina", "Matrix"),
        entry("Olivia", "James bond"));

favouriteMoview
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey())
        //.forEach(System.out::println);
        .forEachOrdered(System.out::println);

์ž๋ฐ”8์—์„œ HashMap์€ ๋งŽ์€ ํ‚ค๊ฐ€ ๊ฐ™์€ ํ•ด์‹œ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ, O(logn) ์‹œ๊ฐ„์ด ์†Œ์š”๋˜๋Š” ์ •๋ ฌ๋œ ํŠธ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ๋™์ ์œผ๋กœ ์น˜ํ™˜ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œ์ผฐ๋‹ค.

8.3.3 getOrDefault ๋ฉ”์„œ๋“œ

๊ธฐ์กด์—๋Š” ์ฐพ์œผ๋ ค๋Š” ํ‚ค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด null์ด ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ NullPointerException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. getOrDefault๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ์ง€์ •ํ•ด์คŒ์œผ๋กœ์จ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

Map<String, String> favouriteMovies = Map.ofEntries(
        entry("Rahpael", "Star Wars"),
        entry("Cristina", "Matrix"));
System.out.println(favouriteMovies.getOrDefault("Olivia", "Matrix"));
System.out.println(favouriteMovies.getOrDefault("Timothy", "Dune"));

๋‹จ, ํ‚ค๋Š” ์กด์žฌํ•˜๊ณ  ๊ฐ’์€ null์ธ ๊ฒฝ์šฐ ์œ„ ์ฝ”๋“œ๋กœ๋„ NullPointerException์„ ๋งˆ์ฃผํ•  ์ˆ˜ ์žˆ๋‹ค.

8.3.4 ๊ณ„์‚ฐ ํŒจํ„ด

  • computeIfAbsent : ์ œ๊ณต๋œ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ด ์—†์œผ๋ฉด, ํ‚ค๋ฅผ ์ด์šฉํ•ด ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋งต์— ์ถ”๊ฐ€
  • computeIfPresent : ์ œ๊ณต๋œ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ด ์žˆ์œผ๋ฉด, ํ‚ค๋ฅผ ์ด์šฉํ•ด ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๊ฐ’์„ ๋ณ€๊ฒฝ
  • compute : ์ œ๊ณต๋œ ํ‚ค๋กœ ์ƒˆ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๊ณ  ๋งต์— ์ €์žฅํ•œ๋‹ค.

ํŒŒ์ผ ํ•œ์ค„ํ•œ์ค„ ํ•ด์‹œ๊ฐ’์„ ๊ณ„์‚ฐํ•ด ์ €์žฅํ•˜๋Š” ์ปฌ๋ ‰์…˜. computeIfAbsent๋กœ ์ด๋ฏธ ์ฝ์–ด์˜จ ์ค„์ธ์ง€ ํ™•์ธํ•˜๊ณ  ํ•ด์‹œ๊ฐ’ ๊ณ„์‚ฐํ•ด ์ถ”๊ฐ€.

try (Stream<String> lines = Files.lines(Paths.get("D:\\DailyLogs\\README.md"), Charset.defaultCharset())){
    Map<String, byte[]> dataToHash = new HashMap<>();
    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
    this.messageDigest = messageDigest;

    lines.forEach(line -> dataToHash.computeIfAbsent(line, this::calculateDigest));

} catch (NoSuchAlgorithmException | IOException e) {
    e.printStackTrace();
}

์นœ๊ตฌ์ด๋ฆ„/์˜ํ™”๋ชฉ๋ก ์ปฌ๋ ‰์…˜. ์นœ๊ตฌ ์ด๋ฆ„์„ ๊ฒ€์ƒ‰ํ•ด ์—†์œผ๋ฉด ์ƒˆ ์˜ํ™”๋ชฉ๋ก์„ ์ƒ์„ฑํ•ด ์ถ”๊ฐ€.

Map<String, List<String>> friendsToMovies = Map.ofEntries();
String friend = "Raphael";
List<String> movies = friendsToMovies.get(friend);
if(movies==null){
    movies = new ArrayList<>();
    friendsToMovies.put(friend, movies);
}
movies.add("Star Wars");

Map<String, List<String>> friendsToMovies2 = Map.ofEntries();
friendsToMovies2.computeIfAbsent("Rahpael", name -> new ArrayList<>())
                .add("Star Wars");

8.3.5 ์‚ญ์ œ ํŒจํ„ด

๊ธฐ์กด์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ‚ค๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” remove ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ๊ทธ์ „์— ํ•ด๋‹น ํ‚ค๊ฐ€ ์ปฌ๋ ‰์…˜์— ์กด์žฌํ•˜๋Š”์ง€ ๊ทธ๋ฆฌ๊ณ  ์‚ญ์ œํ•˜๋ ค๋Š” ๋ฐ์ดํ„ฐ์˜ ํ‚ค/๊ฐ’๊ณผ ๋น„๊ตํ•ด์•ผ ํ–ˆ๋‹ค.

Map<String, String> favouriteMovies = new HashMap<>(Map.ofEntries(
        entry("Rahpael", "Star Wars"),
        entry("Cristina", "Matrix"),
        entry("Olivia", "James bond")));

String key = "Raphael";
String value = "Jack Reacher 2";

if(favouriteMovies.containsKey(key) && Objects.equals(favouriteMovies.get(key), value)){
    favouriteMovies.remove(key);
}

์ด์ œ ํ‚ค/๊ฐ’์„ ์ธ์ž๋กœ ๋„˜๊ฒจ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

favouriteMovies.remove(key, value);

8.3.6 ๊ต์ฒด ํŒจํ„ด

  • replaceAll : BiFunction์„ ์ ์šฉํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐ ํ•ญ๋ชฉ์˜ ๊ฐ’์„ ๊ต์ฒดํ•œ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ์ด์ „์— ์‚ดํŽด๋ณธ List์˜ replaceAll๊ฐ€ ๋น„์Šทํ•œ ๋™์ž‘์„ ํ•œ๋‹ค.
  • replace : ํ‚ค๊ฐ€ ์กด์žฌํ•˜๋ฉด ๋งต์˜ ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค. ํ‚ค๊ฐ€ ํŠน์ • ๊ฐ’์œผ๋กœ ๋งคํ•‘๋˜์—ˆ์„ ๋•Œ๋งŒ ๊ฐ’์„ ๊ต์ฒดํ•˜๋Š” ์˜ค๋ฒ„๋กœ๋“œ ๋ฒ„์ „๋„ ์žˆ๋‹ค.
favouriteMovies.replaceAll((friend, movie) -> movie.toUpperCase());

8.3.7 ํ•ฉ์นจ

๋‘ ๋งต์„ ํ•ฉ์น  ๋•Œ ์ค‘๋ณต๋œ ํ‚ค/๊ฐ’์ด ์žˆ๋‹ค๋ฉด ์˜๋„ํ•˜์ง€ ์•Š์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

Map<String, String> family = Map.ofEntries(entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));
Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars"), entry("Cristina", "Matrix"));

Map<String, String> everyone = new HashMap<>(family);
everyone.putAll(friends);

BiFunction์„ ์ธ์ž๋กœ ๋„˜๊ฒจ ์ค‘๋ณต๋œ ํ‚ค๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ฉ์น ์ง€ ๋ฏธ๋ฆฌ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

Map<String, String> everyone2 = new HashMap<>(family);
friends.forEach((k,v)->everyone2.merge(k,v,(movie1, movie2)->movie1+" & "+movie2));

๋˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ดˆ๊ธฐ๊ฐ’์„ ์ •ํ•˜๊ณ  ๊ฐ’์„ ๊ฐฑ์‹ ํ•ด๋‚˜๊ฐˆ ์ˆ˜๋„ ์žˆ๋‹ค.

moviesToCount.merge(movieName, 1L, (k, v)->v+1L);

8.4 ๊ฐœ์„ ๋œ ConcurrentHashMap

  • ๋‚ด๋ถ€ ์ž๋ฃŒ๊ตฌ์กฐ์˜ ํŠน์ • ๋ถ€๋ถ„๋งŒ ์ž ๊ถˆ ๋™์‹œ ์ถ”๊ฐ€, ๊ฐฑ์‹  ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋™๊ธฐํ™”๋œ Hashtable์— ๋น„ํ•ด ์ฝ๊ธฐ ์“ฐ๊ธฐ ์—ฐ์‚ฐ ์„ฑ๋Šฅ์ด ์›”๋“ฑํ•˜๋‹ค. (์ฐธ๊ณ ๋กœ HashMap์€ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘)

8.4.1 ๋ฆฌ๋“€์Šค์™€ ๊ฒ€์ƒ‰

  • ์—ฐ์‚ฐ์ข…๋ฅ˜

    • forEach ํ‚ค/๊ฐ’ ์Œ์— ์ฃผ์–ด์ง„ ์•ก์…˜์„ ์‹คํ–‰

    • redue ๋ชจ๋“  ํ‚ค/๊ฐ’ ์Œ์„ ์ œ๊ณต๋œ ๋ฆฌ๋“€์Šค ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๊ฒฐ๊ณผ๋กœ ํ•ฉ์นจ

    • search ๋„์ด ์•„๋‹Œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ๊นŒ์ง€ ํ‚ค/๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์ ์šฉ

  • ์—ฐ์‚ฐ๋ฐฉ๋ฒ•

    • ํ‚ค, ๊ฐ’์œผ๋กœ : forEach, reduce, search
    • ํ‚ค๋กœ : forEachKey, reduceKeys, searchKeys
    • ๊ฐ’์œผ๋กœ : forEachValue, reduceValues, searchValues
    • Map.Entry ๊ฐ์ฒด๋กœ : forEachEntry, reduceEntries, searchEntries

์œ„ ์—ฐ์‚ฐ๋“ค์€ ConcurrentHashMap์˜ ์ƒํƒœ๋ฅผ ์ž ๊ทธ์ง€ ์•Š๋Š”๋‹ค. ์ด๋“ค ์—ฐ์‚ฐ์— ์ œ๊ณตํ•œ ํ•จ์ˆ˜๋Š” ์ง„ํ–‰๋˜๋Š” ๋™์•ˆ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด, ๊ฐ’, ์ˆœ์„œ ๋“ฑ์— ์˜์กดํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.

์•„์šธ๋Ÿฌ ์ฒซ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ๋ณ‘๋ ฌ์„ฑ ๊ธฐ์ค€๊ฐ’(threshold)์„ ์ •ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ, ๊ธฐ์ค€๊ฐ’์ด 1์ด๋ฉด ๋ณ‘๋ ฌ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๊ณ  Long.MAX_VALUE์ด๋ฉด ํ•œ ๊ฐœ ์Šค๋ ˆ๋“œ๋กœ ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•œ๋‹ค.

ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>();
long parallelismThreshod = 1;
Optional<Long> maxValue = Optional.ofNullable(map.reduceValues(parallelismThreshod, Long::max));

ํ•„์š”์‹œ ๊ธฐ๋ณธ๊ฐ’ ์ „์šฉ ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•˜์ž. (reduceValuesToInt, reduceKeysToLong ๋“ฑ)

8.4.2 ๊ณ„์ˆ˜

  • mappingCount : long์„ ๋ฐ˜ํ™˜ํ•จ
  • size : int๋ฅผ ๋ฐ˜ํ™˜

concurrentHashMap ํฌ๊ธฐ๊ฐ€ int๊ฐ’์„ ๋„˜์–ด๊ฐˆ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•  ์ˆ˜ ์žˆ๋‹ค.

โ€‹

8.4.3 ์ง‘ํ•ฉ๋ทฐ

  • keySet : ํ‚ค๊ฐ’์„ ์ง‘ํ•ฉ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ง‘ํ•ฉ/๋งต ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ƒํ˜ธ๊ฐ„ ์ ์šฉ๋œ๋‹ค. ๋‹จ, ์ง‘ํ•ฉ์— ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด keySet ์ƒ์„ฑ์‹œ ์ธ์ž๋กœ ๊ธฐ๋ณธ๊ฐ’์„ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • newKeySet : ์ƒˆ ๋งต ๊ฐ์ฒด์™€ ์ง‘ํ•ฉ๋ทฐ๋ฅผ ๋™์‹œ์— ์ƒ์„ฑํ•œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์„ TRUE๋กœ ์ง€์ •ํ•ด์ค€๋‹ค.
Set<String> set = map.keySet(0L);
System.out.println(map);
System.out.println(set);

set.add("BYE");
System.out.println(map);
System.out.println(set);

map.replace("A", 100L);
map.put("HELLO", 1000L);
System.out.println(map);
System.out.println(set);

keySet์€ CollectinView๋ฅผ ์ƒ์†ํ•˜๋Š” keySetView๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  • CollectionView ๋ฉ”์„œ๋“œ

    • clear() size() isEmpty() iterator(), containse(), remove(), toArray(), toString(), containsAll(), removeAll(), retainAll()
  • keySetView ๋ฉ”์„œ๋“œ

    • keySetView ์ƒ์„ฑ์ž์—์„œ ๋””ํดํŠธ ๊ฐ’์„ ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค. ๋งŒ์•ฝ ๊ฐ์ฒด ์ƒ์„ฑ์‹œ ๋””ํดํŠธ ๊ฐ’์„ ์ •ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

    • public boolean add(K e) {
          V v;
          if ((v = value) == null)
              throw new UnsupportedOperationException();
          return map.putVal(e, v, true) == null;
      }
System.out.println("#1 ==========================================");
ConcurrentHashMap.KeySetView<String, Long> set = map.keySet(0L);
System.out.println(map);
System.out.println(set);
System.out.println(set.getMappedValue());

System.out.println("#2 ==========================================");
ConcurrentHashMap.KeySetView<String, Long> kset = map.keySet();
System.out.println(kset.getMappedValue());

System.out.println("#3 ==========================================");
set.add("BYE");
System.out.println(map);
System.out.println(set);

System.out.println("#4 ==========================================");
map.replace("A", 100L);
map.put("HELLO", 1000L);
System.out.println(map);
System.out.println(set);

System.out.println("#5 ==========================================");
ConcurrentHashMap.KeySetView<String, Boolean> keySet = ConcurrentHashMap.newKeySet();
keySet.add("HELLO");
System.out.println(keySet);

System.out.println("#6 ==========================================");
ConcurrentHashMap.KeySetView<String, Boolean> keySet2 = ConcurrentHashMap.newKeySet(10);
keySet.add("HELLO");
System.out.println(keySet);
System.out.println(keySet.size());
โš ๏ธ **GitHub.com Fallback** โš ๏ธ