⑤繰り返し処理とリストを組み合わせる

課題3 リストの練習 解答

①リストの作成と要素の追加

    // リストの宣言
    List<Card> cards = new ArrayList<>();

    // リストに要素を追加
    cards.add(new Card("スペード", 1));
    cards.add(new Card("ハート", 1));
    cards.add(new Card("ダイヤ", 1));
    cards.add(new Card("クローバー", 1));

②要素の取得

    Card card = cards.get(2);
    System.out.println(card.getSuit() + "の" + card.getNumber());

image

③(応用)すべての要素の取得・表示

    for (int i = 0; i < 4; i++) {
        Card card = cards.get(i);
        System.out.println(card.getSuit() + "の" + card.getNumber());
    }

image

その機能は誰が持つ?

「カードを表示する」という機能、前に書いた気がしませんか?
そう、課題1で書いたコードと同じはずです

うーん、これから「カードを表示して」と言われるたびに
同じコードを書くのは少々面倒ですね(ということにしましょう)
というわけで、このようなよく使う処理は
メソッドとして切り出しておき、いつでも使えるようにしましょう

じゃあさっそく…

    public class App {

        public static void main(SuitEnum[] args) throws Exception {

            // リストの宣言
            List<Card> cards = new ArrayList<>();

            // リストに要素を追加
            cards.add(new Card("スペード", 1));
            cards.add(new Card("ハート", 1));
            cards.add(new Card("ダイヤ", 1));
            cards.add(new Card("クローバー", 1));

            for (int i = 0; i < 4; i++) {
                Card card = cards.get(i);
                disp(card);
            }

        }

        // cardを引数として受け取って、それを表示するメソッド
        private static void disp(Card card) {
            System.out.println(card.getSuit() + "の" + card.getNumber());
        }

    }

こんな感じで、dispメソッドを作りましょうか
カードを表示したくなるたびにこのメソッドを呼び出せば楽ですね

と、ここで終わってしまったら、この余談は生まれないわけです
(もちろん、上記のように作ってもいいですが)

大切なのは、カードを表示するという機能は
カードが存在しないと生まれないということです
そこにカードが存在するから、見たくなるんですよね

こういう場合、カードを表示する機能はカードクラスに実装すべきです
オブジェクト指向では「単一責任の法則」と称される部分です
表示メソッドがクラス内にあることで
今後クラスの属性に変更が加わったとしても、修正はクラス内のみで完結します

単一責任の法則

というわけで、ついでにメソッド名もちょっと工夫して
このようにしてみました

    public class Card {

        /* 属性 */
        private String suit; // スート
        private int number; // 数値

        /* 記載省略 */

        // 自身の情報を表示するメソッド
        public void openFace() {
            System.out.println(this.suit + "の" + this.number);
        }

    }
    public class App {

        public static void main(SuitEnum[] args) throws Exception {

            /* 記載省略 */

            for (int i = 0; i < 4; i++) {
                Card card = cards.get(i);
                card.openFace;
            }

        }
    }

べた書きでSystem.out.printlnを書くよりも
何となく意味が通りやすくなったなぁ…と感じてくれたらうれしいです

(休題)繰り返し処理とリストを組み合わせる

リストのサイズを取得して使用する

話を戻して…
課題3-③のfor文の条件指定は、4回処理を繰り返すためのものですが
リストの要素がそれよりも少なかったらどうでしょう

    List<Card> cards = new ArrayList<>();

    cards.add(new Card("スペード", 1));
    cards.add(new Card("ハート", 1));
    cards.add(new Card("ダイヤ", 1));

    for (int i = 0; i < 4; i++) {
        Card card = cards.get(i);
        card.openFace;
    }

image

java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3

おや、エラーが出てしまいました
length(リストの大きさ・要素数)が 3 なのに、
3 番目のインデックスを指定してしまっている…と怒られてしまったようです

あれ、大きさが 3 の時に 3 番目がダメなの?
そう、インデックスは 0 番目から始まるから、0, 1, 2 しか選べないんです

と、いうことは要素数が減ったら繰り返し条件も変える必要がありそうです
リストの要素数が増えた場合も同様ですね

でもそれって、ちょっと面倒じゃありませんか
また、要素数がわからないときはどうすればいいんでしょうか
というときに使える方法があります

    for (int i = 0; i < cards.size(); i++) {
        Card card = cards.get(i);
        card.openFace;
    }

size()は、リストの現在の要素数を返すメソッドです
これを使えば要素がいくつであろうと、その数だけ処理することができそうです
(i の値は 0 ~ 要素数-1 を遷移します)

リストそのものを繰り返しの条件に使用する(拡張for文)

上記の方法は、リストのサイズを取得して繰り返しの条件とし、
繰り返し処理の中で、各要素をgetしていますが、これは…

「リストの大きさはこれくらいだから、サイズの数だけ順番に数数えて」
「繰り返しごとに、今の番号の位置の要素取り出して表示して」

と指示するイメージに近いと思います
ただ、もっと雑に…

「リスト渡すから、それ順番に取り出して表示してよ」

と指示することはできないでしょうか
プログラマーは怠惰なので、より直観的で楽な書き方を目指そうとします
その結果が以下の記述です

    for (Card card : cards) {
        card.openFace;
    }

条件を(Card card : cards)という記述に変えることで
Card型の変数cardにリストcardsの要素が順番に代入され
結果として、課題3-③と同じ出力を得ることができます

このような記述方法を「拡張for文」と呼びます

好みにもよるかもしれませんが、両者を比較したときに
「リストの要素を順番に取り出して操作している」
と理解しやすい(読みやすい)のはどちらなのか、考えてみるのも楽しいかもしれません

    for (int i = 0; i < cards.size(); i++) 

        VS

    for (Card card : cards) 

まぁ、拡張for文にも問題はあるんですが…
(どこかのタイミングで説明するかも)

課題4 すべてのカードをつくる(繰り返し処理+リスト)

  • ①全52種類のカードを持つリストを作りましょう

  • ②拡張for文を使用して、全部のカードを表示しましょう