Sojiro’s Blog

This is nothing much.

WordPress on GAE

GCP の事前準備

以下の準備が事前に必要。ここでは割愛。

  • GCP アカウントの作成
  • GCP プロジェクトの作成
  • 課金の有効化
  • GCS(Google Cloud Storage)に default bucket を作成
    • default bucket: YOUR_PROJECT_NAME.appspot.com
  • Google Cloud SDK のインストール

Composer のインストール

Composer は PHP のパッケージ管理ツール

Composer is strongly inspired by node’s npm and ruby’s bundler.

node の npm、ruby の bundler に強く影響を受けているようだ。

手順

PHP がインストールされていることを確認

1
2
$ php -v
PHP 5.5.38 (cli) (built: Aug 21 2016 21:48:49)

こちら の手順通り Composer をインストール

1
2
3
4
$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$ php -r "if (hash_file('SHA384', 'composer-setup.php') === '669656bab3166a7aff8a7506b8cb2d1c292f042046c5a994c43155c0be6190fa0355160742ab2e1c88d40d5be660b410') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
$ php composer-setup.php
$ php -r "unlink('composer-setup.php');"

グローバルに呼び出せるようにしておく

1
$ mv composer.phar /usr/local/bin/composer

GCS へのアクセス権限変更

作成したプロジェクトでログイン

1
2
$ gcloud auth login
$ gcloud config set project YOUR_PROJECT_NAME

GCS のアクセス権更新

1
$ gsutil defacl ch -u AllUsers:R gs://YOUR_PROJECT_NAME.appspot.com

DB の setup

Cloud SQL を使うので GCP の SQL メニューへ

  • wp という名前で MySQL のインスタンスを立てる
    • このとき第2世代を選択可
    • 日本向けがメインであればリージョンは asia-northeast1
    • マシンタイプは安さ重視なら db-f1-micro とする (このタイプで 30円/日 という印象)
    • 他の設定はお好みで…
  • 立てたインスタンスに wp という名前のデータベースを作る
  • wp というユーザーを作る
    • アクセス制御 → ユーザー → ユーザーアカウントを作成、とたどる

WordPress の setup

ローカルに WordPress 構築に必要な素材を用意する

1
2
3
$ git clone https://github.com/GoogleCloudPlatform/php-docs-samples.git
$ cd php-docs-samples/appengine/wordpress/
$ composer install

setup の開始

1
2
$ php wordpress-helper.php setup
# ここでいろいろインタラクティブに聞かれるので答える

※ リージョンを聞かれるが、 asia-northeast1 がない場合は適当に答えておく

DB_HOST 設定を変更

上記手順で WordPress setup 時に適切なリージョンが指定できなかった場合には自分で一部設定を変更する

1
2
cd YOUR_PROJECT_NAME/
vim wordpress/wp-config.php

/** Production login info */ 以下の DB_HOST を適切なリージョンを用いて変更する

Deploy

GAE インスタンスに WordPress を deploy する

1
$ gcloud app deploy --promote --stop-previous-version app.yaml cron.yaml

最初はかなり多くのファイルを GCS にあげるのでそこそこ時間がかかる

参照

https://github.com/GoogleCloudPlatform/php-docs-samples/tree/master/appengine/wordpress

Unix Timeの扱い

Unix time を日付及び時刻に変換したいこと、日付及び時刻を Unix time に変換したいことがある。

Unix time からの変換

1
2
$ date -r 616388399
1989年 7月14日 金曜日 11時59分59秒 JST

日付からの変換

1
2
$ date -jf '%Y-%m-%d %H:%M:%S' '1989-07-14 11:59:59' +%s
616388399

ファイルの内容を直接クリップボードに出力する

ファイルの内容を直接クリップボードにコピーしたり、クリップボードからファイルにペーストしたいことがある。

クリップボードにコピー

1
$ cat ~/Desktop/memo.text | pbcopy

クリップボードからペースト

1
$ pbpaste > ~/Documents/memo.text

Could Not Open the Requested Socket エラーが出たら

Android Studio で GAE に乗せるアプリを開発しているとき、ローカルで立ち上げようとすると以下のようなエラーが出た

1
2
Could not open the requested socket: Address already in use
Try overriding --address and/or --port.

どうやら以前立ち上げた際のプロセスが生き残っていて邪魔しているらしい

以下のように対応した

Android Studio のメニューから RunEdit Configurations... とたどり、対象 module が使っている port を確認

ポートを指定してプロセスを確認、そして kill

1
2
3
$ lsof -i:8080
COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
java    74792  sojiro   48u  IPv6 0xXXXXXXXXXXXXXXXX      0t0  TCP *:http-alt (LISTEN)
1
$ kill -9 74792

パーフェクトJava読書メモ Chapter 10 Javaプログラムの実行と制御構文

Javaを使うために改訂2版 パーフェクトJavaを読んだメモ

条件分岐

if-else文

インデントの取り方に関係なく else は最も近い(直近の) if にかかる

switch-case文

1
2
3
4
5
6
7
8
switch() {
case 定数1:
    0個以上の文
case 定数N:
    0個以上の文
default:
    0個以上の文
}

式の評価値は以下

  • int 型
  • int 型に暗黙に型変換される型
    • char
    • byte
    • short
  • 数値ラッパークラス
    • Integer
    • Character
    • Byte
    • Short
  • enum 型
  • String 型

式が null になると NullPointerException が発生する

break; 文がない限り処理を続ける

1
2
3
4
5
6
7
8
9
int i = 0;
switch (i) {
    case 0:
        System.out.println(0);
    case 1:
        System.out.println(1);
    default:
        System.out.println("default");
}
1
2
3
0
1
default

繰り返し

for文

1
2
3
for (初期化式; 条件式; 更新式) {
    
}

条件式は評価値が boolean もしくは Boolean

初期化式と更新式は複数の式を , で区切って書ける

1
2
for (int i = 0, j = 0; (i < 10 && j < 10); i++, j++) {}

異なる型の宣言と初期化を並べるとエラー

1
2
for (int i = 0, byte j = 0; (i < 10 && j < 10); i++, j++) {}

for文の外に出して回避

1
2
3
4
int i;
byte j;
for (i = 0, j = 0; (i < 10 && j < 10); i++, j++) {}

ジャンプ

break文

  • ループを抜ける
  • ループがネストしている場合、抜けるのは内側のループのみ

continue文

  • ループ内の文をスキップしてループの条件式の評価に戻る

ラベル

  • 繰り返しにラベルをつける
  • break文、continue文にラベルを渡すことでどの繰り返しを対象にするか定める
1
2
3
4
5
6
target_loop:
while (true) {
    while (true) {
        break target_loop;
    }
}
  • 外側の while ループに target_loop というラベルがつく
  • break target_loop で外側の while ループを抜ける
  • continue の場合は対象のループ内の文をスキップして条件式の評価に戻る)

参照

改訂2版 パーフェクトJava

パーフェクトJava読書メモ Chapter 9 文、式、演算子

Javaを使うために改訂2版 パーフェクトJavaを読んだメモ

Java の文法と文

Java で扱う文は以下の4種類

  • 制御文
  • ブロック文
  • 宣言文
  • 式文

宣言文と式文は終端にセミコロン必要

Java の演算子と式

  • &&||? : (三項演算子)を除くすべての演算子は演算前にすべてのオペランドを評価する
  • オペランドは左のものから評価する
  • メソッド及びコンストラクタ呼び出し式では引数を左から評価する

数値の演算

インクリメント/デクリメント演算の前置後置

前置演算は評価値が演算後の値、後置演算は評価値が演算前の値

1
2
3
4
5
6
int n = 10;
int m = ++n;
// m: 11, n: 11
int n = 10;
int m = n++;
// m: 10, n: 11
1
2
3
4
int n = 0;
while (++n < 10) {
    // ループが回る回数は 9 回
}

論理演算

遅延評価

&&|| は遅延評価を行う

  • && は左辺の評価値が偽であれば右辺の評価値に関係なく結果が偽になるため右辺を評価しない
  • || は左辺の評価値が真であれば右辺の評価値に関係なく結果が真になるため右辺の評価をしない

これらの演算子の右辺に副作用のある式を書くと、左辺の評価値により実行さるか否かが変わってくるので注意

その他の演算

instanceof 演算子

ダウンキャストを安全に行えるかを事前にチェックする

参照

改訂2版 パーフェクトJava

GAE Task Queue について

GAE の Task Queue についてざっくり。

具体的な実装に関しては別エントリに書こうと思います。

Overview

  • アプリケーションの処理(task)を Task Queue API に渡す(queue)ことでユーザーからのリクエスト外で非同期に処理させることができる
  • task はスケーラブルな GAE の Worker モジュールによってバックグラウンドで処理される

Task Queue には以下の2つのタイプがある

Push queues

  • queue を処理する間隔をあらかじめスケジュールすることができる
  • queue は GAE のモジュールへのリクエストとして処理される
  • task の処理には期限がある
    • 自動的にスケーリングするモジュールで処理するものは 10 分以内
    • そうでないモジュールで処理するものは 24 時間以内

Pull queues

  • GAE は queue を処理せず、外部の Worker が queue を取得(lease)して処理する
  • 外部の Worker で、どのような間隔で queue を処理するか管理する必要がある
  • lease のタイミングで外部 Worker に対して queue の処理期限が渡される
  • 期限内に queue の処理が完了するか、 queue が削除されない場合は同一 queue に対する他の Worker プロセスからの lease を許可する

queue の処理は非同期で行われるので task を作成したアプリケーションは処理の結果を知ることができないが、処理に失敗した場合は自動でリトライ処理が走る

Use cases

Push queues

  • SNS メッセージアプリケーションでユーザーがメッセージを送るたびにフォロワーの更新を非同期で行う
  • キャンペーン広告の送信をあらかじめスケジュールしておいて決められた時間に送る

Pull queues

  • バッチジョブに効果がある
  • task に tag をつけることで外部 worker が lease する時に同じ tag がつく task をまとめて処理することができる
  • 実装例としては複数のゲームのリーダーボードが典型的
    • ハイスコアの更新があるたびに game id を tag として、 score と user 名を enqueue する

Push queue

  • Push queue は GAE の Worker モジュールに HTTP リクエストで task を渡す
  • このリクエストは一定の間隔で実行され、失敗すると新たなリクエストをもってリトライされる
  • タスクの種類ごとにハンドラを書く必要がある
  • 1つのモジュールに各種類ごとのハンドラを用意することができる
  • task の種類ごとに別々のモジュールを使うこともできる

Working with push queues

  • task を作って push queue に enqueue するプログラムを書く
  • task リクエストを受け取って GAE モジュールに渡すハンドラを書く
  • quota を気にする必要がある

Pull queue

  • Pull queue を使うことで独自のシステムから GAE の task を処理することができる
  • ここでいう独自のシステムには GAE で構成されたシステムも含まれる
  • GAE アプリケーションからは com.google.appengine.api.taskqueue パッケージを使って task を処理できる
  • その他のアプリケーションからは Task Queue REST API を使う
  • Push queue では GAE がやってくれる以下の処理を自前で用意する必要がある
  • アプリケーション側で worker スケールの管理をする
  • アプリケーション側で処理の終わった task を削除する
  • Pull queue は queue.xml という設定ファイルが必要

Pull queue による task 処理の流れ

  1. アプリケーションが task を lease する
  2. GAE が task データを返す
  3. アプリケーションが task を処理する
    1. もし lease の期限以内に処理を完了できなかった場合は再度 lease できる
    2. retry できる最大の回数はあらかじめ設定できる
    3. この回数を超えると GAE が task を削除する
  4. アプリケーションは task への処理が完了したら必ずその task を削除する

参照

GAE Datastore について

GAE の Datastore についてざっくり

Overview

  • スキーマのないオブジェクトデータベース
  • オートスケーリング
  • ディスクへの書き込み時に自動で暗号化、読み出し時に自動で復号
  • 計画的ダウンタイムなし
Concept Cloud Datastore Relational database
オブジェクトが所属するカテゴリ Kind Table
オブジェクト Entity Row
オブジェクトがもつデータ Property Field
オブジェクトを特定する ID Key Primary key
  • 同一 kind の entity でも違う property を持ちうる
  • それぞれの entity は同名の property でも型の違うデータを持ちうる

Other storages

  • 複数の table の join や 複数のカラムに対する不等号比較など、すべての SQL 操作が必要なら Google Cloud SQL
  • ACID transaction を必要としないスキーマレスなデータを扱うなら Google Bigtable
  • オンラインで分析されるデータを扱うなら Google BigQuery
  • 画像や動画などの変更がない大きなデータを扱うなら Google Cloud Storage

Entities

  • ひとつの entity は1つ以上の property をもつ
  • property は1つ以上の値を取りうる

Keys

  • key は entity を特定する
  • key は以下を含む
    • entity の kind
    • 以下のいずれかの識別子
      • 文字列の key name
      • 数値の ID
    • ancestor path (optional)
  • 識別子は entity が生成されたタイミングで設定される
  • 一度設定された識別子は変更されない
  • 識別子は以下の2つの方法で設定できる
    • アプリケーションで特定の key name を設定する
    • Datastore が自動で発行する数値の ID を使う

Ancestor paths

  • Datastore はファイルシステムのディレクトリ構成に似た階層構造をもつ
  • entity 作成時に parent entity を指定することができる
  • parent entity が指定されない entity は root entity と呼ぶ
  • 一度親子関係ができた entity はその関係が変更されることはない
  • 同じ parent をもつ2つの entity に対して Datastore は同一の ID を払い出すことはない
  • 同様に2つの root entity に対して同一の ID を払い出すこともない
  • parent や parent の更に parent を ancestor と呼ぶ
  • 逆に children や children の更に children を descendant と呼ぶ
  • root entity とその descendant は同一の entity group に属する
  • ancestor path は root entity からたどって該当の entity に到達するまでの親子関係で構成される

Transactions and entity groups

  • entity に対する create, update, delete はトランザクションで管理される
  • トランザクションにはこれらの複数の操作が内包される
  • トランザクションは一貫性の担保のため、そこに含まれる操作をひとまとまりとして適用する、あるいはいずれかの操作が失敗するとすべての操作を適用しない
  • commit を試みてエラーが返ってきたとしても、トランザクションが失敗したとは限らない
    • DatastoreTimeoutExceptionDatastoreFailureException といったエラーが返ってきたとしても、 commit は成功している可能性がある
    • このため、 Datastore は可能な限り同一のトランザクションを複数回適用しても最終的な結果が変わらないように設計すべきである
  • entity group とトランザクションの関係
    • 1つのトランザクションで扱うデータは 25 の entity group 内に収まっていなければならない
    • トランザクション内でクエリを発行する場合には正しいデータにマッチする ancestor filter を指定できるように entity group 内のデータを設計する必要がある
    • 広域に渡って各 entity group をレプリケーションするために、1つの entity group ごとに1秒あたりの書き込みスループットの上限値が定められている

Understanding write costs

  • 発生する書き込み
    • entity 自身
    • EntitiesByKind という index
    • 1つの property value ごとに EntitiesByPropertyEntitiesByPropertyDesc の各 index

以下のような entity を考える

1
2
3
4
Key: 'Foo:1' (kind = 'Foo', id = 1, no parent)
A: 1, 2
B: null
C: 'this', 'that', 'theOther'

このとき発生する書き込み

  • 1: entity 自身
  • 1: EntitiesByKind index
  • 4: A property の2つの値にそれぞれ2つの index
  • 2: B property の値に2つの index (null でも必要)
  • 6: C property の3つの値にそれぞれ2つの index

よって発生する書き込みの合計は 1 + 1 + 4 + 2 + 6 = 14

上記の entity に複合 index を追加することを考える

1
2
Kind: 'Foo'
A ▲, B ▼, C ▼

このとき、各 property の値の組み合わせ分の書き込みが発生する

1
2
(1, null, 'this') (1, null, 'that') (1, null, 'theOther')
(2, null, 'this') (2, null, 'that') (2, null, 'theOther')

したがって発生する書き込みの合計は 1 + 1 + 4 + 2 + 6 + 6 = 20

次にこれまでと同様に ancestor が存在する以下のような entity を考える

1
2
3
4
Key: 'GreatGrandpa:1/Grandpa:1/Dad:1/Foo:1' (kind = 'Foo', id = 1, parent = 'GreatGrandpa:1/Grandpa:1/Dad:1')
A: 1, 2
B: null
C: 'this', 'that', 'theOther'
1
2
3
Kind: 'Foo'
A ▲, B ▼, C ▼
Ancestor: True

このとき各 property の値と各 ancestor 及び自身の entity の組み合わせ分の書き込みを必要とする

1
2
3
4
5
6
(1, null, 'this', 'GreatGrandpa') (1, null, 'this', 'Grandpa') (1, null, 'this', 'Dad') (1, null, 'this', 'Foo')
(1, null, 'that', 'GreatGrandpa') (1, null, 'that', 'Grandpa') (1, null, 'that', 'Dad') (1, null, 'that', 'Foo')
(1, null, 'theOther', 'GreatGrandpa') (1, null, 'theOther', 'Grandpa') (1, null, 'theOther', 'Dad') (1, null, 'theOther', 'Foo')
(2, null, 'this', 'GreatGrandpa') (2, null, 'this', 'Grandpa') (2, null, 'this', 'Dad') (2, null, 'this', 'Foo')
(2, null, 'that', 'GreatGrandpa') (2, null, 'that', 'Grandpa') (2, null, 'that', 'Dad') (2, null, 'that', 'Foo')
(2, null, 'theOther', 'GreatGrandpa') (2, null, 'theOther', 'Grandpa') (2, null, 'theOther', 'Dad') (2, null, 'theOther', 'Foo')

したがって発生する書き込みの合計は 1 + 1 + 4 + 2 + 6 + 24 = 38

参照

パーフェクトJava読書メモ Chapter 7 インターフェース

Javaを使うために改訂2版 パーフェクトJavaを読んだメモ

クラスとインターフェースの違い

  • インターフェースは雛形としての役割を持たない
  • インターフェースは実体化できず、型定義に特化している
  • インターフェースの継承の目的は多様性のみ(クラスの拡張継承には実装コードの共有という側面もある)

インターフェース宣言

1
2
3
[修飾子] interface インターフェース名 {
    メンバ宣言
}

インターフェースの修飾子

modifier description
public グローバルにアクセス可(書かないとパッケージ内に限定)
abstract インターフェースは暗黙的に abstract なので書かなくても同じ
strictfp インターフェース内に記述した浮動小数点演算を厳密に評価
アノテーション 省略

インターフェースのメンバ

  • 抽象メソッド(実装なし)
  • default メソッド
  • static メソッド
  • 定数フィールド
  • static なネストしたクラス
  • static なネストしたインターフェース

メソッド宣言

modifier description
public 暗黙的に public なので書かなくても同じ
default デフォルトメソッド
static static メソッド
abstract 暗黙的に abstract なので書かなくても同じ
  • default メソッドはインスタンスメソッド
  • インターフェースを継承した具象クラスのインスタンスメソッドになる
  • フィールド変数は暗黙的に public static final

インターフェースと実装クラス

インターフェース継承

1
2
3
[修飾子] class クラス名 implements インターフェース名 {
    クラス本体
}

複数のインターフェースを同時に継承することができる

1
2
3
[修飾子] class クラス名 implements インターフェース名, インターフェース名 {
    クラス本体
}
  • クラスの拡張継承とインターフェースの継承を同時にできる
  • このとき implementsextends より後に書く
1
2
3
[修飾子] class クラス名 extends 親クラス名 implements インターフェース名 {
    クラス本体
}
  • インターフェースを継承したクラスはインターフェースの抽象メソッドをすべてオーバーライドする必要がある
  • インターフェースから継承したメソッドのアクセス制御は public 修飾子がないとコンパイルエラー
  • メソッドのオーバーライドはクラスの拡張継承と同様に行う

ネストしたインターフェース

  • クラス内のネストしたインターフェース
    • public protected 無指定 private のいずれかを指定する
  • インターフェース内のネストしたインターフェース
    • 常に public
  • インターフェース内のネストしたクラス
    • 常に public かつ static
  • ネストして宣言されたインターフェースは常に static

インターフェース自体の拡張継承

  • インターフェースも拡張継承できる
  • インターフェースは複数の親インターフェースを指定可能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Parent {
    void print();
}

interface Child extends Parent {
    // void print() を継承
}

interface Father {
    void print();
}

interface Mother {
    void exec();
}

interface Child2 extends Father, Mother {
    // void print(), void exec() を継承
}

参照

改訂2版 パーフェクトJava

パーフェクトJava読書メモ Chapter 6 コレクションと配列

Javaを使うために改訂2版 パーフェクトJavaを読んだメモ

コレクションフレームワーク

—– Hash Table Array Tree Linked List Hash Table + Linked List
set HashSet TreeSet LinkedHashSet
list ArrayList LinkedList
deque ArrayDeque LinkedList
map HashMap TreeMap LinkedHashMap

コレクション型オブジェクトの生成

1
コレクションのインターフェース型<要素の型> 変数 = new コレクションのインターフェースを実装した具象クラス<>([コンストラクタの引数]);
1
List<String> list = new ArrayList<>();
  • 上記例で ArrayList<>ArrayList<String> の略
1
List<int> list = new ArrayList<>();
  • 要素の型に基本型は指定できない

リスト

リストの具象クラス

  • ArrayList
  • LinkedList

ArrayList

  • 良い点
    • インデックスを指定して要素を読み出す速度が速い[get]
    • インデックスを指定して要素を書き換える速度が速い[set]
    • 先頭から順にすべての要素をなめる処理が速い
  • 悪い点
    • 要素の挿入が遅いことがある[add]
      • 先頭に近い位置への挿入は遅い
      • 末尾に近い位置への挿入は速いときもあるが遅いときもある
    • 要素の削除が遅いことがある[remove]
      • 先頭に近い位置の削除ほど遅い
      • 末尾に近い位置の削除ほど速い
      • 最末尾の削除は高速
    • 条件に合致した要素の検索があまり速くない[contains, indexOf, lastIndexOf]
  • 要素の順序をもつので、順序が入れ替わる要素が多くなる処理は遅い
  • 連続したメモリの確保ができない場合、末尾への挿入は遅くなる

LinkedList

  • 良い点
    • 要素の挿入が速い[add]
    • 要素の削除が速い[remove]
  • 悪い点
    • インデックスを指定して要素を読み出す速度はあまり速くない[get]
    • インデックスを指定して要素を書き換える速度はあまり速くない[set]
    • 条件に合致した要素を検索する処理の速度はあまり速くない[contains, indexOf, lastIndexOf]
  • 要素にたどり着くまでリンクをたどるのでリストの真ん中に近い要素ほどアクセスに時間がかかる

マップ

1
2
Map<String, Integer> map = new HashMap<>();
Map<Integer, List<String>> map = new TreeMap<>();
  • Map インターフェースはキーと値の両方の型を <> の中に指定する
  • List インターフェース同様 <> に指定できるのは参照型のみ

マップの具象クラス

  • HashMap
  • LinkedHashMap
  • TreeMap

HashMap

HashMapの内部動作

  • HashMap は内部に配列を確保する。これをハッシュ表と呼ぶ。
  • ハッシュ表の初期サイズは HashMap のコンストラクタで指定する。
  • キーと値のペアを HashMap に追加[put]すると HashMap は内部でキーをハッシュ関数に通す。
  • ハッシュ関数の出力がハッシュ表のインデックスになる。
  • 得られたインデックスの要素としてキーと値のペアを格納する。

LinkedHashMap

LinkedHashMapの内部動作

  • HashMap を拡張継承している
  • ハッシュ表に加えて、 LinkedList と同じリンクリストを内部に保持する
  • 要素を追加すると内部でハッシュ表とリンクリストの両方に要素を追加する
1
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
  • 上記コンストラクタの accessOrdertrue を指定するとリンクリストの順序が要素にアクセスした順になる

TreeMap

二分探索木の一種である赤黒木と呼ばれるアルゴリズムによるツリー構造で要素をもつ

NavigableMap

TreeMap がキーの順序をもつことを利用したインターフェース

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Sample {
    public static void main(String... args) {
        NavigableMap<String, String> map = new TreeMap<>();
        map.put("kanayama", "one");
        map.put("kawai", "four");
        map.put("kushibiki", "five");

        Map.Entry<String, String> entry = map.ceilingEntry("kana");
        if (entry != null) {
            entry.getKey() + ": " + entry.getValue();  // "kanayama: one"
        }
    }
}

セット

  • セットは数学の集合の概念
  • 要素の重複を許さない
  • Set インターフェースを実装した具象クラスは以下の3つ
    • HashSet
    • LinkedHashSet
    • TreeSet
  • 要素の重複を許さない点が Map のキーの性質と重なるため、具象クラスも Map と重なる

スタック、キュー、デック

  • スタック: 最新要素から常に取り出し
  • キュー: 最古要素から常に取り出し
  • デック: 上記どちらにも対応
  • スタック構造は Deque クラス(Stack クラスは古い)
  • キュー構造は Queue クラス

イテレータ

拡張for構文

1
2
3
for (要素型 ループ変数 : コレクション変数) {
    forループが回るたびにループ変数が要素オブジェクトを順に参照
}

イテレータ

  • イテレーションを抽象化したオブジェクト
  • Iterator インターフェースは以下のメソッドをもつ
    • boolean hasNext();
    • E next();
    • void remove();
  • ListIterator インターフェースは Iterator を継承している
  • ListIterator は上記に加えて以下の以下のメソッドをもつ
    • boolean hasPrevious();
    • E previous();

セットに対するイテレーション

1
2
3
4
5
6
7
8
9
// Set<Integer> set
for (Integer n : set) {
    sum += n;
}

for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
    Integer n = it.next();
    sum += n;
}

マップに対するイテレーション

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Map<String, String> map

// 拡張 for でキーを繰り返す
for (String key : map.keySet()) {
    System.out.println(key);
}

// イテレータで値を繰り返す
for (Iterator<String> it = map.values().iterator(); it.hasNext(); ) {
    String val = it.next();
    System.out.println(val);
}

// 拡張 for でキーと値を繰り返す
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

リストを逆順になめる

1
2
3
4
5
// List<Integer> list
for (ListIterator<Integer> it = list.listIterator(list.size()); it.hasPrevious(); ) {
    Integer i = it.previous();
    System.out.println(i);
}

配列

1
2
3
int[] arr = new int[3];

int[] arr = { 0, 1, 2 };

多次元配列の初期化

1
2
3
4
5
6
7
8
9
10
StringBuilder[][][] arr = {
    {
        { new StringBuilder("000"), new StringBuilder("001") },
        { new StringBuilder("010"), new StringBuilder("011") },
    },
    {
        { new StringBuilder("100"), new StringBuilder("101") },
        { new StringBuilder("110"), new StringBuilder("111") },
    },
};

参照

改訂2版 パーフェクトJava