グアバ19:何が新しいの?

グァバ19:新しい何ですか?

1. 概要

Google Guavaは、Java開発を容易にするユーティリティをライブラリに提供します。 このチュートリアルでは、Guava 19 releaseで導入された新機能を見ていきます。

2. common.baseパッケージの変更

2.1. CharMatcher静的メソッドを追加しました

CharMatcherは、その名前が示すように、文字列が一連の要件に一致するかどうかを確認するために使用されます。

String inputString = "someString789";
boolean result = CharMatcher.javaLetterOrDigit().matchesAllOf(inputString);

上記の例では、resulttrueになります。

CharMatcherは、文字列を変換する必要がある場合にも使用できます。

String number = "8 123 456 123";
String result = CharMatcher.whitespace().collapseFrom(number, '-');

上記の例では、resultは「8-123-456-123」になります。

CharMatcherを使用すると、特定の文字列内の文字の出現回数をカウントできます。

String number = "8 123 456 123";
int result = CharMatcher.digit().countIn(number);

上記の例では、resultは10になります。

以前のバージョンのGuavaには、CharMatcher.WHITESPACECharMatcher.JAVA_LETTER_OR_DIGITなどのマッチャー定数があります。

Guava 19では、これらは同等の方法(それぞれ、CharMatcher.whitespace()およびCharMatcher.javaLetterOrDigit())に置き換えられました。 これは、CharMatcherが使用されるときに作成されるクラスの数を減らすために変更されました。

静的ファクトリメソッドを使用すると、必要な場合にのみクラスを作成できます。 将来のリリースでは、マッチャー定数は廃止され、削除される予定です。

Throwables2.2. lazyStackTraceメソッド

このメソッドは、指定されたThrowableのスタックトレース要素(行)のListを返します。 一部のみが必要な場合は、完全なスタックトレース(Throwable.getStackTrace())を反復処理するよりも高速になる可能性がありますが、完全なスタックトレースを反復処理する場合は低速になる可能性があります。

IllegalArgumentException e = new IllegalArgumentException("Some argument is incorrect");
List stackTraceElements = Throwables.lazyStackTrace(e);

3. common.collectパッケージの変更

3.1. FluentIterable.toMultiset()を追加

前の例の記事Whats new in Guava 18では、FluentIterableを調べました。 toMultiset()メソッドは、FluentIterableImmutableMultiSetに変換する必要がある場合に使用されます。

User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};
ImmutableMultiset users = FluentIterable.of(usersArray).toMultiset();

Multisetは、Setと同様に、順序に依存しない同等性をサポートするコレクションです。 SetMultisetの主な違いは、Multisetに重複する要素が含まれている可能性があることです。 Multisetは、同じ単一要素のオカレンスとして等しい要素を格納するため、Multiset.count(java.lang.Object)を呼び出して、特定のオブジェクトのオカレンスの総数を取得できます。

いくつかの例を見てみましょう。

List userNames = Arrays.asList("David", "Eugen", "Alex", "Alex", "David", "David", "David");

Multiset userNamesMultiset = HashMultiset.create(userNames);

assertEquals(7, userNamesMultiset.size());
assertEquals(4, userNamesMultiset.count("David"));
assertEquals(2, userNamesMultiset.count("Alex"));
assertEquals(1, userNamesMultiset.count("Eugen"));
assertThat(userNamesMultiset.elementSet(), anyOf(containsInAnyOrder("Alex", "David", "Eugen")));

重複する要素の数を簡単に判断できます。これは、標準のJavaコレクションよりもはるかにクリーンです。

3.2. RangeSet.asDescendingSetOfRanges()asDescendingMapOfRanges()を追加しました

RangeSetは、空でない範囲(間隔)で動作するために使用されます。 RangeSetは、切断された空でない範囲のセットとして説明できます。 空でない新しい範囲をRangeSetに追加すると、接続されている範囲はすべてマージされ、空の範囲は無視されます。

新しい範囲を構築するために使用できるいくつかのメソッドを見てみましょう:Range.closed()Range.openClosed()Range.closedOpen()Range.open()

それらの違いは、オープンレンジにはエンドポイントが含まれていないことです。 彼らは数学で異なる指定を持っています。 開いた間隔は「(」または「)」で示され、閉じた範囲は「[」または「]」で示されます。

たとえば、(0,5)は「0より大きく5未満の任意の値」を意味し、(0,5)は「0より大きく5以下の任意の値」を意味します。

RangeSet rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));

ここでは、RangeSetに範囲[1、10]を追加しました。 そして今、新しい範囲を追加することでそれを拡張したい:

rangeSet.add(Range.closed(5, 15));

これらの2つの範囲は5で接続されていることがわかります。したがって、RangeSetはそれらを新しい単一の範囲[1、15]にマージします。

rangeSet.add(Range.closedOpen(10, 17));

これらの範囲は10で接続されているため、マージされて、閉じた状態から開いた状態になります[1、17)。 containsメソッドを使用して、値が範囲に含まれているかどうかを確認できます。

rangeSet.contains(15);

範囲[1,17)には15が含まれているため、これはtrueを返します。 別の値を試してみましょう:

rangeSet.contains(17);

範囲[1,17)には上限エンドポイント17が含まれていないため、これはfalseを返します。 enclosesメソッドを使用して、範囲が他の範囲を囲んでいるかどうかを確認することもできます。

rangeSet.encloses(Range.closed(2, 3));

範囲[2,3]が完全に範囲[1,17)内にあるため、これはtrueを返します。

Range.greaterThan()Range.lessThan()Range.atLeast()Range.atMost()など、間隔を操作するのに役立つメソッドが他にもいくつかあります。 最初の2つは開いた間隔を追加し、最後の2つは閉じた間隔を追加します。 例えば:

rangeSet.add(Range.greaterThan(22));

これにより、RangeSetに新しい間隔(22、+∞)が追加されます。これは、他の間隔との接続がないためです。

asDescendingSetOfRangesRangeSetの場合)やasDescendingMapOfRangesRangeSetの場合)などの新しいメソッドを使用して、RangeSetSetに変換できます。 sまたはMap

3.3. Lists.cartesianProduct(List…)Lists.cartesianProduct(List<List>>)を追加しました

デカルト積は、2つ以上のコレクションの可能なすべての組み合わせを返します。

List first = Lists.newArrayList("value1", "value2");
List second = Lists.newArrayList("value3", "value4");

List> cartesianProduct = Lists.cartesianProduct(first, second);

List pair1 = Lists.newArrayList("value2", "value3");
List pair2 = Lists.newArrayList("value2", "value4");
List pair3 = Lists.newArrayList("value1", "value3");
List pair4 = Lists.newArrayList("value1", "value4");

assertThat(cartesianProduct, anyOf(containsInAnyOrder(pair1, pair2, pair3, pair4)));

この例からわかるように、結果のリストには、提供されたリストのすべての可能な組み合わせが含まれます。

3.4. Maps.newLinkedHashMapWithExpectedSize(int)を追加

標準のLinkedHashMapの初期サイズは16です(これはLinkedHashMapのソースで確認できます)。 負荷率がHashMap(デフォルトでは0.75)に達すると、HashMapは再ハッシュしてサイズを2倍にします。 ただし、HashMapが多くのキーと値のペアを処理することがわかっている場合は、16より大きい初期サイズを指定して、繰り返しの再ハッシュを回避できます。

LinkedHashMap someLinkedMap = Maps.newLinkedHashMapWithExpectedSize(512);

3.5. Multisets.removeOccurrences(Multiset, Multiset)を再追加

このメソッドは、Multisetで指定されたオカレンスを削除するために使用されます。

Multiset multisetToModify = HashMultiset.create();
Multiset occurrencesToRemove = HashMultiset.create();

multisetToModify.add("John");
multisetToModify.add("Max");
multisetToModify.add("Alex");

occurrencesToRemove.add("Alex");
occurrencesToRemove.add("John");

Multisets.removeOccurrences(multisetToModify, occurrencesToRemove);

この操作の後、「最大」のみがmultisetToModifyに残ります。

multisetToModifyに特定の要素の複数のインスタンスが含まれ、occurrencesToRemoveにその要素のインスタンスが1つしかない場合、removeOccurrencesは1つのインスタンスのみを削除することに注意してください。

4. common.hash Package Changes

4.1. Hashing.sha384()を追加しました

Hashing.sha384()メソッドは、SHA-384アルゴリズムを実装するハッシュ関数を返します。

int inputData = 15;

HashFunction hashFunction = Hashing.sha384();
HashCode hashCode = hashFunction.hashInt(inputData);

15のSHA-384は「0904b6277381dcfbddd…2240a621b2b5e3cda8」です。

4.2. Hashing.concatenating(HashFunction, HashFunction, HashFunction…)Hashing.concatenating(Iterable<HashFunction>)を追加しました

Hashing.concatenatingメソッドを使用して、一連のハッシュ関数の結果を連結します。

int inputData = 15;

HashFunction crc32Function = Hashing.crc32();
HashCode crc32HashCode = crc32Function.hashInt(inputData);

HashFunction hashFunction = Hashing.concatenating(Hashing.crc32(), Hashing.crc32());
HashCode concatenatedHashCode = hashFunction.hashInt(inputData);

結果のconcatenatedHashCodeは、「4acf27794acf2779」になります。これは、それ自体と連結されたcrc32HashCode(「4acf2779」)と同じです。

この例では、わかりやすくするために単一のハッシュアルゴリズムを使用しました。 ただし、これは特に役立ちません。 2つのハッシュ関数を組み合わせると、ハッシュをより強力にする必要がある場合に便利です。ハッシュ関数は、2つのハッシュが壊れている場合にのみ壊れる可能性があるためです。 ほとんどの場合、2つの異なるハッシュ関数を使用します。

5. common.reflectパッケージの変更

5.1. TypeToken.isSubtypeOfを追加

TypeTokenは、実行時でもジェネリック型を操作およびクエリするために使用され、problems due to type erasureを回避します。

Javaは実行時にオブジェクトのジェネリック型情報を保持しないため、特定のオブジェクトにジェネリック型があるかどうかを知ることはできません。 しかし、リフレクションの助けを借りて、メソッドまたはクラスの汎用タイプを検出できます。 TypeTokenはこの回避策を使用して、追加のコードなしでジェネリック型を操作およびクエリできるようにします。

この例では、TypeTokenメソッドisAssignableFromがないと、ArrayList<String>ArrayList<Integer>から割り当てられなくても、trueが返されることがわかります。

ArrayList stringList = new ArrayList<>();
ArrayList intList = new ArrayList<>();
boolean isAssignableFrom = stringList.getClass().isAssignableFrom(intList.getClass());

この問題を解決するために、TypeTokenを使用してこれを確認できます。

TypeToken> listString = new TypeToken>() { };
TypeToken> integerString = new TypeToken>() { };

boolean isSupertypeOf = listString.isSupertypeOf(integerString);

この例では、isSupertypeOfはfalseを返します。

以前のバージョンのGuavaには、この目的のためのメソッドisAssignableFromがありましたが、Guava 19の時点では、isSupertypeOfを優先して非推奨になっています。 さらに、メソッドisSubtypeOf(TypeToken)を使用して、クラスが別のクラスのサブタイプであるかどうかを判別できます。

TypeToken> stringList = new TypeToken>() { };
TypeToken list = new TypeToken() { };

boolean isSubtypeOf = stringList.isSubtypeOf(list);

ArrayListListのサブタイプであるため、期待どおり、結果はtrueになります。

6. common.ioパッケージの変更

6.1. ByteSource.sizeIfKnown()を追加

このメソッドは、データストリームを開かずに、ソースのサイズをバイト単位で返します(特定できる場合)。

ByteSource charSource = Files.asByteSource(file);
Optional size = charSource.sizeIfKnown();

6.2. CharSource.length()を追加

以前のバージョンのGuavaには、CharSourceの長さを決定する方法がありませんでした。 これで、この目的でCharSource.length()を使用できます。

6.3. CharSource.lengthIfKnown()を追加

ByteSource,の場合と同じですが、CharSource.lengthIfKnown()を使用すると、ファイルの長さを文字数で決定できます。

CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
Optional length = charSource.lengthIfKnown();

7. 結論

Guava 19では、成長するライブラリに多くの便利な追加と改善が導入されました。 次のプロジェクトでの使用を検討する価値があります。

この記事のコードサンプルは、GitHub repositoryで入手できます。