2014年12月12日金曜日

Java継承、その他(12章~終わり)

継承

クラスのフィールドやメソッドを継承して、新しいクラスを派生させることができる

派生されたサブクラスは継承元のフィールド、メソッドを全て引き継ぎ、新しく追加することもできる

※Javaではクラスの継承時に直属の親クラスは1つしか持てない(多重継承不可)

class 派生クラス名 extents 継承元クラス名 {
     ・・・
}


//継承元クラス
class A {
  int a;
  void printA() {
    System.out.println(a);
  }
}

//Aを派生させたクラス
class B extends A {
  //新たなフィールドを追加
  int b;

  //新たなメソッドを追加
  void printB() {
    System.out.println(b);
  }

  void printAB() {
    //親クラスのメソッドを呼ぶことも可能
    printA();
    System.out.println(b);
  }

  void printAPlusB() {
    //親クラスのフィールドにもアクセス可能
    System.out.println(a + b);
  }
}

class Test {
  public static void main(String[] args) {
 
    //クラスAを生成
    A clsA = new A();
    clsA.a = 111;
 
    //printAメソッドを実行
    //「111」と表示
    clsA.printA();
 
 
    //Aを継承したクラスBを生成
    B clsB = new B();
 
    //親クラスのフィールドaを設定
    clsB.a = 222;
 
    //親クラスのprintAメソッドを実行
    //「222」と表示
    clsB.printA();
 
    //新たに定義したフィールドに設定
    clsB.b = 333;
 
    //新たに定義したメソッドを実行
    //「333」と表示
    clsB.printB();
 
    //「222」「333」と表示
    clsB.printAB();
 
    //「555」
    clsB.printAPlusB();
  }
}


呼称

派生元のクラス…親クラス、基底クラス、スーパークラス
派生したクラス…子クラス、派生クラス、サブクラス


メンバ

フィールドとメソッドをまとめて「メンバ」と呼ぶ


派生とコンストラクタ

クラスの派生において、コンストラクタ静的初期化子インスタンス初期化子は継承されない



「super(…)」によってスーパークラスのコンストラクタを呼び出せる
※ただし、コンストラクタの先頭のみで、「this(…)」とどちらかしか呼べない


サブクラス内のコンストラクタ内では、「super()」が自動的に挿入される


自動で作られたデフォルトコンストラクタには「super();」が追加されている
コンストラクタを定義しないクラスは、そのスーパークラスが引数なしのコンストラクタを持っていなければならない


「super.」によって、スーパークラスのメンバにアクセスできる
→スーパークラスと同じ名前のフィールドやメソッドを定義可能で、それらに名前のみでアクセスするとサブクラスの方となる



インスタンス生成時の処理の流れ

全てのフィールドをデフォルト値で初期化
上位クラスの初期化子
上位クラスの引数なしのコンストラクタ
下位クラスの初期化子
下位クラスのコンストラクタ



is-Aの関係

継承関係は「サブクラスは一種のスーパークラスである」と成り立つ、これを「is-Aの関係」という





継承の特徴

継承ではメンバを新たに追加することはできるが削除はできない

メンバの変更は「オーバーライド」と「隠蔽」によって行う




オーバーライド

スーパークラスのメソッドと同じシグネチャのメソッドをサブクラスで再定義して上書きすること



隠蔽

スーパークラスのフィールドと同じ名前のフィールド(型は違ってもよい)をサブクラスで定義し覆い隠してしまうこと
→隠蔽されたフィールドは「super.」でアクセスできる

※いろいろと紛らわしいので隠蔽を使うべきではない


アノテーション

オーバーライド時に「@Override」と記載しておくと、コーディングミスが防げる
→メソッド名や引数などが間違っている場合に警告が出るようになる

※「@Deplicate」アノテーションで推奨外と定義できる


class A {
  int a = 1;
  void printA() {
    System.out.println("sp.a=" + a);
  }
}

class B extends A {
  //フィールドを隠蔽
  int a = 2;

  //オーバーライド
  @Override
  void printA() {
    System.out.println("sub.a=" + a);
  }

  void printSP() {
    //スーパークラスのフィールドを表示
    System.out.println("super.a=" + super.a);
 
    //スーパークラスのメソッドを実行
    super.printA();
  }
}

class Test {
  public static void main(String[] args) {
 
    //クラスAを生成
    A clsA = new A();
 
    //printAメソッドを実行
    //「sp.a=1」と表示
    clsA.printA();
 
 
    //Aを継承したクラスBを生成
    B clsB = new B();
 
    //オーバーライドしたメソッドを実行
    //「sub.a=2」と表示(隠蔽した値を表示)
    clsB.printA();
 
    //スーパークラスの値を表示するメソッド
    //「super.a=1」「sp.a=1」と表示
    clsB.printSP();
  }
}


ポリモーフィズム

同じ名前のメソッドで異なる挙動を実現することが可能でやり方は以下のとおりである

継承関係にある型(クラス)同士であれば、上位の型を持つ変数に下位の型を持つインスタンスを代入できる

その場合、呼び出せるメンバは変数の型にあるもののみだが、実際に呼び出すのはインスタンスのメンバである(動的結合


class Base {
  void print() {
    System.out.println("base");
  }
}

class Sub1 extends Base {
  //オーバーライド
  void print() {
    System.out.println("sub1");
  }

  void printSub() {
    System.out.println("111");
  }
}

class Sub2 extends Base {
  //オーバーライド
  void print() {
    System.out.println("sub2");
  }

  void printSub() {
    System.out.println("222");
  }
}

class Test {
  public static void main(String[] args) {
 
    //Sub1のインスタンスを生成
    Sub1 s1 = new Sub1();
 
    //Sub2のインスタンスを生成
    Sub2 s2 = new Sub2();
 
    //Baseクラスの配列
    Base b[] = new Base[2];
 
    //Base配列にSub1のインスタンスを入れる
    b[0] = s1;
 
    //Base配列にSub2のインスタンスを入れる
    b[1] = s2;
 
    for (Base base : b) {
      //ポリモーフィズム
      //「sub1」「sub2」と表示される
      base.print();
   
      //Baseに無いメソッドは呼べない
      //コンパイルエラーとなる!!
      //base.printSub();
    }
 
  }
}



finalクラス

finalを頭に付けて宣言されたクラスの派生クラスを定義することはできない(継承できない)


finalメソッド

finalを頭に付けたメソッドはサブクラスでオーバーライドすることはできない



オーバーライドとメソッドのアクセス性

メソッドをオーバーライドする場合は上書きするメソッドのアクセス修飾子よりも、privateに近い方のアクセス修飾子をつけることはできない


class Base {
  //protectedアクセスのメソッド
  protected void print() {
    System.out.println("Print");
  }
}

class Sub1 extends Base {
  //同じアクセス修飾子はOK
  protected void print() {
    System.out.println("Print1");
  }
}

class Sub2 extends Base {
  //publicに近づけるのはOK
  public void print() {
    System.out.println("Print2");
  }
}

class Sub3 extends Base {
  //privateに近づけるのは×
  private void print() {
    System.out.println("Print3");
  }
}

class Sub4 extends Base {
  //privateに近づけるのは×
  void print() {
    System.out.println("Print3");
  }
}


抽象メソッド

具体的な機能を持たない空のメソッドで「abstract メソッド名(引数);」と宣言する


抽象クラス

abstract クラス名 {
    ・・・・
}

抽象メソッドを含んだクラスで、これを継承するサブクラスは抽象メソッドをオーバーライドする必要がある
→全ての抽象メソッドを実装しない場合はサブクラスも抽象クラスになる

※抽象クラスは継承されることを前提としたものであり、そのインスタンスを直接生成することはできない

※抽象メソッドを持たないクラスでも抽象クラスにすることはできる

※スーパークラスの非抽象メソッドを抽象メソッドとしてオーバーライドできる


//抽象クラス
abstract class Base {
  //抽象メソッド
  abstract void print();
}

//抽象クラスを継承
class Sub1 extends Base {
  //抽象メソッドを実装(オーバーライド)
  void print() {
    System.out.println("sub1");
  }
}

class Test {
  public static void main(String[] args) {
 
    //Sub1のインスタンスを生成
    Sub1 s1 = new Sub1();
 
    //「sub1」と表示される
    s1.print();
 
    //抽象クラスのインスタンスは生成できない
    //Base b = new Base();
  }
}



アクセス修飾子

public

どこからでもアクセス可能



protected

同一パッケージ内と、違うパッケージの継承先(サブクラス)がアクセス可能



デフォルト(無し)

同一パッケージ内のみアクセス可能


private

自分のクラス内のみアクセス可能(他のクラスからはアクセスできない)



※メソッドのオーバーライドや抽象メソッドの実装時に、元のメソッドのアクセス修飾子よりも厳しいアクセス制限(privateより)にすることはできない


class Base {
  //protectedアクセスのメソッド
  protected void printA() {
    System.out.println("A");
  }
}

class Sub1 extends Base {
  //制限を緩める(publicより)のはOK
  public void printA() {
    System.out.println("S1");
  }
}

class Sub2 extends Base {
  //同じアクセス修飾子はOK
  protected void printA() {
    System.out.println("S2");
  }
}

class Sub3 extends Base {
  //コンパイルエラー
  //制限を厳しくする(privateより)のは×
  void printA() {
    System.out.println("S3");
  }
}

class Sub4 extends Base {
  //コンパイルエラー
  //制限を厳しくする(privateより)のは×
  private void printA() {
    System.out.println("S3");
  }
}



インターフェイス


全てのメソッドが抽象メソッドのクラスのようなもので、複数実装することができる
→単一継承しか認めない抽象クラスの使用の欠点を補うモノである


※インターフェイス内のメソッドは全てpublicかつabstractとなる
メソッドを実装する場合はpublicにする必要がある



インターフェイスで宣言されたフィールドは「public static final」となる
→つまり、フィールドには定数しか宣言できない


interface インターフェイス名 {
    ・・・・
}




インターフェイスの実装

class クラス名 implements インターフェイス名(, インターフェイス名) {
    ・・・・
}

※複数実装する場合はカンマで区切る


//インターフェイス
interface If1 {
  void print1();
}

//インターフェイス
interface If2 {
  void print2();
}

//インターフェイスを実装
class Imp1 implements If1 {

  //オーバーライドしなければならない!
  @Override
  public void print1() {
    System.out.println("Imp1");
  }
}

//インターフェイスを複数実装
class Imp2 implements If1, If2 {

  //オーバーライドしなければならない!
  @Override
  public void print1() {
    System.out.println("Imp2-1");
  }

  //オーバーライドしなければならない!
  @Override
  public void print2() {
    System.out.println("Imp2-2");
  }
}

class Test {
  public static void main(String[] args) {
 
    //インターフェイスのインスタンスは作れない
    //コンパイルエラー!
    //If1 if0 = new If1();
 
    //インターフェイス型に実装クラスを入れるのはOK
    If1 if1 = new Imp1();
 
    //「Imp1」と表示される
    if1.print1();
 
    //多重実装したクラスを生成
    Imp2 imp2 = new Imp2();
 
    //「Imp2-1」「Imp2-2」と表示される
    imp2.print1();
    imp2.print2();
 
    //If1型に入れることもできる
    If1 if1a = imp2;
 
    //「Imp2-1」と表示される
    if1a.print1();
 
    //print2は呼べない(コンパイルエラー)
    //if1a.print2();
 
    //If2型に入れることもできる
    If2 if2a = imp2;
 
    //「Imp2-2」と表示される
    if2a.print2();
 
    //print1は呼べない(コンパイルエラー)
    //if2a.print1();
  }
}


extentsとimplementsの両方がある場合、extendsを先に書かなければならない


//インターフェイス
interface If1 {
  void print1();
}

//親クラス
class Base {
  void print2() {
    System.out.println("Print2");
  }
}

//継承かつ実装
class Sub extends Base implements If1 {
  @Override
  public void print1() {
    System.out.println("Print1");
  }
}



インターフェイスの派生

インターフェイスもクラスと同様に継承により派生することができる
→クラスと同様に多重継承はできない

interface If1 {
  void print1();
}

//If1を継承
interface If2 extends If1 {
  void print2();
}

//If2を実装
class Imp implements If2 {
  //If1のメソッドを実装
  public void print1() {
    System.out.println("If1");
  }

  //If2のメソッドを実装
  public void print2() {
    System.out.println("If2");
  }
}


class Test {
  public static void main(String[] args) {
 
    //インターフェイスの実装クラスを生成
    If2 if2 = new Imp();
 
    //「If1」「If2」と表示
    if2.print1();
    if2.print2();
  }
}



抽象クラス or インターフェイス

どちらを使うか迷ったらインターフェイスだが、共通の処理がある場合は抽象クラス



Objectクラス

全てのクラスの親となるクラスで、明示的に派生していないクラスは自動的にObjectクラスのサブクラスとなる


Objectクラスのメソッド

public String toString() … オブジェクトを文字列で表現する

public boolean equals(Object obj) … 等しいかどうか

public int hashCode() … equalsメソッドを修正する場合、こちらも要修正




例外処理

例外とはアプリケーション実行時に発生するエラーのことで、それを対処する必要がある


実行時エラー

リソースの状態によって発生するエラーなどで、ソースコードのミスではない場合のエラー
→エラーでプログラムが終了してしまわない為にも例外処理が必要



try~catch

例外が投げられた(スローされた)場合、その例外を捕捉(キャッチ)して、例外毎に対応する処理を行う

※catchは複数書くこともできるし無くてもよい

※catchは上から順番に捕捉できるか判定され、キャッチできない場合は次のcatchを判定する

※catchに書かれた例外クラスのサブクラスも捕捉される


finally

tryブロックの後に例外が発生する・しないに関わらず必ず実行される処理を記載する
→tryブロックの中で利用したリソースの後始末などに利用する

※finallyは無くてもよい


try {

  例外が発生する可能性のあるコード

} catch (例外クラス1 変数1) {

  例外1が発生した場合の処理

} catch (例外クラス2 変数2) {

  例外2が発生した場合の処理

} finally {
  tryブロックの後に必ず実行したい処理
}




try {

  //例外が発生する可能性がある処理
  FileReader reader = new FileReader("./test.txt");

} catch (FileNotFoundException e) {

  //ファイルが存在しない場合の処理
  e.printStackTrace();
  ・・・・

} catch (IOException e) {

  //入出力エラーの場合の処理
  e.printStackTrace();
  ・・・・

} catch (Exception e) {

  //その他の例外全てをここで捕捉
  e.printStackTrace();
  ・・・・

} finally {

  //最後に行いたい処理
  ・・・・
}



例外クラスの型


Error … このクラスのサブクラスは致命的なエラーで例外処理もできない
Exception … このクラスのサブクラス(RuntimeException系以外)は例外処理が必須
RuntimeException … このクラスのサブクラスの例外処理は任意




アップキャスト

親クラスの型にサブクラスのインスタンスを変換すること
暗黙的(自動的)に変換される(明示的なキャストは不要)


ダウンキャスト

親クラスの型からサブクラスの型に変換すること
明示的なキャストが必要(暗黙的に変換されない)

※ダウンキャストは失敗する可能性があるので「instanceof」で確認する


インスタンス変数 instanceof ダウンキャストしたいクラス型



class Base {
  void print() {
    System.out.println("base");
  }
}

class Sub extends Base {
  void printSub() {
    System.out.println("sub1");
  }
}

class Test {
  public static void main(String[] args) {
    //親クラス型
    Base b;
 
    //サブクラス型
    Sub s;
 
 
    s = new Sub();
 
    //アップキャスト
    b = s;
 
    //サブクラス型に子クラスを入れるのは×
    //コンパイルエラー!!
    //s = new Base();
 
    //明示的にキャストしてもこれは×
    //実行時エラー(変換できない)
    //Base b2 = new Base();
    //Sub s2 = (Sub)b2;
 
 
    //ベース型にサブのインスタンスを入れる
    Base b3 = new Sub();
 
    //b3に入っているインスタンスがSub型かどうか?
    if (b3 instanceof Sub) {
      //b3のインスタンスがSub型ならば、
      //このダウンキャストはOK
      Sub s3 = (Sub)b3;
      s3.printSub();
    }
  }
}




データの種類

CSV(comma-separated values)

データをカンマで区切った形式でExcelで読み込める

赤,red,#ff0000



プログラムで取得する場合はsplitメソッドを使うとよい

String str = "赤,red,#ff0000";

//カンマごとに分割し配列に格納
String[] ary = str.split(",");

for (String s : ary) {
  System.out.println(s);
}



TSV(tab-separated values)

データをタブで区切った形式

赤[\t]red[\t]#ff0000


xml(Extensible Markup Language)

xmlというマークアップ言語で記載した形式

<person>
  <name>yamada</name>
  <email>ym@xxx.xx</mail>
</person>


json(JavaScript Object Notation)

JavaScriptのオブジェクト記法を利用した形式

{
  person:[
    name:yamada,
    email:ym:xxx.xx
  ]
}


Perse(パース)

データを解析してプログラムで使える形式に変換する処理



スクレイピング

元のデータから必要な部分を抽出すること



ファイル書き込み

FileWriter→BufferedWriter→PrintWriterと徐々に便利なクラスに拡張していく


PrintWriter writer = null;

try {
  //sample.txtというファイルを開く(無ければ生成)
  writer = new PrintWriter(new BufferedWriter(new FileWriter("sample.txt")));

  //追記の場合は第二引数を付ける
  //writer = new PrintWriter(new BufferedWriter(new FileWriter("sample.txt", true)));

  //書き込み
  writer.write("Hello");

  //printlnも使える
  writer.printf("abcd");

  //printfも使える
  writer.printf("%d\n",123);

} catch (IOException e) {

  e.printStackTrace();

} finally {

  if (writer != null) {
    //ファイルを閉じる
    writer.close();
  }

}



ファイルの読み込み

FileReader→BufferedReaderと徐々に便利なクラスに拡張していく


BufferedReader reader = null;

try {

  //ファイルを開く
  reader = new BufferedReader(new FileReader("sample.txt"));
  String line;

  //ファイルを一行づつ読み込み
  while ((line = reader.readLine()) != null) {
    //一行分を出力
    System.out.println(line);
  }
} catch (FileNotFoundException e) {

  e.printStackTrace();

} catch (IOException e) {

  e.printStackTrace();

} finally {
  if (reader != null) {
    try {
      //ファイルを閉じる
      reader.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}




文字

文字は整数値のコードで表され、JavaではUnicodeを採用しており、最初の128文字はASCIIコードと一致する



16進数

1~9abcdefで表し16で桁上がりする数値で頭に「0x」を付ける

0x32…10進数では50(16*3 + 2)
0xff…10進数では255(16*15 + 15)



文字リテラル

シングルクォート「'」で囲んだ文字で、char型の変数に入れられる



文字列とString


文字列リテラルはString型インスタンスへの参照であり、フィールドとしてchar型の配列を持っていてそこに1文字ずつ格納されている



文字列の比較

Stringクラスのインスタンスメソッドequalsを使用する

変数名.equals(比較する文字列);

s1 = "ABC";
s2 = "ABC";

if (s1.equals(s2)) {
  System.out.print("等しい");
} else {
  System.out.print("異なる");
}



文字列連結

StringBuilderクラスを使用すると効率的にメモリを使って連結できる

StringBuilder sb = new StringBuilder();
sb.append("abc");
sb.append("defg");

//「abcdefg」と表示される
System.out.println(sb);




コマンドライン引数


javaコマンドに引数を入力すると、mainメソッドのString[]型の引数として渡される


ソース

public class PrintArgs {
  public static void main(String[] args) {
    for (int i = 0; i < args.length; i++) {
      System.out.printf("args[%d]=%s\n", i, args[i]);
    }
  }
}


コマンドプロンプト

>java PrintArgs aaa bbb


実行結果

args[0]=aaa
args[1]=bbb



文字列から基本型への変換

基本型のラッパークラスのpase…メソッドで文字列から基本型への変換ができる


//文字列から数値に変換
int num = Integer.parseInt("5");

//文字列から浮動小数に変換
double d = Double.parseDouble("1.23");







スレッド(Thread)

複数の処理を並行して実行させるための機能


シングルスレッド

1つのプログラムで1つの処理だけを行う


マルチスレッド

1つのプログラムで複数の処理を同時に行う



スレッドの実装

Threadクラスを継承、Runnableインターフェイスを実装の2つの方法がある



Threadクラスを継承

1.Threadクラスをextends
2.runメソッドをオーバーライド
3.サブクラスのインスタンスを生成
4.startメソッドでスレッド開始(runが実行される)

public class ThreadBasic extends Thread {

  private String name;

  public ThreadBasic(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    super.run();
 
    for (int i = 0; i < 50; i++) {
      System.out.printf("%s:%d,", name, i);
    }
  }

  public static void main(String[] args) {
 
    Thread t1 = new ThreadBasic("t1");
    Thread t2 = new ThreadBasic("t2");
    Thread t3 = new ThreadBasic("t3");
 
    t1.start();
    t2.start();
    t3.start();
 
  }
}


実行結果

t2:0,t3:0,t1:0,t1:1,t2:1,t3:1,t1:2,t2:2,t3:2,t2:3,t1…



Runnableインターフェイスを実装


1.Runnableインターフェイスをimplements
2.runメソッドを実装
3.実装クラスのインスタンスを引数にThreadクラスのインスタンスを生成
4.startメソッドでスレッド開始(runが実行される)

public class RunnableBasic implements Runnable {

  private String name;

  public RunnableBasic(String name) {
    this.name = name;
  }

  @Override
  public void run() {
    for (int i = 0; i < 50; i++) {
      System.out.printf("%s:%d,", name, i);
    }
  }

  public static void main(String[] args) {
 
    Thread t1 = new Thread(new RunnableBasic("t1"));
    Thread t2 = new Thread(new RunnableBasic("t2"));
    Thread t3 = new Thread(new RunnableBasic("t3"));
 
    t1.start();
    t2.start();
    t3.start();
  }
}


実行結果

t2:0,t3:0,t1:0,t1:1,t2:1,t3:1,t1:2,t2:2,t3:2,t2:3,t1…




匿名クラス(無名クラス)

名前を持たないクラスで以下のようなメリットがある


・関連するコードをシンプルに表現できる
・かたまりを把握しやすい
・式が許されている箇所であればどこにでも記述できる
・名前が無いので、名前空間を汚さない
※その性質上、定義したクラスを後から利用しないことがわかっている(=その場限りの)クラスを定義するのに利用する



匿名クラスを使ったスレッドの実装例

public class AnoymouseThread {

  public static void main(String[] args) {
 
    Thread t1 = new Thread() {
      @Override
      public void run() {
        for (int i = 0; i < 50; i++) {
          System.out.printf("%s:%d\n", "t1", i);
        }
      }
    };
 
    Thread t2 = new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 50; i++) {
          System.out.printf("%s:%d\n", "t2", i);
        }
      }
    });
 
    t1.start();
    t2.start();
  }
}


コレクション

配列よりもコレクションを優先して使うべきである
→配列よりも機能に乏しく、コレクションとの親和性も低いため



リスト(Listインターフェイス)

インデックスで管理、要素の重複OK

ArrayList:可変長配列クラス
LinkedList:リンク構造のリストクラス(要素の挿入・削除が高速)



セット(Setインターフェイス)

要素に順番なし、重複NG

HashSet:任意の順序で格納
LinkedHashSet:格納順序を保障
TreeSet:キーにより要素を自動的にソート



キュー(Queueインターフェイス)

待ち行列の機能を提供、先頭・末尾の要素だけを追加・削除できる

ArrayDeque:両端キュークラス
LinkedList:ListのLinkedListと同じ



マップ(Mapインターフェイス)

キーと値の組み合わせで管理、キーの重複はNG

HashMap:基本的なMap
TreeMap:キーにより要素を自動的にソートするMap



基本的な使い方

インターフェイス型に実装クラスのインスタンスを入れて使う

List<String> list = new ArrayList<String>();


インターフェイス型を用いる目的は、クラスへの依存を最小限にするため

ただし、Android開発においては以下の書き方の方がよい
→ポリモーフィズムはオーバーヘッドがあるため

ArrayList<String> list = new ArrayList<String>();



匿名クラスを使った初期化

List<String> list = new ArrayList<String>() {
  //インスタンス初期化子を定義
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};



配列→リスト変換


String[] str = {"Java", "C#", "Ruby"};

List<String> list1 = Arrays.asList(str);

//0番目の要素を更新
list1.set(0, "ジャバ");

//Listを表示
//[ジャバ, C#, Ruby]と表示される
System.out.println(list1);

//元の配列を表示
//[ジャバ, C#, Ruby]と表示される
System.out.println(Arrays.toString(str));

//エラー
list1.add("VB");

//エラー
list1.remove(0);

※この変換の仕方では要素の追加削除はできない




String[] str2 = {"Java", "C#", "Ruby"};
List<String> list2 = new ArrayList<String>();
Collections.addAll(list2, str2);

list2.set(0, "ジャバ");
list2.add("Perl");
list2.remove(1);

//[ジャバ, Ruby, Perl]と表示
System.out.println(list2);

//[Java, C#, Ruby]と表示
System.out.println(Arrays.toString(str2));

※この変換なら要素の追加、更新もOK



リスト→配列変換


List<String> list3 = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

String[] str3 = new String[list3.size()];
list3.toArray(str3);
list3.set(0, "ジャバ");

//[Java, C#, Ruby]と表示
System.out.println(Arrays.toString(str3));

//[ジャバ, C#, Ruby]と表示
System.out.println(list3);



リストの順番処理

基本は拡張for文を使うがこれはイテレーターを利用したシンタックスシュガー(糖衣構文)
※糖衣構文:人間が読み書きしやすいように用意された構文


List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

for (String s : list) {
  System.out.println(s);
}



イテレーター

イテレーターはIteratorインターフェイスで定義され、コレクションからiteratorメソッドを利用することで、それぞれの実装に応じたiteratorオブジェクトを取得できる


Iteratorの主なメソッド

boolean hasNext():次の要素が存在するか?
E next():次の要素を取得
void remove():現在の要素を削除



List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};


for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
  System.out.println(ite.next());
}



Iteratorを使うメリット

拡張for文ではできないことができる場合がある


List<String> list = new ArrayList<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

for (String s : list) {
  System.out.println(s);

  //リストの要素を削除する
  //拡張for文では実行時エラーとなる
  //list.remove(s);
}

for (Iterator<String> ite = list.iterator(); ite.hasNext();) {
  System.out.println(ite.next());

  //リストの要素を削除する
  //iteratorを使用する場合はOK
  ite.remove();
}



ArrayList or LinkedList

要素の挿入削除が多い場合はLinkedList、そうではない場合はArrayList




Setの使い方

//初期値を匿名クラスの初期化子で追加
Set<String> s = new HashSet<String>() {
  {
    add("Java");
    add("C#");
    add("Ruby");
  }
};

//要素の追加
s.add("PHP");

//重複した要素なので何も起こらない
s.add("Java");

//ArrayListに入っている要素を追加
s.addAll(new ArrayList<String>() {
  {
    add("PHP");
    add("JavaScript");
  }
});

//[C#, PHP, Ruby, JavaScript, Java]と表示
System.out.println(s);

//要素を削除
s.remove("Perl");

//全要素を削除
s.clear();




Mapの使い方

//<キーの型,値の型>
Map<String, String> map = new HashMap<String, String>(){
  //匿名クラスの初期化子で初期値を追加
  {
    //追加
    put("red", "赤");
    put("green", "緑");
    put("blue", "青");
  }
};

//キーが含まれているか?
//「true」と表示
System.out.println(map.containsKey("red"));

//値が含まれているか?
//「false」と表示
System.out.println(map.containsValue("紫"));

//キーを順番に取り出す
for (String key : map.keySet()) {
  System.out.println(key);
}

//値を順番に取り出す
for (String val : map.values()) {
  System.out.println(val);
}

//キーと値の両方を取り出す
for (Map.Entry<String, String> entry : map.entrySet()) {
  System.out.printf("%s:%s\n", entry.getKey(), entry.getValue());
}



キュー(Queue)

先入れ先出し(FIFO:First in First Out)と呼ばれるデータ構造で「待ち行列」とも呼ばれる




スタック(Stack)

後入れ先出し(LIFO:Last in First Out)と呼ばれるデータ構造



//キュー(FIFO:First in First Out)
Deque<Integer> dq = new ArrayDeque<Integer>();


//追加
dq.addLast(1);
dq.addLast(2);
dq.addLast(3);

//[1, 2, 3]と表示
System.out.println(dq);

//最初の要素を取り出し、削除
//1と表示
System.out.println(dq.removeFirst());

//[2, 3]と表示
System.out.println(dq);


//スタック(LIFO:Last in First Out)
Deque<Integer> dq2 = new ArrayDeque<Integer>();

//追加
dq2.addLast(1);
dq2.addLast(2);
dq2.addLast(3);

//[1, 2, 3]と表示
System.out.println(dq2);

//最後の要素を取り出し削除
//3と表示
System.out.println(dq2.removeLast());

//[1, 2]と表示
System.out.println(dq2);

0 件のコメント:

コメントを投稿