Java – プリミティブ型 – 最大値・最小値

概要

数値表現可能なプリミティブ型の最大値・最小値は、それぞれのラッパークラスの定数として定義されている。

WrapperClass.MAX_VALUE
WrapperClass.MIN_VALUE

整数型、実数型、文字型で、それぞれの意味が異なる。

整数型の最大値・最小値

整数型の最大値・最小値は、それぞれのビット長で表現可能な符号付整数の最大値(正数)、最小値(負数)。

byte型

short型

int型

long型

整数型のオバーフロー/アンダーフロー

整数型の最大値を超える演算や最小値を下回る演算をすると、その方の範囲内のビットパターンで表現される値になる。

実数型の最大値・最小値

実数型の最大値・最小値は整数型と異なり、次の値。

最大値
正の数で表現できる最大の数
最小値
正の数で表現できる絶対値が最も小さい数

したがって、負の領域で表現可能な最小値は最大値のマイナス、負の領域で表現可能な絶対値が最小の数は最小値のマイナスになる。

float型

double型

実数型のオーバーフロー/アンダーフロー

実数型の最大値を超える演算をするとInfinityまたは-Infinityになる。

実数型の最小値の絶対値を下回る計算をすると、0.0または−0.0になる。

文字型の最大値・最小値

char型

char型の最小値は文字コード0の文字、最大値は文字コード65535の文字。

文字型のオーバーフロー/アンダーフロー

char型の最大値を超える演算や最小値を下回る演算をすると、それで表現可能な整数型に変換される。

 

Java – 演算子 – 算術演算子

概要

算術演算子はbyte型~long型などの整数や実数(float型double型)を対象とした演算子で、文字型のcharも演算対象になる。

<単項演算子>

+a 正符号、意味はない
-a 負符号、正負反転
++a 参照前インクリメント
a++ 参照後インクリメント
--a 参照前デクリメント
a-- 参照後デクリメント

<二項演算子>

a + b 加算
a - b 減算
a * b 乗算
a / b 除算
a % b 剰余

単項演算子

+:正符号

数値リテラルや数値型変数の前に置く。正数・負数とも符号は変わらず、実質意味を持たない。

-:負符号

数値リテラルや数値型変数の前に置き、符号を反転させる。

符号演算子の連続

単項演算子は交互に連続して書くことができて、その結果は負符号’-‘が偶数回なら’+‘と同じ、奇数回なら’-‘と同じ結果になる。

ただしその中に--++のように連続した符号があると、インクリメント/デクリメント演算子と解釈されてエラーになる。

++/–:インクリメント/デクリメント

  • ++を変数の直前に書くと「変数が評価される前に」変数の内容が1増加する
  • ++を変数の後に書くと「変数が評価された後に」変数の内容が1減少する
  • 演算子と変数の間に空白が入ってもよい。

整数の例

以下の例では、演算子が前置された場合は表示前にインクリメント/デクリメントされ、後置された場合は表示後にインクリメント/デクリメントされる。

また、以下の例では二項演算子の項にインクリメント/デクリメント演算子を使った例。

 

上記の例をnの変化で表すと以下の様になる。

3(→4) + →5 – 5(→4) = 3(4)

実数の例

実数の場合も++で1.0増加、--で1.0減少になるが、計算誤差が出る。

char型の例

char型にも適用可能で、インクリメントで1文字進み、デクリメントで1文字戻る。

二項演算子

四則演算

四則演算は多言語と同じ記号で、整数・実数の関係も同じ。整数同士の演算は整数で桁数が多い方に自動的にキャストされ、2項のうちいずれかでも実数なら結果も実数になる。

+:加算

-:減算

*:乗算

/:除算

%:剰余

abで割った時の剰余(余り)をa % bで与える。実数表現でも整数商に対する剰余が計算される。

除数・被除数が小数部を持つ場合、整数商に対する剰余が実数で計算される。

除数・被除数が負数の場合は、商と剰余の以下の式から考える。

(1)    \begin{equation*} a / b = q, \; a \% b = r \quad \Leftrightarrow \quad a = b q + r \end{equation*}

たとえば以下のように。

(2)    \begin{equation*} -7 \% (-2) = -1 \quad \Leftarrow \quad -7 = -2 \times 3  - 1 \end{equation*}

 

ゼロによる除算

整数除算でゼロで割ると実行時例外となる。剰余も除算を行っているので同じ。

浮動小数点演算の場合にゼロで割ると例外にならず、結果はInfinityとなる。剰余計算の場合は結果はNaN

べき乗

べき乗の演算子はなく、必要ならMathpow()メソッドを使う。

 

PHP – 対話モード

対話モード

開始

-aオプションをつけてphpコマンドを実行する。

php -a

終了

exit

実行例

日本語が入力できない

phpの対話モードで日本語を入力しようとしたところ、変換候補は表示されるが確定すると消えてしまう。

MySQLでもそのような症状があったが、OSのロケール設定とMySQLの文字コード設定で解決した。

OSのロケールは共通でUTF-8、日本語を含むコードをファイルから実行した場合は問題なく表示される。

原因はわかっていない。

phpコマンドのみの場合

オプション-aをつけずにphpコマンドを入力した場合もスクリプト実行ができるが

  • プロンプトが表示されない
  • Enterを入力しても実行されない
  • exitを入力しても終わらない
  • Ctrl-Cで抜け出せるが抜けるだけ

PHPマニュアルに”–with-readline オプションつきで PHP をコンパイルした場合に CLI SAPI で対話シェルが使えるようになりました。”とあるが、このオプションなしでコンパイルされている?

対応策

以下の2点。

  • PHPの実行タグ<?phpで始める
  • 入力終了後にCtrl-Dを入力する(Ctrl-Dで入力を終了させる)

 

 

 

Java – 配列 – 同一性・等価性

概要

配列の同一性は変数が指しているインスタンスが同じかどうかのみによるが、等価性の場合は末端の要素一つ一つが等価かどうかを判定することになる。

Javaの配列で等価性を判定する場合、1次元配列はArrays.equals()で等価性を判定できるが、2次元以上の配列になるとArrays.deepEquals()で判定しなければならない。

配列のコピーでもディープコピーができるメソッドがなくループで回さないといけないなど、Javaの配列は使いにくい。というよりも、下手に使うと思わぬところでミスの要因になりそう。

1次元配列

同一性(==)

配列変数が指している対象が同じかどうか。内容が同じでも変数が指しているインスタンスが異なる場合は同一にならない。

以下の例ではarray1array2が同じ配列インスタンスを指している(ハッシュ値が等しい)ので、内容が変わっても同一。

一方で変更前のarray1array4、変更後のarray1array3は、内容が同じでも配列のインスタンスが異なるため、同一とは判定されない。

等価性(Array.equals)

配列の内容が同じかどうか。異なる配列インスタンスでも内容が同じなら等価。同値性とも。

配列の等価性をチェックするにはArrays.equals()メソッドを使う。

以下の例では、array1array2は同一であり等価。array1の変更前はarray3と等価だが、変更後にはarray4と等価になる。

equals()メソッドは要注意

配列もオブジェクトとしてequals()メソッドを備えているが、これは同一性の判定しかせず、等価性の判定には使えない。

以下の例で、結果が==のときと同じになっているのが確認できる。

多次元配列

Array.equals()も要注意

Arrays.equals()clone()の挙動と似ていて、

  • 1次元目の各要素が同一なら結果はtrue
  • 1次元目の各要素のいずれかが同一でないなら結果はfalse

多次元配列でArray.equals()を使うと、2次元目の要素配列が同一であればtrueとなり、同一でなければ内容が同じでもfalseとなる。

以下の例では、

  • array2d1array2d2は元のインスタンスが同一であり等価
  • array2d1からclone()で作られたarray2d3は、元のインスタンスは異なるが、それが指している要素が同じなので同一ではないが等価
  • array2d3array2d4も、元のインスタンスから要素に至るまでarray2d1と異なるため、変更前/変更後に内容が同じになっても等価と判定されない

Arrays.equals()の等価性判定は1次元配列にしか使えない

深層比較(deepEquals)

Arrays.deepEquals()は、引数の2つの配列の要素の等価性を再帰的に判定する。

 

 

Java – 配列 – コピー

概要

Javaにおける配列のコピーについて整理する。

  • 1次元配列はclone()メソッドかSystem.arraycopy()メソッド
  • 多次元配列は次元分forでネストしてclone()System.arraycopy()

Javaの配列は扱いづらく、deepToString()deepEquals()はあるのにdeepCopy()がないのが不思議。

代入(assignment)

配列変数を他の変数に代入した場合、代入先の変数が元の変数と同じ配列を参照する。一方での変更が他方の結果にも反映される。

下記コードで、2つの配列のハッシュ値が同じこと、片方の変更が他方にも反映されることが確認できる。

clone()

clone()メソッドは配列の要素の「内容」を順次コピーして新たな配列を返す。一方の配列への変更は他方に影響しない。プリミティブ型の配列でも使える。

以下のコードで、2つの変数のハッシュ値が異なること、一方の変更が他方に影響していないことが確認できる。

System.arraycopy()

System.arraycopy()メソッドは引数にコピー元・コピー先の配列とコピー位置、コピー長さを指定して、コピー先にはコピー元の要素が複製された新たな配列が与えられる。clone()と同じく、一方の配列への変更は他方に影響しない。

System.copy(元配列, 元開始位置, 先配列, 先開始位置, コピー長)

clone()はシャローコピー

多次元配列をclone()でコピーしてもディープコピーにはならない。一方の配列への変更が他方に影響する。

以下のコードで、1次元目は違うハッシュ値でコピーされているが、それぞれが持っている要素配列はそのままのハッシュ値でコピーされている。

これは、clone()で元の配列の要素(配列)が新たなインスタンスとして複製されるが、その要素は単に代入されるだけで再帰的に複製されないため。

多次元配列のディープコピー

多次元配列をディープコピーするには、最後の要素までclone()で複製する必要がある。

2つの配列を同時に扱うのにPythonのzipのような機能がないので、カウンターで回さないといけない。

 

Java – 配列 – Object配列

概要

Object配列は、任意のオブジェクトを要素とすることができ、異なる型の要素を持つことができる。

1次元のObject配列

イニシャライザー

配列の型をObject[]とすると、任意の型・クラスの値を要素に持つことができる。

newによる初期化

多次元のObject配列

多次元のObject配列をイニシャライザーで宣言することもできるが、ダイナミックに要素を追加していくこともできる。

以下の例では、まず1次元のObject配列を要素数を与えて生成し、1つ目の要素はString型の要素、2つ目と3つ目はObject配列を要素配列として生成してセットしている。

 

 

Java – 配列 – 多次元配列

初期化

多次元配列の型はtype[][]...で表し、1次元と同じくイニシャライザーやnewで初期化できる。

newとイニシャライザーを使うと、要素配列を個別に定義することが可能。

内容表示

多次元配列を表示するには、ループで回すかArrays.deepToString()を使う(Java – 配列 – 内容表示)。

 

Java – 配列 – 内容表示

そのままだとオブジェクトの表示

Javaの配列をそのまま表示しようとすると、参照値が表示される。

Arrays.toString()

配列の内容を文字列として表現するには、java.util.ArraystoString()メソッドを使う。

多次元配列の場合

多次元配列の親配列をtoString()で表示しても下位次元までは適切に表示してくれない。

ループによる表示

ループを回して表示させることは考えられる。

Arrays.deepToString()

Arrays.deepToString()メソッドを使うと、多次元配列の内容を手軽に表示できる。

 

Java – 配列 – 長さ(サイズ)

配列の長さ(サイズ)はlengthプロパティーで得られる。Stringの場合のlength()メソッドと異なるので注意。

多次元配列の場合、親の配列の長さは子配列の数になる。

 

Java – 配列 – 初期化

イニシャライザーによる初期化

全要素が同じ型の配列をイニシャライザーで初期化する書き方。typeはプリミティブ型でも参照型でもよい。

type[] arrayName = {emelent1, emenent2, ...};

newによる初期化

newによる配列インスタンスの生成の書き方。typeはプリミティブ型でも参照型でもよい。

type[] arrayName = new type[length];

以下の例では、配列を生成した後に一つずつ要素を定義している。

newによる初期化直後の値はプリミティブ型ならそれぞれのデフォルト値、参照型ならnullになる。

newによる初期化とイニシャライザー

配列宣言時ではなく、宣言済みの配列変数をイニシャライザーで初期化する書き方。

単にイニシャライザーで代入しようとするとエラーになるので、newによる配列の生成とイニシャライザーによる定義を同時に行う。

1つ目のint[]に関してはイニシャライザーによる初期化と同じだが、2つ目のString[]のように一度宣言された変数に対して新たな配列で初期化する場合に有効。