多くの文字列比較を行い、比較的少ない文字列操作を行い、別のテーブルを使用して文字列を識別子にマップし、効率的な等価性とメモリフットプリントの削減を実現する、文字列を多用するプログラムをいくつか見たことを思い出します。
public class Name {
public static Map<String, Name> names = new SomeMap<String, Name>();
public static Name from(String s) {
Name n = names.get(s);
if (n == null) {
n = new Name(s);
names.put(s, n);
}
return n;
}
private final String str;
private Name(String str) { this.str = str; }
@Override public String toString() { return str; }
// equals() and hashCode() are not overridden!
}
これらのプログラムの 1 つが OpenJDK の javac であったことは確かなので、おもちゃのアプリケーションではありません。もちろん、実際のクラスはもっと複雑でした(また、CharSequence を実装していると思います)が、おわかりのように、プログラム全体Name
が予想されるあらゆる場所に散らばっていString
て、文字列操作が必要なまれなケースでは、変換されました。概念的には次のように、文字列に変換してから再度キャッシュしました。
Name newName = Name.from(name.toString().substring(5));
私はこれのポイントを理解していると思います-特に、周りに同じ文字列がたくさんあり、多くの比較がある場合-しかし、通常の文字列を使用してintern
それらを使用するだけでは同じことを達成できませんでしたか? ドキュメントにはString.intern()
明示的に次のように記載されています。
...
intern メソッドが呼び出されたときに、equals(Object) メソッドによって決定されたこの String オブジェクトと等しい文字列がプールに既に含まれている場合、プールからの文字列が返されます。それ以外の場合は、この String オブジェクトがプールに追加され、この String オブジェクトへの参照が返されます。したがって、任意の 2 つの文字列 s および t について、s.intern() == t.intern() は、s.equals(t) が true である場合にのみ true になります。
...
では、のようなクラスを手動で管理することと、 を使用することの利点と欠点は何Name
intern()
ですか?
これまで考えたことは次のとおりです。
- マップを手動で管理するということは、通常のヒープを
intern()
使用することを意味し、permgen を使用します。 - マップを手動で管理する場合
Name
、インターンされた文字列とインターンされていない文字列が同じ型を共有しているため、ある場所でインターンを忘れる可能性があります。 - に依存する
intern()
ということは、余分なクラスをコーディングせずに、既存の、最適化され、実証済みのメカニズムを再利用することを意味します。 - マップを手動で管理すると、新しいユーザーにとってコードがより混乱し、厳格な操作がより面倒になります。
...しかし、ここで何か他のものが欠けているように感じます。