{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 06. Iterators, Collections, and Streams\n", "\n", "Java를 포함한 많은 프로그래밍 언어에서 *반복자*(iterator)와 *컬렉션*(collection)이라는 개념을 표준라이브러리에 포함하고 있다.\n", "\n", "컬렉션은 여러 개의 오브젝트를 모아 놓은 데이타 구조이며 반복자는 그러한 데이터 구조를 따라 각각의 오브젝트 순회하는 기능을 제공한다.\n", "\n", "자바 표준라이브러리(JDK)에서 이 부분에 해당하는 것을 *자바 컬렉션 프레임워크*(Java Collection Framework)라고 부르기도 한다.\n", "\n", "이러한 컬렉션 프레임워크가 범용 프로그래밍 언어 표준라이브러리의 일부로 제공되기 시작한 것은 C++ 언어부터 유행하기 시작했다.\n", "Standard Template Library (STL)가 C++ 표준라이브러리의 *컨테이너*, *이터레이터*, *알고리듬*, *함수객체*\n", "관련 내용으로 표준화되기 시작한 것을 계기로 그 이후 Java를 비롯한 다른 언어들(C#, Python, JavaScript, ...)도 이런 추세를 따르고 있다.\n", "C++에서 *컨테이너*가 Java에서는 *컬렉션*에 해당하며 *이터레이터*는 C++에서도 Java에서도 똑같이 부르고 있다.\n", "다른 언어들도 *컬렉션*(혹은 *컨테이너*)과 *이터레이터*(혹은 *제너레이터*) 이 두 개념을 포함하여 표준라이브러리로 제공하는 경우가 많다.\n", "\n", "참고로 자바 컬렉션 프레임워크에는 없지만 유용한 컬렉션(mutiset 등)을\n", "Guava나 Apache Commons 라이브러리에서 제공하고 있다.\n", "\n", "Stream은 영어 단어 의미로는 *물줄기* 또는 *시냇물*을 의미한다. 이런 단어의 원래 이미지처럼 한번 주르륵 흘러가고 나면 없어지는 대상으로 일련의 물건들을 마치 컨베이어 벨트에서 흘러가면서 처리해서 내보내는 그런 것으로 생각하면 된다. 단 Stream은 실제로 대상을 처리하기 전까지는 실체화를 최대한 미루기 때문에 개념상으로는 매우 많은 객체를 포함하는 기다란 스트림을 정의하더라도 실제로 뽑아서 처리하는 객체들만 실체화되기도 하는데, 이를 게으른(lazy) 계산법을 구현한다고 이야기하기도 한다. `map`, `filter` 등 함수형 프로그래밍의 관용구(idiom)격인 고차함수 메소드를 Java에서는 바로 보통 스트림을 통해 활용한다고 보면 된다." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Iterators" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "String courses[] = {\"데이터구조\", \"객체지향프로그래밍\", \"이산수학\", \"프로그래밍언어\"};" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "자바의 배열 순회를 위해 두 가지 다른 형태의 for 문을 활용할 수 있다." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "데이터구조\n", "객체지향프로그래밍\n", "이산수학\n", "프로그래밍언어\n" ] } ], "source": [ "for (int i = 0; i < courses.length; ++i) { // 전통적인 for 문\n", " System.out.println( courses[i] );\n", "}" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "데이터구조\n", "객체지향프로그래밍\n", "이산수학\n", "프로그래밍언어\n" ] } ], "source": [ "for (String c : courses) { // 자바에 나중에 추가된 방식\n", " System.out.println( c );\n", "}" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "List cs = List.of(\"고급프로그래밍\", \"인공지능\", \"시스템프로그래밍\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "기본적으로 제공되는 배열이 아닌 사용자 정의 데이타 구조도 Iterable 인터페이스를 구현하고 있는\n", "경우에는 위에서 살펴본 두 번째 형태의 for 문을 활용할 수 있다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cs instanceof List" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cs instanceof Collection" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cs instanceof Iterable" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "고급프로그래밍\n", "인공지능\n", "시스템프로그래밍\n" ] } ], "source": [ "for (String c : cs) {\n", " System.out.println( c );\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "고급프로그래밍\n", "인공지능\n", "시스템프로그래밍\n" ] } ], "source": [ "// 위의 for 문을 같은 일을 하는 전통적인 for 문으로 바꿔 작성해보면\n", "for (var i = cs.iterator(); i.hasNext(); /*내용없음*/ ) {\n", " String c = i.next(); //\n", " System.out.println( c );\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "----\n", "## Collections\n", "* `List` : 일렬로 배열된 원소를 포함하는 데이터 구조에 대한 인터페이스. C++ 등 다른 언어에서는 이에 해당하는 개념을 Sequence라는 용어로 부르기도 함.\n", " - ImmutableList\n", " - ArrayList - 배열을 대신해서 쓸 수 있는 (크기를 미리 정해놓지 않은 배열이라고 보면 됩니다)\n", " - LinkedList\n", "* `Set` : 집합(중복 없는 원소들의 모임)을 표현하는 데이터 구조에 대한 인터페이스 \n", " - ImmutableSet\n", " - HashSet\n", " - TreeSet\n", "* `Map` : 키(key)에 대응되는 값(value)을 모아놓은 데이터 구조에 대한 인터페이스\n", " - ImmutableMap\n", " - HashMap\n", " - TreeMap\n", " \n", "참고로 `Map`은 `Collection`이나 `Iterable`의 하위 인터페이스가 아니다!!!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `List`" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "List l1 = List.of(1,2,3,4);" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.ImmutableCollections$ListN" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l1.getClass()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "ename": "EvalException", "evalue": "null", "output_type": "error", "traceback": [ "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1m\u001b[31mjava.lang.UnsupportedOperationException: null\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75)\u001b[0m", "\u001b[1m\u001b[31m\tat .(#23:1)\u001b[0m" ] } ], "source": [ "l1.add(5)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "List l2 = new LinkedList()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2.add(1)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2.add(2)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.LinkedList" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2.getClass()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "List l3 = new LinkedList( List.of(1,2,3) );" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l3" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l3.add(4)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l3" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.LinkedList" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l3.getClass()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "Integer[] arr4 = {1,2,3,4};\n", "\n", "List l4 = Arrays.asList( arr4 ); // 자바 배열로부터 LinkedList를 초기화" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l4;" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.LinkedList" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l2.getClass()" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "List l5 = new ArrayList<>( List.of(1,2,3,4,5) ); // ImmutableList로부터 초기화" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l5" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.ArrayList" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l5.getClass()" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l5.add(6)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5, 6]" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l5" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "import org.apache.commons.lang3.tuple.*;\n", "\n", "List > l6 = List.of( Pair.of(\"one\",1), Pair.of(\"two\",2), Pair.of(\"three\",3) );" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(one,1), (two,2), (three,3)]" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l6" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "List > l7 = List.of( List.of(1,2), List.of(3,4,5), List.of(6,7,8,9) );" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[1, 2], [3, 4, 5], [6, 7, 8, 9]]" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Set`" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "Set s1 = Set.of(1,2,3,4);" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 2, 1, 4]" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.ImmutableCollections$SetN" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1.getClass()" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1.contains(3)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1.contains(5)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s1 instanceof Iterable" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3\n", "2\n", "1\n", "4\n" ] } ], "source": [ "for (Integer v : s1)\n", " System.out.println(v)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "ename": "EvalException", "evalue": "null", "output_type": "error", "traceback": [ "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1m\u001b[31mjava.lang.UnsupportedOperationException: null\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75)\u001b[0m", "\u001b[1m\u001b[31m\tat .(#56:1)\u001b[0m" ] } ], "source": [ "s1.add(5)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "Set s2 = new HashSet<>( Set.of(2,3,4) ); // ImmutableSet으로 초기화" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 3, 4]" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2.add(1)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2.add(3)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2.add(5)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5]" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s2" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "Set s3 = new HashSet<>( Set.of(\"hello\", \"world\", \"hi\", \"wall\") );" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[hi, world, hello, wall]" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s3" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "Set s4 = new TreeSet<>( Set.of(\"hello\", \"world\", \"hi\", \"wall\") );" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[hello, hi, wall, world]" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s4" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [], "source": [ "Set > s5 =\n", " Set.of( Pair.of(\"one\",1), Pair.of(\"two\",2), Pair.of(\"three\",3), Pair.of(\"three\",4) );" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(three,4), (one,1), (two,2), (three,3)]" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s5" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [], "source": [ "Set > s6 = new TreeSet<>( s5 );" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(one,1), (three,3), (three,4), (two,2)]" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s6" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [], "source": [ "Set > s7 = Set.of( List.of(1,2), List.of(2,3,4), List.of(5,6,7,8) );" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[2, 3, 4], [1, 2], [5, 6, 7, 8]]" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "s7" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "ename": "EvalException", "evalue": "class java.util.ImmutableCollections$ListN cannot be cast to class java.lang.Comparable (java.util.ImmutableCollections$ListN and java.lang.Comparable are in module java.base of loader 'bootstrap')", "output_type": "error", "traceback": [ "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1m\u001b[31mjava.lang.ClassCastException: class java.util.ImmutableCollections$ListN cannot be cast to class java.lang.Comparable (java.util.ImmutableCollections$ListN and java.lang.Comparable are in module java.base of loader 'bootstrap')\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.TreeMap.compare(TreeMap.java:1291)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.TreeMap.put(TreeMap.java:536)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.TreeSet.add(TreeSet.java:255)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:352)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.TreeSet.addAll(TreeSet.java:312)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.TreeSet.(TreeSet.java:160)\u001b[0m", "\u001b[1m\u001b[31m\tat .(#73:1)\u001b[0m" ] } ], "source": [ "Set > s8 = new TreeSet<>( s7 ); // 왜 안될까?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### `Map`" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [], "source": [ "Map m1 = Map.of(\"Bob\",13, \"Sam\",16, \"Ted\",17); // key1,value1, key2,value2, ..." ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Sam=16, Bob=13, Ted=17}" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.size()" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "16" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.get(\"Sam\")" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "13" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.get(\"Bob\")" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "ename": "EvalException", "evalue": "null", "output_type": "error", "traceback": [ "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1m\u001b[31mjava.lang.UnsupportedOperationException: null\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.ImmutableCollections$AbstractImmutableMap.put(ImmutableCollections.java:714)\u001b[0m", "\u001b[1m\u001b[31m\tat .(#79:1)\u001b[0m" ] } ], "source": [ "m1.put(\"Paul\",20);" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "class java.util.ImmutableCollections$MapN" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.getClass()" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.get(\"Paul\") == null" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "false" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1 instanceof Iterable" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "ename": "CompilationException", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1m\u001b[30m| \u001b[1m\u001b[30mfor (var x : \u001b[0m\u001b[1m\u001b[30m\u001b[41mm1\u001b[0m\u001b[1m\u001b[30m) System.out.println(x);\u001b[0m", "\u001b[1m\u001b[31mfor-each not applicable to expression type\u001b[0m", "\u001b[1m\u001b[31m required: array or java.lang.Iterable\u001b[0m", "\u001b[1m\u001b[31m found: java.util.Map\u001b[0m", "" ] } ], "source": [ "for (var x : m1) System.out.println(x) // Iterable 인터페이스를 구현하지 않으므로" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Sam=16, Bob=13, Ted=17]" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m1.entrySet()" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "Set > es = m1.entrySet();" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "es instanceof Iterable" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sam=16\n", "Bob=13\n", "Ted=17\n" ] } ], "source": [ "for (var e : es) \n", " System.out.println(e)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sam\n", "Bob\n", "Ted\n" ] } ], "source": [ "for (var e : es) \n", " System.out.println( e.getKey() ) // Map.Entry의 key 얻어오기" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "16\n", "13\n", "17\n" ] } ], "source": [ "for (var e : es) \n", " System.out.println( e.getValue() ) // Map.Entry의 value 얻어오기" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [], "source": [ "Map m2 = new HashMap();" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{}" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2.size()" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [], "source": [ "m2.put(\"Bob\",13);\n", "m2.put(\"Sam\",16);\n", "m2.put(\"Ted\",17);" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Ted=17, Bob=13, Sam=16}" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "16" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2.get(\"Sam\");" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "m2.getClass()" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "m2.put(\"Paul\",20)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Ted=17, Bob=13, Paul=20, Sam=16}" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "17" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2.put(\"Ted\",19)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{Ted=19, Bob=13, Paul=20, Sam=16}" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "19" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m2.get(\"Ted\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`TreeMap` 도 있다 ... 여러분이 스스로" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## Streams\n", "\n", "람다식 등 함수형 프로그래밍의 개념이 활용되는 클래스.\n", "\n", "List, Set과 같은 Collection과 함께 자주 활용된다." ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [], "source": [ "import java.util.stream.*;\n", "\n", "Stream stream1 = Stream.of(1,2,3,4,2,3); // 유한개의 데이터로 스트림 정의" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 2, 3]" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stream1.collect( Collectors.toList() ) // 스트림의 끝에서 정보를 모아 List를 만든다" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "ename": "EvalException", "evalue": "stream has already been operated upon or closed", "output_type": "error", "traceback": [ "\u001b[1m\u001b[31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1m\u001b[31mjava.lang.IllegalStateException: stream has already been operated upon or closed\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)\u001b[0m", "\u001b[1m\u001b[31m\tat java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)\u001b[0m", "\u001b[1m\u001b[31m\tat .(#112:1)\u001b[0m" ] } ], "source": [ "stream1.collect( Collectors.toSet() ) // 이미 한번 흘려보냈기 때문에 더 이상 유효하지 않은 스트림" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [], "source": [ "Stream stream2 = Stream.of(1,2,3,4,2,3); // 유한개의 데이터로 스트림 정의" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4]" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "stream2.collect( Collectors.toSet() ) // 스트림의 끝에서 정보를 모아 Set을 만든다" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 4, 6, 8]" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List.of(1,2,3,4,2,3).stream().map( x -> x*2 ).collect( Collectors.toSet() );" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 4, 6, 8, 4, 6]" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List.of(1,2,3,4,2,3).stream().map( x -> x*2 ).collect( Collectors.toList() );" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [], "source": [ "List l6 = List.of(1,2,3,4,2,3)" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 4, 6, 8]" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l6.stream().map( x -> x*2 ).collect( Collectors.toSet() )" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 2, 3]" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l6" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[16, 1, 4, 9]" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// stream() 메소드를 부를 때마다 새로운 Stream 생성\n", "l6.stream().map( x -> x*x ).collect( Collectors.toSet() )" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 11, 12, 13, 14]" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Stream stream2 = Stream.of(1,2,3,4);\n", "Stream stream3 = Stream.of(11,12,13,14);\n", "\n", "Stream.concat(stream2, stream3).collect( Collectors.toList() )" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[3, 4, 5, 6, 7, 9]" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List l7 = List.of(4,5,6,4,5);\n", "Set s8 = Set.of(3,5,7,9);\n", "\n", "// 리스트 l7과 리스트 s8의 모든 원소를 모아서 집합으로 만들고 싶다\n", "Stream.concat(l7.stream(), s8.stream()).collect( Collectors.toSet() )" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[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, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// 1,2,3,4,... 무한 스트림 (지금 당장 무한한 개수를 계산하는 것은 아님)\n", "Stream stream4 = Stream.iterate( 1, x -> x+1 );\n", "\n", "stream4.limit(100).collect( Collectors.toList() ) // 유한한 개수만큼만 실체화하여 List로 변환" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// 자연수를 제곱한 수들 중에서 앞에서 10개만 모아서 리스트로 변환\n", "Stream.iterate( 1, x -> x+1 ).map(x -> x*x).limit(10).collect( Collectors.toList() )" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// 자연수 중에서 홀수만 선택해 제곱한 수들 중에서 앞에서 10개만 모아서 리스트로 변환\n", "Stream.iterate( 1, x -> x+1 )\n", " .filter(x -> x%2 != 0)\n", " .map(x -> x*x)\n", " .limit(10).collect( Collectors.toList() )" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[java.util.stream.ReferencePipeline$Head@7aaaf743, java.util.stream.ReferencePipeline$Head@31f7a210, java.util.stream.ReferencePipeline$Head@4a15f73f]" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List< List > ll1 = List.of( List.of(1,2), List.of(3,4,5), List.of(6,7,8,9,10) );\n", "\n", "ll1.stream() // Stream< List >\n", " .map( l -> l.stream() ) // Stream< Stream >\n", " .collect( Collectors.toList() ) // List< Stream >" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[1, 2, 1], [2, 3, 4, 3], [3, 5, 7, 9, 5]]" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List< List > ll2 = List.of( List.of(1,2,1),\n", " List.of(2,3,4,3),\n", " List.of(3,5,7,9,5) );\n", "\n", "ll2.stream() // Stream< List >\n", " .map( l -> l.stream() ) // Stream< Stream >\n", " .map( s -> s.collect( Collectors.toList() ) ) // Stream< List >\n", " .collect( Collectors.toList() ) // List< List >" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 1, 2, 3, 4, 3, 3, 5, 7, 9, 5]" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List< List > ll2 = List.of( List.of(1,2,1),\n", " List.of(2,3,4,3),\n", " List.of(3,5,7,9,5) );\n", "\n", "// flatMap은 Stream에 흘러가는 각각의 데이터를 처리해 결과로 스트림을 만들고\n", "// (각각의 스트림을 만드는 방법은 인자로 작성한 람다함수를 각각에 데이터에 호출)\n", "// 그렇게 만들어진 여러개의 모든 스트림을 다 이어붙여서 하나의 스트림으로 구성한다\n", "\n", "ll2.stream() // Stream< List >\n", " .flatMap( l -> l.stream() ) // Stream\n", " .collect( Collectors.toList() ) // List" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" ] }, "execution_count": 117, "metadata": {}, "output_type": "execute_result" } ], "source": [ "List list1 = Stream.iterate(1, x -> x+1).limit(10).collect( Collectors.toList() );\n", "\n", "list1" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Optional[55]" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list1.stream().reduce( (x,y) -> x+y )" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "55" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list1.stream().reduce( (x,y) -> x+y ).get()" ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Optional.empty" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Stream stream11 = Stream.of(); // 직접 비어있는 정수의 스트림 정의 \n", "stream11.reduce( (x,y) -> x+y ) /// 아무것도 없으면" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Optional.empty" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "// 빈 리스트로부터 정수의 스트림을 생성\n", "new LinkedList().stream().reduce( (x,y) -> x+y ) // 아무것도 없으면" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "55" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list1.stream().reduce(0, (x,y) -> x+y)" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "new LinkedList().stream().reduce(0, (x,y) -> x+y)" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1055" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list1.stream().reduce( 1000, (x,y) -> x+y )" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1000" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "new LinkedList().stream().reduce( 1000, (x,y) -> x+y )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// 노트에서 설명하지 않은 것 중에 많이 쓰는 스트림 메소드 forEach 등은 Do it 자바 교재 참고" ] } ], "metadata": { "kernelspec": { "display_name": "Java", "language": "java", "name": "java" }, "language_info": { "name": "java" } }, "nbformat": 4, "nbformat_minor": 4 }