第20章 Flyweight ~同じものを共有して無駄をなくす~ 深澤研究室 M0 1w120399-6 西田 和馬
Flyweightパターンとは Flyweight とは、英語で「フライ級」 ボクシングで最も軽い階級 ⇨ オブジェクト(メモリの使用量)を「軽く」するもの Flyweightは、「インスタンスをできるだけ共有させて、無駄にnewしない」 インスタンスの生成「new」は、メモリを確保 多くの「new」はメモリの使用量の増加
サンプルプログラム コマンドラインで入力された値・文字を大きな文字で出力 大きな文字を構成するデータはtxtファイルで用意 数字は「0~9」 文字は ” - ” のみ 目的のファイルがない場合(” - ”以外の文字) 文字の後に ” ? ”をつけたものをフォントデータとして出力
「大きな文字」のtxtファイル big0.txt big1.txt big-.txt big9.txt
サンプルプログラムのクラス図
BigCharクラス ファイルから大きな文字のテキスト読み込み lineにファイル内容を一行書き、これをbufに追加 import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BigChar { private char charname; // 文字の名前 private String fontdata; // 大きな文字を表現する文字列('#' '.' '\n'の列) public BigChar(char charname) { // コンストラクタ this.charname = charname; try { BufferedReader reader = new BufferedReader( new FileReader("big" + charname + ".txt") ); String line; StringBuffer buf = new StringBuffer(); while ((line = reader.readLine()) != null) { buf.append(line); buf.append("\n"); } reader.close(); this.fontdata = buf.toString(); } catch (IOException e) { this.fontdata = charname + "?"; public void print() { // 大きな文字を表示する System.out.print(fontdata); 大きな文字を表現するクラス ファイルから大きな文字のテキスト読み込み lineにファイル内容を一行書き、これをbufに追加 StringBufferクラスではStringクラスと異なり一度オブジェクトの値を代入した後でもその値の変更が可能です。但し、値が不変な Stringクラスと比較すると値が同一の場合でもメモリ内に格納されている値の共有ができないなどあまり効率がよくありません。また初期容量を指定せず StringBufferオブジェクトを生成した場合は値の変更を見越して代入された値に16文字を加えた値がメモリ領域に確保されてしまいます。そのため値の変更がないのであればStringBufferクラスを使用すべきではありません。 fontdataに作成した文字列(テキスト内容)を格納 大きな文字はメモリ消費量が多い ⇨ BigCharのインスタンス共有を目指す
BigCharのインスタンスを共有しながら生成するクラス BigCharFactoryクラス BigCharのインスタンスを共有しながら生成するクラス import java.util.HashMap; public class BigCharFactory { // すでに作ったBigCharのインスタンスを管理 private HashMap pool = new HashMap(); // Singletonパターン private static BigCharFactory singleton = new BigCharFactory(); // コンストラクタ private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { bc = new BigChar(charname); // BigCharのインスタンスを生成 pool.put("" + charname, bc); return bc; 文字列→インスタンスの対応関係を管理 ・ Singletonパターン - BigCharFactoryのインスタンスは1つで良い ・pool:これまでに作ったインスタンスを格納 ・pool内から目的の文字のインスタンスを検索しbcに格納 ⇨ ない場合は、bc = null
BigCharのインスタンスを共有しながら生成するクラス BigCharFactoryクラス BigCharのインスタンスを共有しながら生成するクラス import java.util.HashMap; public class BigCharFactory { // すでに作ったBigCharのインスタンスを管理 private HashMap pool = new HashMap(); // Singletonパターン private static BigCharFactory singleton = new BigCharFactory(); // コンストラクタ private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { bc = new BigChar(charname); // BigCharのインスタンスを生成 pool.put("" + charname, bc); return bc; ・poolに目的の文字のインスタンスがない場合 引数charnameで与えられた文字に対応するBigCharのインスタンスを作成 poolに作成したインスタンスを登録
BigCharFactoryクラス 既に同じ文字のインスタンスを作っている場合 新しいインスタンスを作らない import java.util.HashMap; public class BigCharFactory { // すでに作ったBigCharのインスタンスを管理 private HashMap pool = new HashMap(); // Singletonパターン private static BigCharFactory singleton = new BigCharFactory(); // コンストラクタ private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { bc = new BigChar(charname); // BigCharのインスタンスを生成 pool.put("" + charname, bc); return bc; 既に同じ文字のインスタンスを作っている場合 新しいインスタンスを作らない
BigStringクラス BigCharのインスタンスを保持 コマンドラインで指定した文字 文字列stringのi番目の文字を返す public class BigString { // 「大きな文字」の配列 private BigChar[] bigchars; // コンストラクタ public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } // 表示 public void print() { bigchars[i].print(); BigCharのインスタンスを保持 コマンドラインで指定した文字 文字列stringのi番目の文字を返す (先頭は0文字目) 文字列からn番目の文字列を抜き出すには、charAtメソッドを利用します。文字列の先頭は0文字目と見なします(よって、最後の文字は「length() -1」番目です)。 i番目の文字に対応するインスタンスを生成
Mainクラス 実行例 java Main 1212123 動作テスト用のクラス 出力 public class Main { public static void main(String[] args) { if (args.length == 0) { System.out.println("Usage: java Main digits"); System.out.println("Example: java Main 1212123"); System.exit(0); } BigString bs = new BigString(args[0]); bs.print(); 出力 実行例 java Main 1212123
プログラムの流れ 1212123 BigStringクラス 入力例 Mainクラス public class BigString { // 「大きな文字」の配列 private BigChar[] bigchars; // コンストラクタ public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } // 表示 public void print() { bigchars[i].print(); 1212123 Mainクラス public class Main { public static void main(String[] args) { if (args.length == 0) { System.out.println("Usage: java Main digits"); System.out.println("Example: java Main 1212123"); System.exit(0); } BigString bs = new BigString(args[0]); bs.print();
プログラムの流れ 1212123 1 1 BigCharFactoryクラス BigStringクラス import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; public class BigString { // 「大きな文字」の配列 private BigChar[] bigchars; // コンストラクタ public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } // 表示 public void print() { bigchars[i].print(); 1212123 1 1
プログラムの流れ 1 1 BigCharクラス BigCharFactoryクラス import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BigChar { private char charname; // 文字の名前 private String fontdata; public BigChar(char charname) { // コンストラクタ this.charname = charname; try { BufferedReader reader = new BufferedReader( new FileReader("big" + charname + ".txt") ); String line; StringBuffer buf = new StringBuffer(); while ((line = reader.readLine()) != null) { buf.append(line); buf.append("\n"); } reader.close(); this.fontdata = buf.toString(); } catch (IOException e) { this.fontdata = charname + "?"; public void print() { // 大きな文字を表示する System.out.print(fontdata); import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; 1 1
プログラムの流れ pool 1 BigCharクラス BigCharFactoryクラス import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BigChar { private char charname; // 文字の名前 private String fontdata; public BigChar(char charname) { // コンストラクタ this.charname = charname; try { BufferedReader reader = new BufferedReader( new FileReader("big" + charname + ".txt") ); String line; StringBuffer buf = new StringBuffer(); while ((line = reader.readLine()) != null) { buf.append(line); buf.append("\n"); } reader.close(); this.fontdata = buf.toString(); } catch (IOException e) { this.fontdata = charname + "?"; public void print() { // 大きな文字を表示する System.out.print(fontdata); import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; pool 1
プログラムの流れ pool 1 BigCharFactoryクラス import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; 1
プログラムの流れ pool 1212123 2 2 BigCharFactoryクラス BigStringクラス import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; public class BigString { // 「大きな文字」の配列 private BigChar[] bigchars; // コンストラクタ public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } // 表示 public void print() { bigchars[i].print(); 1212123 2 2
プログラムの流れ pool 1212123 1 1 BigCharFactoryクラス BigStringクラス import java.util.HashMap; public class BigCharFactory { private HashMap pool = new HashMap(); private static BigCharFactory singleton = new BigCharFactory(); private BigCharFactory() { } // 唯一のインスタンスを得る public static BigCharFactory getInstance() { return singleton; // BigCharのインスタンス生成(共有) public synchronized BigChar getBigChar(char charname) { BigChar bc = (BigChar)pool.get("" + charname); if (bc == null) { // BigCharのインスタンスを生成 bc = new BigChar(charname); pool.put("" + charname, bc); return bc; public class BigString { // 「大きな文字」の配列 private BigChar[] bigchars; // コンストラクタ public BigString(String string) { bigchars = new BigChar[string.length()]; BigCharFactory factory = BigCharFactory.getInstance(); for (int i = 0; i < bigchars.length; i++) { bigchars[i] = factory.getBigChar(string.charAt(i)); } // 表示 public void print() { bigchars[i].print(); 1212123 1 1
“12123”に対応するBigStringの インスタンスの様子
まとめ Flyweightは、「インスタンスを共有」 短所 newの削減 ⇨ メモリの消費量を削減 ⇨ プログラムの実行速度の向上 共有するインスタンスの変更は、複数箇所に影響 ⇨ 共有させるべきインスタンスとそうでないものを区別することが重要