javaで帳票印刷 複数ページの印刷のためのデータ

複数人のデータをどう用意するか

今度のテーマは複数ページの印刷です。話の流れでいくと通知表を1クラス分まとめて印刷しようということです。もちろん今のままで最初の一人だけダイアログを出すという手もありますが、Printableなクラスに使えるものがあれば自分の使える道具を増やすことになります。

ここで問題になるのは、1クラス分の生徒のデータをどのように用意するかです。この解説は帳票印刷がテーマですから、データの用意の仕方は話の中心ではありません。複雑なデータ構造をもたせると本質が見えなくなるので、できるだけわかりやすい構造を採用したいものですが、うまく設計すると使い回しができて、今後のテーマでもデータ構造をそのつど確認しなくても良くなります。プログラムの組みやすさ読みやすさにも関係しますので考えてしまうところです。

データ構造を考えることはプログラム本体を書くことと同じぐらい重要ですが、プログラムを書き始めてから見えてくるものも多いのです。

面倒に感ずる方は飛ばしてください。

配列の次を考える

BAISC時代は配列しか使えませんでした。listやMapなども必要なら自前で配列を使って何とかしたものです。

いままで使ってきた配列はそれを踏襲しています。

String[] kams = { "国語総合","現代社会","数学Ⅰ","数学A","化学基礎",
                    "生物基礎","体育","保健","音楽Ⅰ",//"コミュニケーⅠ",
                    "コミュニケーション英語Ⅰ",
                    "英語表現Ⅰ","家庭基礎","情報の科学" };
int[] tans = { 6,3,4,2,2,
                 2,2,1,1,2,4,
                 2,2,2 };
int[] tens = { 55,45,65,45,75,
                 45,55,55,60,45,
                 65,65,55 };

科目は文字列で単位数や点数は整数ですから、1つの配列には入れられません。"現代社会"の単位数が3で成績が45というのは、「何番目の項目か」というところで対応づけているわけです。

データ構造として、これがまずいという意見もあります。つまり、成績を入れるクラスを作って、

KamokuCell kamoku1 = new KamokuCell("現代社会",3,45);

とする方が、対応付けが密で安心ができるというものです。

こうすると一人の成績は、ArrayList<KamokuCell>などで表現することになります。

使われるデータの全貌がわかっている場合には概ねうまく設計できます。しかし、後からの追加には悩まされます。

例えば、成績では1学期、2学期、3学期、学年評価があります。もちろん始めからわかっていることですが、例として考えてください。一人の生徒が学期ごとにArrayList<KamokuCell>を複数持っていると考えるか、KamokuCellを拡張して

KamokuCell kamoku1 = new KamokuCell("現代社会",3,45,55,60,3);

と入れられるようにするか迷うところです。

Listを複数持たせた場合は、「"現代社会",3」は重複した情報になります。もちろん1クラス分の生徒の成績でもすでに重複しています。また、だからといって削除すると、2番目が現代社会といったデータの位置による対応付けがまた顔を出します。

KamokuCellを拡張する方法では、成績が各学期ごとにデータ化されることから、科目名をキーにして統合する機能が 必要になります。つまりListの中から"現代社会"を捜して2学期のフィールドに55を格納するという動作です。

今回のデータ構造

いろいろ考慮してクラスつまり

クラスのフィールド   "現代社会", 3, 45, 55, 60, 3

を作らずに、2つのマップに分けることにします

単位数マップ         "現代社会"=>3
点数の配列へのマップ "現代社会"   =>45, 55, 60, 3

番号による対応付けも、「何番目の項目か」ではなくIDの番号と位置づけて、これをキーとしたマップとします。

通知表らしく、ここからは出欠も加えますが、科目と異なり出欠の項目は固定なので二次元の配列とし、生徒とは番号をキーとしてマップで対応付けます。

LinkedHashMap<Integer,String> 番号=>氏名
LinkedHashMap<Integer,LinkedHashMap<String,int[]>> 番号=>(科目名=>成績配列[学期])
LinkedHashMap<Integer,int[][]> 番号=>出欠配列[項目][学期]
LinkedHashMap<String,Integer> 科目名=>単位数

データファイルとの関係(成績)

今回は1学期、3名分しか用意していませんが、最初の表はファイルとして有りそうなデータ形式です。成績処理は学期ごとにHRで全員分の表にします。

1学期成績

No 国語
総合
現代
社会
数学
数学
化学
基礎
...
1 55 45 65 45 75 ...
2 75 50 70 55 65
3 85 65 75 40 70
...

ファイルから読み込んで、通知表に書き込む形に再構成します。二重のマップになっています

key => value
1 =>
key => value
1学期 2学期 3学期 学年
国語総合 => 55
現代社会 => 45
数学Ⅰ => 65
数学A => 45
化学基礎 => 75
... => ... ... ... ...
2 =>
key => value
1学期 2学期 3学期 学年
国語総合 => 75
現代社会 => 50
数学Ⅰ => 70
数学A => 55
化学基礎 => 65
... => ... ... ... ...
3 =>
key => value
1学期 2学期 3学期 学年
国語総合 => 85
現代社会 => 65
数学Ⅰ => 75
数学A => 40
化学基礎 => 70
... => ... ... ... ...
... => ...

データファイルとの関係(出欠)

こちらも1学期、3名分しか用意していませんが、ファイルとして有りそうなデータ形式です。出欠も学期ごとにHRで全員分の表にします。

1学期出欠

No 授業
日数
忌引
停止
欠席
日数
遅刻 早退 欠課
時数
1 62 1 3 1 0 18
2 62 0 2 0 1 13
3 62 2 1 0 1 5
...

ファイルから読み込んで、通知表に書き込む形に再構成します。intから2次元配列へのマップになっています

key => value
1 =>
1学期 2学期 3学期
62
1
3
1
0
18
2 =>
1学期 2学期 3学期
62
0
2
0
1
13
3 =>
1学期 2学期 3学期
62
2
1
0
1
5
... => ...

プログラム(SetHRData.java)

将来的にファイルから読み込むことを考えて、構成しています。

コンストラクタで各Mapを作成しておき、getNantokaMap()メソッドで取得するようにします。

SetHRData.java

package print01;
import java.util.*;
//Test用
public class SetHRData{
    LinkedHashMap<Integer,String> namemap;   //番号=>氏名
    LinkedHashMap<Integer,LinkedHashMap<String,int[]>> seisekimap; //番号=>(科目名=>成績配列[学期])
    LinkedHashMap<Integer,int[][]> shukketsumap; //番号=>出欠配列[項目][学期]
    LinkedHashMap<String,Integer> tanis; //科目名=>単位数
    int gakki,nendo;
    String HR;
    public SetHRData(int nendo, String HR, int gakki){
        this.nendo = nendo;
        this.HR = HR;
        this.gakki = gakki;
        String[] kams = { "国語総合","現代社会","数学Ⅰ","数学A","化学基礎",
                              "生物基礎","体育","保健","音楽Ⅰ",//"コミュニケーⅠ",
                              "コミュニケーション英語Ⅰ",
                              "英語表現Ⅰ","家庭基礎","情報の科学" };
        setNameMap();
        setSeisekiMap(kams);
        setShukketsuMap();
        setTanis(kams);
    }
    //番号=>氏名
    private void setNameMap(){
        namemap = new LinkedHashMap<>();
        namemap.put(1,"赤石 美緒");
        namemap.put(2,"荒川 早織");
        namemap.put(3,"石村 千尋");
    }
    //番号=>(科目名=>成績配列[学期])
    private void setSeisekiMap(String[] kams){
        int[][] hitotens = {
                 { 55,45,65,45,75,45,55,55,60,45,65,65,55 }, //no.1
                 { 75,50,70,55,65,50,65,65,50,75,75,60,60 }, //no.2
                 { 85,65,75,40,70,55,60,70,65,55,60,85,95 } //no.3
        };
        seisekimap = new LinkedHashMap<>();
        for(int n=1; 3>=n; n++){
            LinkedHashMap<String,int[]> kamokutens = new LinkedHashMap<>();
            for(int i=0; kams.length>i; i++){
                kamokutens.put(kams[i], new int[4]);
            }
            seisekimap.put(n, kamokutens);
        }
        for(int n=1; 3>=n; n++){
            for(int k=0; hitotens[n-1].length>k; k++){
                seisekimap.get(n).get(kams[k])[gakki-1]= hitotens[n-1][k];
            }
        }
    }
    //番号=>(出欠配列[項目][学期])
    private void setShukketsuMap(){
        int[][] ks = {
                { 62,1,3,1,0,18 }, //no.1
                { 62,0,2,0,1,13 }, //no.2
                { 62,2,1,0,1, 5 }  //no.3
        };
        shukketsumap = new LinkedHashMap<>();
        for(int n=1; 3>=n; n++){
            shukketsumap.put(n, new int[6][3]);
        }
        for(int n=1; 3>=n; n++){
            for(int k=0; ks[n-1].length>k; k++){
                shukketsumap.get(n)[k][gakki-1] = ks[n-1][k];
            }
        }
    }
    //科目名=>単位数
    private void setTanis(String[] kams){
        int[] tans = { 6,3,4,2,2,  2,2,1,1,2,4,  2,2,2 };
        tanis = new LinkedHashMap<>();
        for(int i=0; kams.length>i; i++){
            tanis.put(kams[i],tans[i]);
        }
    }
    //番号=>氏名
    public LinkedHashMap<Integer,String> getNameMap(){
       return namemap;
    }
    //番号=>(科目名=>成績配列[学期])
    public LinkedHashMap<Integer,LinkedHashMap<String,int[]>> getSeisekiMap(){
       return seisekimap;
    }
    //番号=>出欠配列[項目][学期]
    public LinkedHashMap<Integer,int[][]> getShukketsuMap(){
       return shukketsumap;
    }
    //科目名=>単位数
    public LinkedHashMap<String,Integer> getTanis(){
       return tanis;
    }

     public static void main(String[] args) {
        SetHRData hrdata = new SetHRData(2017,"1A",1);
        System.out.println("...");
    }
}

呼び出し方

SetHRData hrdata = new SetHRData(2017,"1A",1);
LinkedHashMap<Integer,String> namemap = hrdata.getNameMap();
LinkedHashMap<Integer,LinkedHashMap<String,int[]>>
       seisekimap = hrdata.getSeisekiMap();
LinkedHashMap<Integer,int[][]> shukketsumap = hrdata.getShukketsuMap();
LinkedHashMap<String,Integer> tanis = hrdata.getTanis();