Java Iterator Pattern on Colletions

The Iterator pattern

Iterator 패턴은 Collection을 탐색하는 방법과 클라이언트가 수행할 작업을 분리하는 GoF의 패턴 중 하나입니다. index, cursor, enumeration 등으로도 불립니다.

Iterator는 Active 또는 Passive한 형태로 나뉩니다. Active Iterator는 클라이언트가 Iterator를 생성하고, 다음 아이템의 존재 여부를 확인하고, 다음 아이템을 얻는 작업을 합니다. 반면, Passive Iterator는 이 작업을 내부적으로 수행합니다.

Java는 Iterator 패턴을 이용하여 Collection을 탐색합니다. Active에서 Passive한 형태로 발전했습니다.

Enumeration

초기 Java 1.0 때는 Collection에 Vector와 Hashtable만 있었고, Enumeration을 이용해 탐색하였습니다. Java 5 이후부터는 enum 타입과 헷갈리기 때문에 위 클래스들을 사용하지 않는 것이 좋습니다.

1
2
3
4
5
6
7
8
9
10
Vector names = new Vector();
// ... add some names to the collection
Enumeration e = names.elements();
while (e.hasMoreElements())
{
String name = (String) e.nextElement();
System.out.println(name);
}

Iterator

Java 1.2부터 Collections Framework를 설계하면서 Iterator라는 명확한 이름으로 구현되었습니다.

1
2
3
4
5
6
7
8
9
10
List names = new LinkedList();
// ... add some names to the collection
Iterator i = names.iterator();
while (i.hasNext())
{
String name = (String) i.next();
System.out.println(name);
}

Generics and the Enhanced for-loop

Java 5부터는 Iteratable 인터페이스를 구현한 객체라면 for-each 구문을 이용할 수 있습니다. 기존에는 Active한 형태였지만 이제는 Passive한 형태로 진화하고 있는 과도기입니다. Java 7에서는 Diamond Operator를 이용해 타입 추론이 가능합니다.

1
2
3
4
5
6
7
List<String> names = new LinkedList<String>(); // Java 5
List<String> names = new LinkedList<>(); // Java 7
// ... add some names to the collection
for (String name : names)
System.out.println(name);

forEach

Java 8에서는 Iterable 인터페이스에 default 메소드로 forEach 메소드를 제공합니다. forEach 메소드의 인자로 lambda 표현식이 들어갑니다. 기존에는 ‘반복’이라는 개념을 명시적으로 표현했다면, forEach는 이것을 암묵적으로 표현합니다.

1
2
3
4
5
List<String> names = new LinkedList<>();
// ... add some names to the collection
names.forEach(name -> System.out.println(name));

Stream API

Java 8에서는 stream 메소드를 이용해 컬렉션을 다룰 수 있습니다. filter(), distinct(), sorted(), map() 등의 중간 연산과 count(), average(), sum(), max(), forEach() 등의 종단 연산을 연결할 수 있습니다. 각 아이템에 대한 연산을 병렬화 하려면 parallelStream() 메소드를 이용하면 됩니다.

1
2
3
4
5
6
7
List<String> names = new LinkedList<>();
// ... add some names to the collection
long count = names.stream()
.filter(name -> name.startsWith("A"))
.count();

Performance

Active와 Passive 사이에는 큰 차이가 없습니다.
LinkedList, LinkedHashSet은 stream과 parallelStream 사이에는 큰 차이가 없습니다.(순차 참조 자료 구조)
ArrayList, TreeSet, HashSet은 parallelStream이 성능이 좋습니다.(직접 참조 자료 구조)

Reference

Iterating over collections in Java 8

Share Comments

최대 공약수와 최소 공배수

tumblr_inline_nhrijmBsV31rubmdv.jpg
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
public class GCDLCM {
public static void main(String[] args) {
System.out.println(gcd(24, 18));
System.out.println(lcm(24, 18));
}
public static int gcd(int a, int b) {
int c = a % b;
if (c == 0) {
return b;
}
return gcd(b, c);
}
public static int lcm(int a, int b) {
int m = a, n = b;
int i = 1, j = 1;
while (m != n) {
if (m < n) {
m = a * ++i;
} else {
n = b * ++j;
}
}
return m;
}
}
Share Comments

로마숫자

tumblr_inline_nhri7qnong1rubmdv.jpg
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
public class RomaNum {
private char[] signs = {
'I', 'V', 'X', 'L', 'C', 'D', 'M'
};
private int[] values = {
1, 5, 10, 50, 100, 500, 1000
};
public String toRomaNum(int digitNum) {
StringBuilder sb = new StringBuilder();
String s = Integer.toString(digitNum);
for (int i = 0; i < s.length(); i++) {
int d = s.length() - i - 1;
char ch = s.charAt(i);
switch (ch) {
case '1':
sb.append(signs[d * 2]);
break;
case '2':
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
break;
case '3':
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
break;
case '4':
sb.append(signs[d * 2]);
sb.append(signs[d * 2 + 1]);
break;
case '5':
sb.append(signs[d * 2 + 1]);
break;
case '6':
sb.append(signs[d * 2 + 1]);
sb.append(signs[d * 2]);
break;
case '7':
sb.append(signs[d * 2 + 1]);
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
case '8':
sb.append(signs[d * 2 + 1]);
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
sb.append(signs[d * 2]);
case '9':
sb.append(signs[d * 2]);
sb.append(signs[d * 2 + 2]);
break;
default:
break;
}
}
return sb.toString();
}
public int toDigitNum(String romaNum) {
int[] n = new int[romaNum.length() * 3];
int sum = 0;
for (int i = 0; i < romaNum.length(); i++) {
char sign = romaNum.charAt(i);
for (int j = 0; j < signs.length; j++) {
if (sign == signs[j]) {
n[i] = values[j];
break;
}
}
}
for (int i = 1; i < n.length; i++) {
if (n[i - 1] < n[i]) {
sum += n[i] - n[i - 1];
i++;
} else {
sum += n[i - 1];
}
}
return sum;
}
public static void main(String[] args) {
RomaNum rn = new RomaNum();
System.out.println(rn.toRomaNum(2493));
System.out.println(rn.toDigitNum("MMMCCCXXXIII"));
}
}

예전 블로그에 있던 소스를 옮기고 있는데 지금 보니 무슨 생각으로 짰는지 모르겠다;; ㅎㄷㄷ

Share Comments

PlayN Game Loop

PlayN은 게임의 메인 루프 진행을 위한 단순한 인터페이스를 제공한다. 이것은 플랫폼 간의 타이밍 구현의 복잡성을 분리하고, 논리적인 업데이트와 렌더링 업데이트의 분리를 추상화한다. 이 글은 PlayN의 구현과 게임의 메인 업데이트 사이클 예제를 설명한다.

게임 루프의 진행

모든 PlayN 게임은 단순히 update와 paint 메소드를 포함한 Game 인터페이스를 구현한다. Game 인터페이스를 구현하고 PlayN.run(game)을 호출하면 다음 두 메소드를 영원히 호출하는 게임 루프를 통제하는 것을 포기해야 한다.

1
2
3
4
while (true) {
game.update(...);
game.paint(...);
}

물론 가능한 한 빠르게 동작하지는 않지만, 플랫폼이 실제로 프레임을 표시할 수 있는 정도로 제한된다. 프레임이 얼마나 빨리 표시 되는지는 갱신률이나, 플랫폼 특유의 최대 표시율에 따라 제한된다.
그러나 두 개의 특정 주기인 표시 주기와 갱신 주기를 고려해야 한다. 상대적으로 단순한 갱신 로직을 가진 게임은 다음과 같이 만들 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyGame implements Game {
public void update(float delta) {
// Update the game's state.
// 'delta' is the time in milliseconds since the last update call.
}
public void paint(float alpha) {
// Paint using the game's current state.
// 'alpha' will always be zero. Ignore it.
}
public int updateRate() {
// Returning zero here explicitly requests an update() call for each frame.
return 0;
}
}

원문: https://developers.google.com/playn/devguide/gameloop

Share Comments

PlayN 개요

최근 게임 개발 기술은 비약적으로 발전했다. 게임 개발자는 다양한 기술과 새로운 게임 플랫폼으로 개발하는 사람을 말한다. 게임 만들기는 쉬워지지 않았지만, 소셜, 모바일, 웹 기술의 발전이 많은 사람들이 게임을 하는 것을 쉽게 만들어줬다.

하지만 문제가 있다. 플랫폼이 너무 많다. 게임 개발자는 많은 사람들이 손안에서 게임하기를 원하지만 사용자는 통제되지 않는다. 그래서 게임을 각 플랫폼에 맞게 만들어야 하고, 시간 및 비용의 낭비, 유지보수의 어려움 등의 문제가 있다. 이런 이유로 PlayN이 태어났다.

Modern game developers have to worry about many platforms.

PlayN은 오픈소스 크로스 플랫폼 추상 레이어이다. GWT 컴파일러를 이용해 자바로 작성된 하나의 코드 베이스를 HTML5, Flash, Java, 안드로이드 어플로 컴파일한다. PlayN 아키텍처는 서비스-프로바이더 인터페이스 패턴을 사용하는데, 코어 부분은 코어 API 집합을 가지고 있고, 나머지 각 타겟에 구현이 분리되어 있다. PlayN을 사용하면 소스 코드에서 플랫폼 종속적인 호출은 제거되고, 하나의 코드 베이스에서 각 플랫폼 종속적인 구현의 결과물이 만들어진다.

PlayN API service provider interface design

PlayN은 게임 엔진이 아니다. 같은 게임을 여러 플랫폼으로 컴파일해주는 라이브러리이다. 많은 부분에서 각 플랫폼의 구현 코드만 가지고 있다.

PlayN은 asset 툴체인, 애니메이션 효과, 추상 게임 로직 등을 가지고 있지 않다.

PlayN의 목표는 update/render 루프, 유저 입력 처리, 리소스 관리 같이 일반적인 게임 오퍼레이션을 위한 크로스 컴파일 코드를 지원하는 기본 기술을 제공하는 것이다. 이는 핵심 라이브러리의 크기를 작게 유지하고, 특정 게임을 위한 코드 팽창을 피할 수 있다.

원문: https://developers.google.com/playn/overview

Share Comments

완전제곱수

문제
public class SQR{

    public static void main(String[] args){

        sqr(60, 100);
    }

    public static void sqr(double min, double max){

        int low = (int)Math.ceil(Math.sqrt(min));
        int high = (int)Math.floor(Math.sqrt(max));

        int sum = 0;

        for(int i = low; i <= high; i++){
            sum += i*i;
        }

        System.out.println(sum);
        System.out.println(low * low);
    }
}
Share Comments

삽입 정렬

삽입 정렬

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
public class InsertionSort{
public int[] insertionSort(int[] s){
int n = s.length;
int val; // 임시 저장 공간
int j;
for(int i = 1; i < n; i++){
// 현재 위치의 요소를 임시로 저장
val = s[i];
j = i - 1;
// 현재 위치 앞의 요소보다 작다면 서로 위치를 바꿈
while((j >= 0) && (val < s[j])){
s[j + 1] = s[j];
j--;
}
s[j + 1] = val;
}
return s;
}
// 배열을 출력하는 메소드
public void display(int[] s){
for(int i = 0; i < s.length; i++){
System.out.print(s[i] + " ");
}
System.out.println();
}
public static void main(String[] args){
InsertionSort s = new InsertionSort();
s.display(s.insertionSort(new int[]{8, 13, 20, 27, 16}));
}
}
Share Comments

개미 수열

개미 수열

public class Ant{
    public static int[] array1;
    public static int[] array2;

    public static void main(String[] args){

        int length = Integer.parseInt(args[0]);

        array1 = new int[50];
        array1[0] = 1;

        for(int i = 0; i < length; i++){
            for(int j = 0; j < array1.length; j++){
                if(array1[j] != 0){
                    System.out.print(array1[j]+" ");
                }
            }
            array1 = makeNextLine(array1);
            System.out.println();
        }
    }

    public static int[] makeNextLine(int[] array1){

        int[] array2 = new int[50];
        array2[0] = array1[0];
        int index = 1;
        int count = 1;

        for(int i = 1; i < array1.length; i++){
            if(array1[i-1] == array1[i]){
                count++;
            }else{
                array2[index] = count;
                index++;
                array2[index] = array1[i];
                index++;
                count = 1;
            }
        }
        return array2;
    }
}
Share Comments

피보나치 수열

피보나치 수열

public class Fibonacci{

    // 피보나치 수열을 출력하는 메소드
    public void fibonacciSequence(int n){ // n은 수열의 길이

        for(int i = 0; i < n; i++){
            System.out.print(getFibonacciNum(i + 1) + " ");
        }
        System.out.println();
    }

    // 인자로 받은 위치의 피보나치 수를 반환하는 메소드
    private int getFibonacciNum(int i){

        if(i == 1){
            return 1; // 첫번째 요소이면 1
        }else if(i == 2){
            return 2; // 두번째 요소이면 2
        }

        // 현재의 수는 앞의 수와 그 앞의 수의 합
        return getFibonacciNum(i - 1) + getFibonacciNum(i - 2);
    }

    public static void main(String[] args){

        Fibonacci f = new Fibonacci();
        f.fibonacciSequence(10);
    }
}
Share Comments

Coffeescript Game Tutorial the Mouse

마우스 이벤트 처리 코드를 추가하였다.

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
init_mouse = ->
canvasMinX = 0
canvasMaxX = canvasMinX + WIDTH
document.onmousemove = (evt) ->
if evt.pageX > canvasMinX and evt.pageX < canvasMaxX
paddlex = evt.pageX - canvasMinX
draw = ->
clear()
circle(x, y, 10)
# move the paddle if left or right is currently pressed
paddlex += 5 if rightDown
paddlex -= 5 if leftDown
rect(paddlex, HEIGHT-paddleh, paddlew, paddleh)
dx = -dx if x + dx > WIDTH or x + dx < 0
if y + dy < 0 then dy = -dy
else if y + dy > HEIGHT
if x > paddlex and x < paddlex + paddlew
dy = -dy
else
# game over, so stop the animation
clearInterval(intervalId)
x += dx;
y += dy;
window.onload = ->
init()
init_mouse()

Reference

Share Comments