プログラマ的京都生活

2010年07月25日

[]guava

いつのまにやら、google-collectionsがguavaに統合されていた。現時点でのバージョンはr06。guavaはGoogleの多くのJavaベースのプロジェクトで使用されているjava core libraryとのこと。

パッケージ名を見れば大体の機能について想像がつく。

見てすぐ気付くのはcommons-langやcommons-collectionsなどcommons系と機能が重複しているということ。違いといえば、以下のような点でしょうか。

  • commons-collectionsの最新版は3.2.1だが2008/04で開発が止まっているっぽい。guavaは今も開発が続いている。
  • ジェネリクスをサポート(ゆえに1.5以上が必要)
  • fluent interfaceベースのプログラミング
  • 1つのjarで上記パッケージをカバーしているので、lib以下を汚さない

guavaの方が今っぽいJavaプログラミングが可能になります。けどcommonsと比べると機能は少ないと思いますので詳しくはjavadocを読んで確認しておく必要があると思います。


とりあえず個人的にnetとconcurrentは使用頻度が少なそうなので以下では省略。


com.google.common.annotations

4つ提供されていますが、普段使いできそうなのは1つかな。

VisibleForTestingは、メソッドやフィールドに付与するもので、テストをしやすくするためにアクセス修飾子のレベルを緩くしているということを明示するために付与する。テストのためにprivateをパッケージプライベートに変更したりってことはよくするので、このアノテーションを利用するのはよさそう。


com.google.common.primitives

Booleans、Bytes、Chars、Doubles、、、というお決まりのプリミティブ型に対するユーティリティメソッドを提供するクラスが含まれています。byteに関しては、Bytes、SignedBytes、UnsignedBytesなんかがありました。簡単な例を以下に。

int[] ary = new int[] { 1, 2, 3 };

List<Integer> list = Ints.asList(ary);          // 配列からListへの変換
int max = Ints.max(ary); 			// 3
String joinedStr = Ints.join(" : ", ary); 	// 1 : 2 : 3

com.google.common.base

基礎となるユーティリティクラスが含まれるパッケージ。大きく分けて、「String関連」と「Functional型」と「その他」の3つに分類される(javadocより)。

■String関連

Stringsにはこんなのがあります。

if ( Strings.isNullOrEmpty(str); ) {

とか

String repeat = Strings.repeat("*", 10);    // **********

とか。

他には、こんなのも。

String str1 = Strings.padStart("7", 2, '0');	// 07
String str2 = Strings.padStart("12", 2, '0');	// 12

第1引数文字列が最低何文字である必要があるかというのを第2引数で指定して、それを満たしていない時は第3引数の文字で最初を埋めるというもの。

これと対となる、Strings#padEndがあります。


Joinerというデータを区切り文字で連結して一つの文字列にするクラスがある。引数はリストでも配列でもOK!

String[] animals = new String[]{"dog", "cat", null, "pig"};
String str = Joiner.on(", ").skipNulls().join(animals);        // dog, cat, pig

Mapだっていけちゃいます。

Map<String, String> dictionary = new HashMap<String, String>();
dictionary.put("米", "rice");
dictionary.put("パン", "bread");
dictionary.put("うどん", null);
		
MapJoiner joiner = Joiner.on(", ").withKeyValueSeparator(":").useForNull("登録なし");
String str = joiner.join(dictionary);        // 米:rice, パン:bread, うどん:登録なし

Splitterってのもあります。CharMatcher(これもbaseパッケージの文字列系クラス)との合わせ技で。

String telnum = "090 1234 5678";        // もしくは090-1234-5678に対応

CharMatcher matcher = CharMatcher.WHITESPACE.or(CharMatcher.is('-'));
Iterable<String> splits = Splitter.on(matcher).split(telnum);       // 090,1234,5678という長さ3のリスト

onの引数のところはCharMatcherだけじゃなくて正規表現パターンなんかも使えます。が、CharMatcherを使ったほうが可読性は上がると思います。正規表現は複雑になると呪文のようになるので。

また、String#splitを使うと上記例はこのようになります。

String[] split = telnum.split("\\s|-");

シンプルな時はこれで十分だと思います。でもこの例であっても例えば上記例のtelnumが「090 1234 5678」とスペースが1つじゃなくて3つ連続してしまうと、長さが3ではなく長さ5の配列になってしまいます。こんな時もSplitterに一つ設定を加えてやると余計なスペースを取り除けます。

Iterable<String> splits = Splitter.on(matcher).omitEmptyStrings().split(telnum);       // 090,1234,5678という長さ3のリスト

■Function系

単独で説明しても分かりにくい。com.google.common.collectと組み合わせることで分かりやすくなるので、あとで。


■その他

Objectsってのがあります。hashCodeの実装やtoStringの実装に便利なメソッドがあります。

public int hashCode() {
    return Objects.hashCode(getX(), getY(), getZ());
}

public String toString() {
  return Objects.toStringHelper(this).add("x", x).add("y", y).toString();
}

Preconditionsを使うことで防御的プログラミングを促進します。

public void setRating(StarRating rating) {
    this.rating = checkNotNull(rating, "引数にnullは設定できません[rating]");        // NullPointerExceptionをthrow
}

com.google.common.collect

google collections library。標準のJDKには含まれていないcollectionの実装クラスをまとめる。

クラス名特徴
MultiSet同じ値を持つことができるSet
MultiMapキーに対する値を複数もつことができるmap。値の管理の仕方でListMultiMapとSetMultiMapを選択できる。
BiMapキーだけでなく値もユニークさを保証するmap。BiMap#inverseで、キーと値が反転する。

特にMultiMapは多用しそうです。keyが含まれていなければ、、というチェックもいらないし、単にputするだけでCollectionに追加されるし。以下例。

// 旧来
if (!map.contains(k)) {
    map.put(k, new ArrayList());
}
map.get(k).add(v);

// MultiMap
map.put(k, v);

なお、宣言はこんな感じ。

private Multimap<String, String> info = ArrayListMultimap.create();

あとはCollection系の宣言部分が簡素化されていていい感じです。

List<String> l1 = Lists.newArrayList();    // ジェネリクス宣言は型宣言時の1回だけ
List<String> l2 = Lists.newArrayList("dog", "cat", "pig");    // 配列みたいに型と初期値を同時に宣言できる。
final List<String> l3 = ImmutableList.of("dog", "cat", "pig");    // 初期値を設定するなら多分immutableな場合が多いのでは。そんな時はこれ。

final Map<String, String> m1 = ImmutableMap.of("dog", "犬", "cat", "猫");    // Mapも型と初期値を同時に宣言でき、かつimmutableに。k,v,k,v...

では、この辺で先のbaseパッケージで飛ばしたFunction系をやります。

■Predicate<T>

英辞郎で訳してみたら述部、述語という意味らしい。気持ちは分かるけどちょっと分かりにくいな。コード見たほうが早い。

インタフェースはシンプルで、入力値に対してtrueかfalseを返す実装すればよい。

boolean apply(T input)

といってもまだ分かりにくいですね。Collectionとの組み合わせ例を。

List<Integer> list = Ints.asList(1,2,3,4,5);
List<Integer> evenList = (List<Integer>)Collections2.filter(list, new Predicate<Integer>() {
	@Override
	public boolean apply(Integer input) {
		return input % 2 == 0;
	}
});

filteringするための条件部を構成するんですね。だから述部っていう意味なんでしょうね。

Predicateは無名クラスにするよりちゃんとクラス定義して定数とかにしてしまう方が可読性が上がりますね。

List<Integer> evenList = (List<Integer>)Collections2.filter(list, EVEN_PREDICATE);

■Function<F,T>

実装しないといけないメソッドはこんな感じ。inputに対して何かしらの処理をして返す。

T apply(F input)

Collectionとの組み合わせ例を。

List<Integer> list = Ints.asList(1,2,3,4,5);
List<Integer> negativeList = Lists.transform(list, new Function<Integer, Integer>() {    // -1,-2,-3,-4,-5
	@Override
	public Integer apply(Integer input) {
		return -1 * input;
	}			
});

PredicateだけでなくFunctionも無名クラスとして宣言するよりはちゃんとクラス定義して再利用を促す方向で使うのがいいと思います。


com.google.common.io

■Files

BufferedReaderの作成が比較的容易。

// 旧来
BufferedReader reader = new BufferedReader(new FileReader(getFile()));

// Files
BufferedReader reader = Files.newReader(getFile(), Charsets.UTF_8);

あとは行関連。

すべての行をListで取得。その後、foreachで自分で処理するタイプ。

List<String> lines = Files.readLines(getFile(), Charsets.UTF_8);

こっちは処理も同時にやっちゃう。「a」から始まる行のみを抽出して返す。processLineでfalseを返すとその行で処理を終了する。この例ではtrueにしているので、取りあえず全行処理する。

String lines = Files.readLines(getFile(), Charsets.UTF_8, new LineProcessor<String>() {
	private List<String> lines = Lists.newArrayList();
				
	@Override
	public boolean processLine(String line) throws IOException {
		if(line.startsWith("a")) {
			lines.add(line);
		}
		return true;
	}

	@Override
	public String getResult() {
		return Joiner.on(",").join(lines);
	}
});

あとはcopyとかmoveとかtouchとかUnixライクなメソッドがあります。


■Resources

// 旧来
URL resource = this.getClass().getClassLoader().getResource("sample.txt");

// Resources
URL resource = Resources.getResource("sample.txt");

取りあえず以上で終了です。

kuronekojimakuronekojima 2011/02/03 14:32 List<Integer> evenList = (List<Integer>)Collections2.filter(list, new Predicate<Integer>() {

上記のコードでClassCastExceptionが発生するようです。Collections2.filterの戻り値がCollection<T>のため、Listへは直接キャストできないようでした。
ですのでfilterしたCollection<T>をLists.newArrayList()の引数で与える形になりますかね。
もっとスマートな方法があるといいのですが…。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

Connection: close