guava를 활용해 Collection 데이터를 변환하는 방법

2013-02-15 18:32

소프트웨어를 개발하다보면 Collection에 담겨있는 객체에서 일부 데이터만 추출해 새로운 Collection으로 변환해야 하는 경우가 있다. 예를 들어 User 객체가 있는데 이 User 객체에서 사용자 이름만 가지는 Collection이 필요한 경우이다.

동적 파이핑 언어에서는 쉽게 처리할 수 있는데 자바에서는 좀 귀찮은 것이 사실이다. 구글에서 제공하는 guava library (http://code.google.com/p/guava-libraries/)를를) 활용해 일정 부분 개선할 수 있지만 이 또한 부족한 것이 사실이다. 그래도 좀 더 깔끔한 방법일 수 있으니 다음과 같이 구현해보자.

Function<SocialUser, String> userToString = new Function<SocialUser, String>() {
    public String apply(SocialUser user) {
        return user.getUserId();
    }
};


Collection<String> userIds = Collections2.transform(searchedUsers, userToString);

자바는 Functional 언어가 아니다보니 위와 같이 처리할 수 밖에 없을 듯하다. 내가 모르는 더 좋은 방법이 있으려나. 아는 분 있으면 공유 좀. 위처럼 새로운 라이브러리 익히는 것보다 기존처럼 그냥 Collection 하나 만들어서 순환하는 것이 더 좋은 분도 있을 듯하다.

9개의 의견 from SLiPP

2013-02-15 19:12
Collection<String> userIds = transform(searchedUsers);


Collection<String> transform(Collection<SocialUser> socialUser) {
	return CollectionUtils.collect(socialUser, new Transformer() {
		@Override
		public Object transform(Object input) {
			return ((SocialUser)input).getUserId();
		}
	});
}

제가 그동안 쓴 코드는 위처럼 apache commons을 써왔는데요, guava를 쓰는 것도 깔끔한 것 같습니다. apache commons를 썼더니 type casting하는 게 지저분하네요.

2013-02-17 22:05

마찬가지 방법으로, Goldman Sachs Collections 에서는 다음과 같이 하고 있네요.

public static final Function<Person, String> TO_LAST_NAME = new Function<Person, String>()
{
    public String valueOf(Person person)
    {
        return person.lastName;
    }
};
MutableList<Person> people = FastList.newListWith(person1, person2, person3);
MutableList<String> sortedLastNames = people.collect(Person.TO_LAST_NAME).sortThis();
System.out.println("Comma separated, sorted last names: " + sortedLastNames.makeString());

방식 자체는 거의 동일할 것으로 보입니다. 일단 이 모습만 놓고보면, 저라면 이런 작업에는 Goldman Sachs Collections를 사용할 듯 합니다.

2013-02-18 09:52

@Kenny Goldman Sachs Collections는 Function을 만드는 과정은 똑같은데 최종적으로 Function을 사용하는 방식이 다르네. 근데 Goldman Sachs Collections와 같은 접근 방식을 취할 경우 Collection Data가 Goldman Sachs Collections의 Collection(위 예에서는 MutableList)로 변환해야 하는 이슈가 있지 않을까? 기존 JDK Collection에 담겨 있는 데이터라면 Guava와 같은 접근 방식이 더 낫지 않을까라는 생각이 든다. 네가 제시한 Goldman Sachs Collections을 활용할 경우 어떤 측면에서 더 좋을까? 나는 Collection 변환 작업이 있어서 더 불편할 거 같은데.. 내가 모르는 뭔가 있는 듯 한데 이야기 좀 해주라.

2013-02-18 15:21

@자바지기

음... 설계차원의 이야기로 넘어가게 되겠네요. (물론, 제가 제시하는 방법이 옳다! 라는건 아닙니다. 코드는 보는 사람마다 다르게 짤 수 있으니까요.) 제가 이해한 부분으로 설명드리면 이렇게 되겠네요.

TO_LAST_NAME 혹은 userToString 혹은 transform은 어디에 속해야 하는가?

이 부분에서, 제 생각은 Person Object의 값을 다루기 때문에, Person 안에 속해야 한다는 의견입니다. 외부에 있을시에, 해당 메소드는 그냥 죽어버리는 경우를 많이 봐서요. 뭐, 실제로도, 해당 메소드는 Person Object 만을 다루기 때문에, 당연히 Person Object 안에 속해야 합니다.

collect 에 관한 이야기

사실, collect / map / transform 은 굉장히 흔한 접근법입니다. 몇몇 언어들은 기본 제공하기도 하고요. (Ruby의 경우, Array 안에 collect / map 등을 사용할 수 있게 되어 있습니다. [http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-collect](http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-collect) Python의 경우에는 Built-in function 으로 제공하네요. [http://docs.python.org/2/library/functions.html#map](http://docs.python.org/2/library/functions.html#map) )
GS-Collection의 Collect Pattern에 대해서는 [http://cloud.github.com/downloads/goldmansachs/gs-collections-kata/GS%20Collections%20Training%20Session%20and%20Kata%202.0.0.pdf](http://cloud.github.com/downloads/goldmansachs/gs-collections-kata/GS%20Collections%20Training%20Session%20and%20Kata%202.0.0.pdf) 에 좀 더 자세한 설명이 나와 있습니다.
일단 제가 문제 삼는 부분은... Collection2 에 현재 Collection과 Collect Function을 넘기는 게 자연스러운가에 대한 부분입니다. A라는 Collection에 collect 적용을 위해 Collection2(혹은 기타 다른 Utility Class)를 통하는건 자연스럽지 않아 보이거든요. A.collect 가 되어야 자연스러운 형태의 코드가 된다고 봅니다. (1. 과 일맥상통하는 이야기라고 봅니다. 해당 행위가 어느 Object에 속해야 하는가?)
따라서, 제 견해로는(그게 FastList건, ArrayList건, 무엇이 되었던 간에) collect method의 위치는 Collection으로 가야 한다는 쪽입니다. (물론, 성능상의 이슈가 있어서 튜닝을 한다면 그건 별개겠지요. 정상적인 코드를 만들고 나서 튜닝을 하는거니까요.)

결론적으로, 두 가지 다 제가 보는 관점에서는 method의 위치 문제 입니다. Goldman Sachs Collections 쪽이 좀 더 납득이 가는 위치에 method가 있는걸로 보여요.

2013-02-18 18:18

@Kenny Function<SocialUser, String> userToString의 위치에 대해서는 딱히 고려해 보지 않았는데 네 이야기 들어보니 그와 같은 접근 방식이 맞겠다는 생각이 든다. 일단 이 부분에 대해서는 공감한다. slipp.net 소스 코드 리팩토링 함 해야겠다.

두번째로 collect 관련해 나 또한 언어적으로 이 기능을 지원한다면 Goldman Sachs Collections와 같이 구현하는 것이 좋겠다는 생각이 든다. 그런데 자바의 경우 JDK Collection API를 쓰고 있는데 Goldman Sachs Collections API를 쓰기 위해 새로운 Collection에 담아야 하는 것이 좀 찜찜하다는 의견이다. 물론 처음부터 Goldman Sachs Collections에 담으면 좋겠지만 프레임워크들을 사용하다보면 그렇지 않은 경우들이 많이 발생하니까.

2013-02-19 10:26

@자바지기 원론적인 의미에서 어느정도 동의 합니다. 하지만, 대부분의 경우에 Java Collection API를 그대로 쓰는 경우는 없어요. 프레임워크를 예로 들어도, 기본적으로 ResultSet으로 돌아온 일종의 HashMap을 변환하는 과정이 들어가게 되니까요. 용도에 맞게 데이터 타입을 변경하는건 일상적인 프로그래밍에서 흔한 일이라, 저라면(^^) 저런 문제가 있는 경우에는 Goldman Sachs Collections를 사용할 것 같다는 의미였습니다~

2013-02-20 21:37

아래와 같은 코드를 SocialUser의 static 메소드로 구현하고 SocialUser.TO_USERID 형태로 구현을 하는 것은 어떨까요?

Function<SocialUser, String> TO_USERID = new Function<SocialUser, String>() { public String apply(SocialUser user) { return user.getUserId(); } };

2013-02-22 12:07

저희 팀 에서는 Collection 객체를 다룰때 lambdaj library 를 보편적으로 사용하고 있습니다.

Collection<String> userIds = ch.lambdaj.Lambda.extract(socialUsers, ch.lambdaj.Lambda.on(SocialUser.class).getUserId());
의견 추가하기

연관태그

← 목록으로