20191230のJavaに関する記事は11件です。

javaにおける文字列の扱い方まとめ

javaにおける文字列の扱い方をまとめる。

まず文字列の型について記述し、String型の基本操作をまとめ、最後にStringBuilderを用いた文字列操作についてまとめる。

文字列の型

String型の特徴

String型は、イミュータブル(不変)である。
つまり、操作によって文字列を連結したり、切り出したりすると、その文字列自体が変化するのではなく、記憶領域に新たな文字列が作成される。
これにより、以下のような特徴がある。

  • 破壊変更が起きないため、プログラムの保守性が向上する
  • プログラムの見通しが立ちやすくなる
  • メモリ効率が悪い
  • メソッドで出来る文字列操作が限られる

StringBufferとStringBuilder

この効率の悪さや操作のしづらさを解決するのがミュータブル(可変)なStringBuilderクラス・StringBufferクラスである。

StringBuilderとStringBufferは使い方が同じであり、違いはStringBufferはスレッドセーフで、StringBuilderはスレッドセーフでないという点のみである。
スレッドセーフとは、あるコードを複数のスレッドが同時並行的に処理した場合に問題が発生しないことを意味する言葉である。

したがって、マルチスレッド処理を行わない場合は、同期処理を行わない分処理が早いStringBuilderを使用した方が良いと言える。
使い方については後述する。

String型の基本操作

以下のString型文字列Sについて基本操作を行う。

String str = "a,b,c";

長さの取得

文字列の長さを取得する。
戻り値はint型。

str.length()

N文字目の取得

戻り値はchar型。

str.charAt(int index)

文字列を結合する

+で文字列の結合ができる。

str += "String型文字列"

文字列を分割する

文字列を指定した区切り文字で分割する。
区切り文字を""とすれば1文字ずつ分割する。
戻り値はString型配列。

str.split("区切り文字")

文字列を切り出す

endIndexは省略可能。
指定する場合は、endIndexは含まれないことに注意する。
戻り値はString型。

str.substring(int beginIndex, int endIndex)

文字列比較

String型は同じ文字列でも参照値が異なる場合があるので、pythonのように==で比較することができない。
戻り値はboolean。

str.equals("String型文字列")

IgnoreCaseをつけると大文字・小文字を無視して比較可能。

str.equalsIgnoreCase("String型文字列")

辞書順比較

比較する文字列がstrと比べてどれだけ前にあるかを数値で返す。
つまり、比較する文字列がstrより後なら負の値を、前なら正の値を、一致するなら0を返す。
戻り値はint型。

str.compareTo("String型文字列")
"a".compareTo("b") //-1
"b".compareTo("a") //1
"a".compareTo("a") //0

こちらも、IgnoreCaseをつけると大文字・小文字を無視して比較可能。

str.compareToIgnoreCase("String型文字列")

検索

真偽判定

指定文字列が含まれるか。
戻り値はboolean。

str.contains("String型文字列")

正規表現を使う場合は、matchesメソッドを使う。

str.matches("正規表現")

前方一致。
戻り値はboolean。

str.startsWith("String型文字列")

後方一致。
戻り値はboolean。

str.endsWith("String型文字列")
indexの取得

指定された部分文字列が最初に出現する位置のindexを返す。
引数を追加すると、それ以降で最初に出現する位置のindexを返す。
戻り値はint型。

str.indexOf("String型文字列")
str.indexOf("String型文字列", int fromIndex)

同様に、最後に出現する位置のindexを返すLastindexOfというメソッドもある。

str.lastIndexOf("String型文字列")
str.lastIndexOf("String型文字列", int fromIndex)

文字列の置換

指定文字列が含まれる場合、それらを全て置き換える。
戻り値はString型。

str.replace("指定文字列", "置換文字列")

また、replaceAllを使えば、正規表現が利用できる。

str.replaceAll("指定文字列(正規表現可)", "置換文字列")

初めの指定文字列だけを置換したい場合は、replaceFirstを用いる。
replaceFirstも、replaceAllと同様に正規表現が使える。

str.replaceFirst("指定文字列(正規表現可)", "置換文字列")

大文字・小文字の変換

戻り値はString型。

str.toUpperCase() //大文字に
str.toLowerCase() //小文字に

文字列への変換

String.valueOf()で数値をはじめとする様々な型を文字列に変換することができる。
戻り値はString型。

String.valueOf(int num)

char型配列を引数とするとその配列をつなぎ合わせた文字列となる。

char[] c = {'a', 'b', 'c'};
String.valueOf(c) //"abc"が返される

数字の桁数を取得したい時などに便利である。

int N = 100000;
String.valueOf(N).length() //6桁とわかる

StringBuilderの使い方まとめ

StringからStringBuilderへの変換

下記のように構築する。

StringBuilder sb = new StringBuilder(str)

StringBuilderからStringへの変換

toStringでString型に変換できる。

sb.toString()

文字列の結合

appendがString型の+に相当する。
引数はString型でなく、int型やchar型などでも良い。

sb.append("結合したいもの")

任意の場所に挿入したいときは、insertを用いる。

sb.insert(int index, "結合したいもの")

部分文字列の削除

indexを指定し、部分文字列を消去できる。
他のメソッド同様、endIndexは含まれない。

sb.delete(int beginIndex, int endIndex)

任意のindexの文字を変える

setCharAtで、任意のindexの文字を別の文字に置き換えることができる。

sb.setCharAt(int index, char c)
//sb.setCharAt(1, 'z')とすれば、azcとなる

文字列の反転

reverseで反転した文字列が得られる。

sb.reverse()

文字列の置換

indexを指定した部分文字列を、任意の文字列に変換する。

sb.replace(int start, int end, "置換文字列")

String型と同じ操作

長さの取得
sb.length()
N文字目の取得
sb.charAt(int index)
indexの取得
sb.indexOf("String型文字列")
sb.indexOf("String型文字列", int fromIndex)
sb.lastIndexOf("String型文字列")
sb.lastIndexOf("String型文字列", int fromIndex)

まとめ

String型とStringBuilderクラスの操作をまとめた。
普段はString型を使えば良いが、操作を何度も行い実行時間がかかる場合や、反転などの操作を行う際は、StringBuilderクラスを使うと良い。

また、競技プログラミングにおいて、何度も文字列を出力すると時間がかかるので、その場合はStringBuilderにappendしていき、最後に1回でまとめて出力すると良い。
改行が必要な場合は、"\n"をappendすれば良い。

参考

Oracle Help Center
Stringクラスの説明
StringBuilderクラスの説明

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javaで文字列の比較

javaで文字列の比較は.equalsを使おう。

//正解
if (moziretu.equals("qiita")) {}
//間違い
if (moziretu == "qiita") {}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

正規表現 \p{...} メモ

複数のプログラミング言語で正規表現を使いまわそうとしたら \p{...} (Unicodeプロパティエスケープ)の互換性がよくわからなかったのでメモ。

概要

下表のように、ECMAScript, .NET (C#), Javaでは \p{カテゴリ名} 形式のGeneral Cateogry指定以外の記法は三者に互換性がない。

Unicodeプロパティ ECMAScript .NET Java
General Category \p{Lu}
\p{General_Category=Lu}
\p{gc=Lu}
\p{Lu} \p{Lu}
\p{IsLu}
\p{General_Category=Lu}
Block - \p{IsMongolian} \p{InMongolian}
\p{Block=Mongolian}
Script \p{Script=Common}
\p{sc=Common}
- \p{IsCommon}
\p{Script=Common}
Script Extensions \p{Script_Extensions=Adlam}
\p{scx=Adlam}
- -
Binary Property \p{Uppercase} - \p{IsUppercase}

言語比較

ECMAScript

ECMAScript 2018以降の、Unicodeフラグ付きの正規表現で使用できる。

\p{プロパティ名=プロパティ値} の形で指定する。

プロパティ名が General_Category の場合は省略して \p{プロパティ値} と書ける(例: \p{Lu})。

Yes/No の二値を取るプロパティ(バイナリプロパティ)は \p{プロパティ名} と書く(例: \p{Uppercase}\p{Uppercase=Yes} のようには書けない)。

非バイナリプロパティとしては ECMAScript® 2019 Language Specification #Table 54: Non-binary Unicode property aliases and their canonical property names に列挙されているGeneral_Category, Script, Script_Extensions と、その別名であるgc, sc, scx が使用できる(Block は含まれないため、ブロック指定はできない。Blockはnot usefulということでproposal段階で除外された —— Which properties should we include in Unicode escapes? · Issue #18 · tc39/proposal-regexp-unicode-property-escapes)。

バイナリプロパティとしては ECMAScript® 2019 Language Specification #Table 55: Binary Unicode property aliases and their canonical property names に列挙されているプロパティが使用できる。

.NET

General CategoryとBlock指定のみ使用できる。

General Categoryは \p{カテゴリ名} と記述する。カテゴリ名には .NET 正規表現での文字クラス | Microsoft Docs #サポートされている Unicode 一般カテゴリに列挙されている名前が使用できる。 .NET FrameworkのバージョンとUnicode標準のバージョンの対応関係は CharUnicodeInfo クラス (System.Globalization) | Microsoft Docs を参照。

Blockは \p{Isブロック名} と記述する。ブロック名には .NET 正規表現での文字クラス | Microsoft Docs #サポートされている名前付きブロックに列挙されている名前が使用できる。ここで指定できる名前はUnicode 4.0 および Perl 5.6に基づいている。

Java

General Category, Block, Script, バイナリプロパティ指定が使用できる。

General Categoryは \p{カテゴリ名}, \p{Isカテゴリ名}, \p{General_Category=カテゴリ名} と記述する。指定できるカテゴリ名の集合は Characterクラスで指定されたUnicodeバージョンで定義されているカテゴリ名の集合に等しい。

Blockは \p{Inブロック名}, \p{Block=ブロック名} と記述する。指定可能なブロック名は Character.UnicodeBlock#forName(String) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

Scriptは \p{Isスクリプト名}, \p{Script=スクリプト名} と記述する。指定可能なスクリプト名は Character.UnicodeScript#forName(String) の受け付ける名前とされている。UnicodeバージョンはCharacterクラスの指定に準じる。

バイナリプロパティは \p{Isプロパティ名} と記述する。指定可能なバイナリプロパティは Patternクラスのドキュメントに列挙されている。

参考: Ruby(鬼雲)

Ruby(鬼雲)で使用可能なUnicodeプロパティは https://github.com/k-takata/Onigmo/blob/master/doc/UnicodeProps.txt に列挙されている。

ブロック名は In_ブロック名 の形式、 Ageは Age=バージョン の形式、それ以外は プロパティ値 の形式になっているようだ。

参考: PCRE

PCREの \p で利用可能なプロパティは pcre2pattern(3) で説明されている。

\p{プロパティ値} の形式で、 General Categoryの値、Scriptの値、 Any と、PCRE拡張として Xan, Xps, Xsp, Xwd が指定できる。

UTS #18: Unicode Regular Expressions

UTS #18: Unicode Regular Expressions —— 1.2 Properties では、正規表現でUnicodeプロパティに対応する際のガイドラインを示している。

構文は以下の通りで、ECMAScriptの \p のスーパーセットになっている。Javaもブロックの \p{Inブロック名} 以外はこの形式だ。

ITEM := POSITIVE_SPEC | NEGATIVE_SPEC

POSITIVE_SPEC := ("\p{" PROP_SPEC "}") | ("[:" PROP_SPEC ":]")

NEGATIVE_SPEC := ("\P{" PROP_SPEC "}") | ("[:^" PROP_SPEC ":]")

PROP_SPEC  := <binary_unicode_property>

PROP_SPEC  := <unicode_property> (":" | "=" | "≠" | "!=" ) VALUE

PROP_SPEC  := <script_or_category_property_value> ("|" <script_or_category_property_value>)*

PROP_VALUE := <unicode_property_value> ("|" <unicode_property_value>)*

\p, \P 以外にPOSIX文字集合形式の [:spec:], [:^spec:] が書けたり、 = の別名として : があったり、 = の代わりに != と書くことで否定が表現できる。また、バイナリプロパティやGeneral Category以外にもScriptプロパティ値も \p{スクリプトプロパティ値} で書けることになっている。

ECMAScriptでは、同一機能の別表記を取り入れるのは実装を複雑にするだけであること、実用上はScriptよりScript_Extensionsの方が有用なため後者を推奨するといった立場からこれらの機能は取り入れなかったようだ。

ECMAScriptはUAX44-LM3のマッチング規則(シンボル値に関して (1) 大文字小文字を区別しない、 (2) -, _, 空白は無視する、 (3) 先頭の is は無視する)も採用していない。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インスタンス変数、クラス変数の違い

1.はじめに

インスタンス変数とクラス変数の違いが曖昧だったので今後のためにまとめてみました。
参考になれば嬉しいです。

2.インスタンス変数とは

それぞれのインスタンスに属する変数となります。具体的には、newキーワードによってインスタンス変数領域(インスタンス)が確保され、初期値代入やコンストラクタ(インスタンス生成時に実行される処理)によって、インスタンス変数が初期化されます。なお、作成されたインスタンスはそれぞれ別々のものとして認識されるので、インスタンス変数の値はインスタンス毎に異なります。そのため、オブジェクト毎に異なる情報を扱いたい場合に使用します。

3.クラス変数とは

staticキーワードを付けて宣言される変数であり、すべてのインスタンスで共有される共通の情報となります。あるオブジェクトからクラス変数の値を変更すると、その他のすべてのオブジェクトに影響が及びます。そのため、オブジェクト全体に共通する情報を扱いたい場合に使用します。

4.それぞれの特徴

  インスタンス変数 クラス変数
特徴 作成されたオブジェクトそれぞれに対してインスタンス変数領域が確保される。そのため、オブジェクト毎に値が異なる。 1つのクラスに対して1つだけ作成され、すべてのオブジェクトに対して共通の値が使用される
生存期間 オブジェクトが作成されてから消滅するまで プログラム開始から終了まで
呼び出し方 オブジェクトを代入した変数.インスタンス変数 1.クラス名.クラス変数
2.オブジェクトを代入した変数.インスタンス変数
宣言 アクセス修飾子 型 変数名 アクセス修飾子 staticキーワード 型 変数名
変更時の影響範囲 インスタンス変数に紐づくオブジェクトだけ すべてのオブジェクト

5.具体例

インスタンス変数

Main.java
public class Main {
  public static void main(String[] args) {
    Sample sampleA = new Sample();
    Sample sampleB = new Sample();

    sampleA.a = 10;
    sampleB.a = 20;
    System.out.println(sampleA.a);
    System.out.println(sampleB.a);
  }
}

public class Sample {

  public int a;
}
出力結果
10
20

それぞれのオブジェクトのインスタンス変数に異なる値を設定したので、
結果として、それぞれ異なる値が出力されます。

クラス変数

Main.java
public class Main {
  public static void main(String[] args) {
    Sample sampleA = new Sample();
    Sample sampleB = new Sample();

    sampleA.setNum(30);
    sampleB.setNum(40);
    System.out.println(sampleA.getNum());
    System.out.println(sampleB.getNum());
  }
}

public class Sample {

  public static int b;

  public void setNum(int value) {
    b = value;
  }

  public int getNum() {
    return b;
  }
}
出力結果
40
40

クラス変数ではすべてのインスタンスで共通の値を共有し、かつ
ひとつのオブジェクトに対する変更がその他すべてのオブジェクトに影響するので、結果として
最後に設定した40がクラス変数の値として出力されます。

参考記事

SOFTWARE ENGINEERING
tutorialspoint

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java で Scala っぽい Option 型のパターンマッチングと map、flatMap を実現してみる

Scala の Option 型を Java で実装してみたらどうなるかなという実験をしたのでメモっとく。列挙型と switch 文の進化形!?Java で代数的データ型とパターンマッチングを実現してみる の続き。

実験用の Gradle プロジェクトを生成する

Lombok を使いたいので JShell ではなく Gradle プロジェクトで実験する。下記のコマンドで Gradle プロジェクトを生成しよう。

$ mkdir option
$ cd option
$ gradle init \
    --type java-library \
    --dsl groovy \
    --test-framework junit \
    --project-name option \
    --package com.example

build.gradle ファイルを更新して Lombok を導入する。

--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@
 plugins {
     // Apply the java-library plugin to add support for Java Library
     id 'java-library'
+    id "io.freefair.lombok" version "4.1.6"
 }

 repositories {

パターンマッチングできる Option 型を実装する

Option 型を下記のように Java で実装してみる。見通しをよくするためシールドクラスパターン (Sealed Class Pattern) を使用していない。

Some クラスが保持する型 T とパターンマッチングの結果型 R があり、実装する中で結構混乱した。コードを読む人は注意してみてほしい。

src/main/java/com/example/Option.java
package com.example;

import lombok.Value;

public interface Option<T> {
    @Value
    class Some<T> implements Option<T> {
        T value;

        public <R> R match(CaseBlock<T, R> caseBlock) {
            return caseBlock._case(this);
        }
    }

    @Value
    class None<T> implements Option<T> {
        public <R> R match(CaseBlock<T, R> caseBlock) {
            return caseBlock._case(this);
        }
    }

    interface CaseBlock<T, R> {
        R _case(Some<T> some);
        R _case(None<T> none);
    }

    <R> R match(CaseBlock<T, R> caseBlock);
}

Option 型を使用するテストを書いてみる。パターンマッチングの動作確認以上の意味はないテストになる。

src/test/java/com/example/OptionTypeTest.java
package com.example;

import org.junit.Test;

import static org.junit.Assert.*;

public class OptionTest {
    @Test
    public void testSomeType() {
        Option<Integer> some = new Option.Some<>(1);

        var actual = some.match(new Option.CaseBlock<>() {
            @Override
            public Integer _case(Option.Some<Integer> some) {
                return some.getValue();
            }

            @Override
            public Integer _case(Option.None<Integer> none) {
                return 0;
            }
        });

        assertEquals(1, actual);
    }

    @Test
    public void testNoneType() {
        Option<Integer> none = new Option.None<>();

        var actual = none.match(new Option.CaseBlock<>() {
            @Override
            public Integer _case(Option.Some<Integer> some) {
                return some.getValue();
            }

            @Override
            public Integer _case(Option.None<Integer> none) {
                return 0;
            }
        });

        assertEquals(0, actual);
    }
}

map と flatMap できる Option 型を実装する

map メソッドと flatMap メソッドを下記のように実装する。

src/main/java/com/example/Option.java
package com.example;

import lombok.Value;

import java.util.function.Function;

public interface Option<T> {
    @Value
    class Some<T> implements Option<T> {
        T value;

        public <R> R match(CaseBlock<T, R> caseBlock) {
            return caseBlock._case(this);
        }
    }

    @Value
    class None<T> implements Option<T> {
        public <R> R match(CaseBlock<T, R> caseBlock) {
            return caseBlock._case(this);
        }
    }

    interface CaseBlock<T, R> {
        R _case(Some<T> some);
        R _case(None<T> none);
    }

    <R> R match(CaseBlock<T, R> caseBlock);

    default <R> Option<R> map(Function<T, R> f) {
        return this.match(new CaseBlock<>() {
            @Override
            public Option<R> _case(Some<T> some) {
                return new Some<>(f.apply(some.getValue()));
            }

            @Override
            public Option<R> _case(None<T> none) {
                return new None<>();
            }
        });
    }

    default <R> Option<R> flatMap(Function<T, Option<R>> f) {
        return this.match(new CaseBlock<>() {
            @Override
            public Option<R> _case(Some<T> some) {
                return f.apply(some.getValue());
            }

            @Override
            public Option<R> _case(None<T> none) {
                return new None<>();
            }
        });
    }
}

Essential Scala に map と flatMap の違いについて次のような説明がある。

We use map when we want to transform the value within the context to a new value, while keeping the context the same. We use flatMap when we want to transform the value and provide a new context.

map は、コンテキストに含まれる値を新しい値に変換したいときに使用し、その間も同じコンテキストを維持する。flatMap は、値を変換し、新しいコンテキストを与えたいときに使用する。

Option 型には Some と None という値のコンテキスト1がある。map を適用すると Some は Some のまま、None は None のままになる。flatMap を適用すると Some は Some か None になり、None は None のままになる。map は失敗しない関数を適用でき、flatMap は失敗するかもしれない関数(結果型が Option 型になる関数)を適用できると考えるといいと思う。

Option 型の map メソッドと flatMap メソッドを使用するテストを書いてみよう。

ここで、Option<Integer> を返す、失敗するかもしれないメソッド、mightFail1 メソッドと mightFail2 メソッド、mightFail3 メソッドがある。この3つのメソッドを用い、Scala の for 内包表記を意識した書き方で、map メソッドと flatMap メソッドをテストしてみる。

src/test/java/com/example/OptionMapAndFlatMapTest.java
package com.example;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class OptionMapAndFlatMapTest {
    @Test
    public void testSomeResultOfMapAndFlatMap() {
        // for {
        //   a <- mightFail1
        //   b <- mightFail2
        // } yield a + b
        var actual = mightFail1().flatMap(a ->
                     mightFail2().map    (b ->
                     a + b));

        assertEquals(new Option.Some<>(3), actual);
    }

    @Test
    public void testNoneResultOfMap() {
        // for {
        //   a <- mightFail1
        //   b <- mightFail2
        //   c <- mightFail3
        // } yield a + b + c
        var actual = mightFail1().flatMap(a ->
                     mightFail2().flatMap(b ->
                     mightFail3().map    (c ->
                     a + b + c)));

        assertEquals(new Option.None<>(), actual);
    }

    @Test
    public void testNoneResultOfFlatMap() {
        // for {
        //   a <- mightFail3
        //   b <- mightFail2
        //   c <- mightFail1
        // } yield a + b + c
        var actual = mightFail3().flatMap(a ->
                     mightFail2().flatMap(b ->
                     mightFail1().map    (c ->
                     a + b + c)));

        assertEquals(new Option.None<>(), actual);
    }

    private Option<Integer> mightFail1() {
        return new Option.Some<>(1);
    }

    private Option<Integer> mightFail2() {
        return new Option.Some<>(2);
    }

    private Option<Integer> mightFail3() {
        return new Option.None<>();
    }
}

Option 型のコンテキスト2の中で3つのメソッドを利用した処理を記述している。ここで重要なのは、「失敗するかもしれない」というコンテキストを意識せずに処理を書けていることだ。すべてのメソッドが成功した場合、ないしは、いずれかのメソッドが失敗した場合という場合分けを Option 型のコンテキストに押し付け、実現したい処理だけを書けていることになる。いわゆるモナドってやつに Option 型がなったわけなんだよね。たぶん。


今回は Java で Scala っぽい Option 型のパターンマッチングと map、flatMap を実現してみた。あくまで勉強目的の実験という感じで、実用性はないと思うのだけど、頭を整理するために記事にした。実用的なコードだと変性や型境界についての記述も必要になると思うしね。まあ何かの参考になれば幸い。


  1. Essential Scala では、Option 型における Some や None という値をコンテキストとして示している。正確な表現ではないと思うが、本稿では「値のコンテキスト」と呼称する。 

  2. Option 型におけるモナドとしてのコンテキストを語るときは「失敗するかもしれない」というコンテキストを示している。正確な表現ではないと思うが、本稿では「型のコンテキスト」と呼称する。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Integerを敢えて「==」で比較すると・・・

Integerの値が等しいかどうか確認したい場合、どうしますか?
もちろん、「==」を使用するようなことはせずに「equals」メソッドを使用して比較しますよね。
なぜならば、Javaにおいては、「==」はその参照が指し示すインスタンスが同一なものかどうかを確認するものであって、インスタンスの値が等しいかどうかは「equals」メソッドを使用して確認すべきだからですね。

では、敢えて「==」を使用してIntegerインスタンスの値を比較するとどうなるでしょうか。
以下のコードをご覧ください。

Sample.java
public class Sample1 {

    public static void main(String[] args) {

        Integer i1 = 100;
        Integer i2 = 100;

        Integer i3 = 1000;
        Integer i4 = 1000;

        System.out.println("100=100: " + (i1 == i2));
        System.out.println("1000=1000: " + (i3 == i4));
    }
}

このプログラムでは「100」と「1000」について、「==」を用いてそれぞれ比較していますが、結果は以下の通りに表示されます。

100=100: true
1000=1000: false

比較した数字によって結果が異なってしまいました。なぜ、このような違いが出るのでしょうか。

コメント欄にある通り、これはIntegerCacheにIntegerインスタンスがキャッシュされているためであり、
コンスタントプールとは無関係のため訂正いたします。

これを説明するためには、まずJavaの領域の一つである「コンスタントプール」について説明する必要があります。

コンスタントプールとは

Integerのインスタンスは、その内部状態を変更することができません。不変です。
したがって、インスタンスの使いまわしができますし、都度都度インスタンスを生成することは無駄ですよね。
ですので、Java VM はこれらのインスタンスをあらかじめ生成しておいて、メモリ上のある領域に蓄積しておき、必要になったタイミングでそれを利用するようにします。
この領域が「コンスタントプール」と呼ばれるものです。

なお、同様にInteger以外にもこのコンスタントプールの対象となるものがあります。
たとえば、ソースコードにべた書きされている文字列もこのコンスタントプールへインスタンスを確保される対象です。

コンスタントプールへの格納対象となるインスタンス

前述したとおり、Integerインスタンスはコンスタントプールにあらかじめ用意されており、プログラムはそのインスタンスを利用します。
ではなぜ、上記のコードで「100」と「1000」に比較結果の差が生まれてしまうのでしょうか。
それは、すべてのIntegerインスタンスがコンスタントプール上に用意されているわけではないからです。
デフォルトでは-128~127の範囲のIntegerインスタンスが対象であり、その範囲外のインスタンスについては、コンスタントプールに用意されていません。

つまり、上記のコードでは、「100」についてはコンスタントプールから取得したインスタンスが利用されるため、それぞれの参照がさしているインスタンスが同一でした。
しかし、「1000」については、コンスタントプールにインスタンスが用意されていなかったため、都度インスタンスを生成し、それぞれの参照が指すインスタンスが異なっており、結果として「==」で比較した結果がfalseになっていたということです。

まとめ

Javaの小ネタとして新人研修で話してみてウケがよかったので書いてみました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Webシステム構築(超基礎)④:Webシステムの構築

目的

Webシステム構築(超基礎)②:APサーバ構築と基本動作Webシステム構築(超基礎)③:DBサーバ構築と基本動作で構築したWeb/APサーバとDBサーバを用い、Webシステムを構築する。

環境条件

  • APサーバ
    • EC2:t2.micro
    • OS:Red Hat Enterprise Linux 8 (HVM), SSD Volume Type
    • Disk:汎用SSD(GP2) 10GB
    • Tomcat:Apache Tomcat 9
    • Java:JDK 1.8
  • DBサーバ
    • EC2:t2.micro
    • OS:Red Hat Enterprise Linux 8 (HVM), SSD Volume Type
    • Disk:汎用SSD(GP2) 10GB
    • MySQL:MySQL 8

セキュリティグループの設定等はいい感じに。

構築手順

以下の流れで実施する。
1. Web/APサーバOSからDBサーバのMySQLに接続できる状態にする
2. Web/APサーバからDBサーバのMySQLに接続できる状態にする
3. データベースへのデータ登録
4. アプリケーションの準備
5. 動作確認

1. Web/APサーバOSからDBサーバのMySQLに接続できる状態にする

DBサーバにec2-userでログイン

rootユーザにスイッチ
$ sudo su - 

MySQLにログイン
#mysql -uroot -ppassword

接続許可ユーザの確認
> select user,host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+

接続用ユーザの作成
> CREATE USER 'appuser'@'%' IDENTIFIED BY 'appuser';

appuserの権限を変更
> GRANT ALL ON *.* TO 'appuser'@'%';

appuserが全ての場所から接続できる状態になっていることを確認する
> select user,host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| appuser          | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+

2. Web/APサーバからDBサーバのMySQLに接続できる状態にする

ドライバーのダウンロード
# curl -O https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.12/mysql-connector-java-8.0.12.jar

Tomcatのlibディレクトリにドライバーを配置
# mv mysql-connector-java-8.0.12.jar /opt/apache-tomcat-9.0.30/lib/

ドライバーにシンボリックリンクを貼る
# cd /opt/apache-tomcat-9.0.30/lib/
# ln -s mysql-connector-java-8.0.12.jar mysql-connector-java.jar

3. データベースへのデータ登録

サンプルデータをデータベースに登録するためのSQL情報を取得
# curl -O https://downloads.mysql.com/docs/world.sql.zip

unzipのインストール
# yum install -y unzip

SQLファイルの解凍
# unzip world.sql.zip 

DBにサンプルデータを投入する
# mysql -uappuser -pappuser  < world.sql

MySQLにログイン
# mysql -uappuser -pappuser

worldデータベースが追加されていることを確認する
> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| world              |
+--------------------+

worldデータベースに含まれる表を確認する
> use world;
> show tables;
+-----------------+
| Tables_in_world |
+-----------------+
| city            |
| country         |
| countrylanguage |
+-----------------+

各表定義は以下の様になっており、


> desc city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+

> desc country;
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Field          | Type                                                                                  | Null | Key | Default | Extra |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+
| Code           | char(3)                                                                               | NO   | PRI |         |       |
| Name           | char(52)                                                                              | NO   |     |         |       |
| Continent      | enum('Asia','Europe','North America','Africa','Oceania','Antarctica','South America') | NO   |     | Asia    |       |
| Region         | char(26)                                                                              | NO   |     |         |       |
| SurfaceArea    | decimal(10,2)                                                                         | NO   |     | 0.00    |       |
| IndepYear      | smallint(6)                                                                           | YES  |     | NULL    |       |
| Population     | int(11)                                                                               | NO   |     | 0       |       |
| LifeExpectancy | decimal(3,1)                                                                          | YES  |     | NULL    |       |
| GNP            | decimal(10,2)                                                                         | YES  |     | NULL    |       |
| GNPOld         | decimal(10,2)                                                                         | YES  |     | NULL    |       |
| LocalName      | char(45)                                                                              | NO   |     |         |       |
| GovernmentForm | char(45)                                                                              | NO   |     |         |       |
| HeadOfState    | char(60)                                                                              | YES  |     | NULL    |       |
| Capital        | int(11)                                                                               | YES  |     | NULL    |       |
| Code2          | char(2)                                                                               | NO   |     |         |       |
+----------------+---------------------------------------------------------------------------------------+------+-----+---------+-------+

> desc countrylanguage;
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| CountryCode | char(3)       | NO   | PRI |         |       |
| Language    | char(30)      | NO   | PRI |         |       |
| IsOfficial  | enum('T','F') | NO   |     | F       |       |
| Percentage  | decimal(4,1)  | NO   |     | 0.0     |       |
+-------------+---------------+------+-----+---------+-------+

4. アプリケーションの準備

JSPとJavaプログラムを利用した簡単なWebアプリケーションを構築し、APサーバ上で動作させる。
Web画面上で国名を入力し検索すると、主要5都市とその人口、また公用語とその利用割合を、
DB検索を実施しながら表示するプログラムである。

world.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" %>

<!DOCTYPE html>
<html>
    <head>
        <title>requestForm</title>
    </head>
    <body>
        <p>各国の主要都市と公用語の検索</p>

        <%-- GETメソッドでテキストを送信 --%>
        <form action="./WorldServlet">
            <p>
                国名(英語表記)を入力してください:<input type="text" name="text1"><br>
                例)日本の場合はJapanと入力
            </p>
            <input type="submit" value="検索実行">
        </form>
</html>
WorldServlet.java
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.NumberFormat;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/WorldServlet")
public class WorldServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * コンストラクタ.
     */
    public WorldServlet() {
        super();
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String inputText = "";  // テキスト1格納用変数

        inputText = request.getParameter("text1");

        String servername     = "172.31.45.98";
        String databasename   = "world";
        String user = "appuser";
        String password = "appuser";
        String serverencoding = "UTF-8";
        String url =  "jdbc:mysql://" + servername + "/" + databasename + "?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B9:00&rewriteBatchedStatements=true";

        Connection con = null;

        try {
            Class.forName( "com.mysql.cj.jdbc.Driver" ).newInstance();
            con = DriverManager.getConnection( url, user, password );
            Statement stat = con.createStatement();
            String sqlStr1 = "select country.name, city.name, city.population from city inner join country on city.countrycode=country.code  where country.name='" + inputText + "' order by city.population desc limit 5;";
            String sqlStr2 = "select country.name, countrylanguage.language, countrylanguage.percentage from country inner join countrylanguage on country.code=countrylanguage.countrycode where country.name='" + inputText + "' order by countrylanguage.percentage desc limit 1;";
            System.out.println("sqlStr1: " + sqlStr1);
            System.out.println("sqlStr2: " + sqlStr2);

            ResultSet resultset1 = stat.executeQuery( sqlStr1 );

            String country_name = null;
            String city_name = null;
            Integer city_population = 0;
            String countrylanguage_language = null;
            Double countrylanguage_percentage = 0.0;

            //NumberFormatインスタンスを生成
            NumberFormat nfNum = NumberFormat.getNumberInstance();    //カンマ区切り形式
            NumberFormat nfPer = NumberFormat.getPercentInstance();   //パーセント形式

            // 画面に出力する内容の設定
            // 出力する内容がHTMLであることを設定
            response.setContentType("text/html");
            // 出力する画面の文字コードをUTF-8に設定
            response.setCharacterEncoding("UTF-8");

            // 画面に出力するためのWriterクラスインスタンスを取得
            PrintWriter pw = response.getWriter();

            // HTMLを出力
            pw.println("<html>");
            pw.println("<head>");
            pw.println("<title>入力結果</title>");
            pw.println("</head>");
            pw.println("<body>");
            pw.println("<h1>検索結果</h1>");
            pw.println("<h2>" + inputText + "の主要都市とその人口は以下である</h2>");
            pw.println("<table border='1'>");
            pw.println("<tr>");
            pw.println("<th>都市名</th>");
            pw.println("<th>人口</th>");
            pw.println("</tr>");
            while( resultset1.next() )
            {
                /*getString()メソッドは、引数に指定されたフィールド名(列)の値をStringとして取得する*/
                city_name = resultset1.getString("city.name");
                city_population = resultset1.getInt("city.population");
                System.out.print("都市名:" + city_name);
                System.out.print("都市の人口:" + city_population);
                pw.println("<tr>");
                pw.println("<th>"+ city_name +"</th>");
                pw.println("<th>"+ nfNum.format(city_population) +"人</th>");
                pw.println("</tr>");

            }
            pw.println("</table>");
            resultset1.close();

            ResultSet resultset2 = stat.executeQuery( sqlStr2 );

            pw.println("<h2>" + inputText + "の公用語とその利用割合は以下である</h2>");
            pw.println("<table border='1'>");
            pw.println("<tr>");
            pw.println("<th>公用語</th>");
            pw.println("<th>利用割合</th>");
            pw.println("</tr>");
            while( resultset2.next() )
            {
                /*getString()メソッドは、引数に指定されたフィールド名(列)の値をStringとして取得する*/
                countrylanguage_language = resultset2.getString("countrylanguage.language");
                countrylanguage_percentage = resultset2.getDouble("countrylanguage.percentage");
                System.out.print("公用語:" + countrylanguage_language);
                System.out.print("利用割合:" + countrylanguage_percentage);
                pw.println("<tr>");
                pw.println("<th>"+ countrylanguage_language +"</th>");
                pw.println("<th>"+ nfPer.format(countrylanguage_percentage / 100) +"</th>");
                pw.println("</tr>");

            }
            pw.println("</table>");

            resultset2.close();
            stat.close();
            con.close();

            pw.println("</body>");
            pw.println("</html>");       
        } 
        catch( SQLException e ){

            /*エラーメッセージ出力*/
            System.out.println( "Connection Failed. : " + e.toString() );

            /*例外を投げちゃうぞ*/
            try {
                throw new Exception();
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }

        }catch (ClassNotFoundException e){

            /*エラーメッセージ出力*/
            System.out.println("ドライバを読み込めませんでした " + e);
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            try{
                if( con != null ){ 
                    con.close();
                }
            }
            catch(Exception e){

                /*エラーメッセージ出力*/
                System.out.println( "Exception2! :" + e.toString() );

                /*例外を投げちゃうぞ*/
                try {
                    throw new Exception();
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }

    }
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">

    <servlet>
        <servlet-name>WorldServlet</servlet-name>
            <servlet-class>WorldServlet.WorldServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>WorldServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

以下の様にアプリケーションを配置する。

/opt/apache-tomcat-9.0.30/webapps/world

world
├── WEB-INF
│   ├── lib
│   │   └── World.jar
│   └── web.xml
└── world.jsp

5. 動作確認

Tomcatサービスを再起動する
# service tomcat restart
Redirecting to /bin/systemctl start tomcat.service

ブラウザからWebサーバの「パブリックDNS:8080/world/world.jsp」に接続し、
作成したWebページの表示及び、処理の動作が正しく行われることを確認する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaはExcelをPDFに変えます

本論文では、JavaプログラムでExcelブックをPDF文書に変更する方法を紹介します, 含む

*Workbook全体をPDFに変更します。
*指定されたシートをPDFに変更します。

使用ツール:Free Spire.XLS for Java (無料版)
https://www.e-iceblue.com/Introduce/free-xls-for-java.html

Jarファイルの取得と導入:
Method 1:ホームページを通じてjarファイルのカバンをダウンロードします。ダウンロード後、ファイルを解凍して、libフォルダの下のSpire.xls.jarファイルをJavaプログラムに導入します。
https://www.e-iceblue.com/Download/xls-for-java-free.html

Method 2:maven倉庫設置による導入:
https://www.e-iceblue.com/Tutorials/Licensing/How-to-install-Spire.PDF-for-Java-from-Maven-Repository.html

Excelテストドキュメントは次の通りです。二つのシートが含まれています:
Sample.png

JAVA Samples

Demo 1. Workbook全体をPDFに変更します

import com.spire.xls.*;

public class ExcelToPDF {
    public static void main(String[] args) {
        // Excelドキュメントを読み込む
        Workbook wb = new Workbook();
        wb.loadFromFile("test.xlsx");

        //呼び出し方法をPDF形式に保存します
        wb.saveToFile("ToPDF.pdf",FileFormat.PDF);
    }
}

効果図:

Excel to PDF 1.png

Demo 2. 指定されたシートをPDFに変更します

import com.spire.xls.*;

public class ExcelToPDF {
    public static void main(String[] args) {
        // Excelドキュメントを読み込む
        Workbook wb = new Workbook();
        wb.loadFromFile("test.xlsx");

        // 2番目のシートを取得
        Worksheet sheet = wb.getWorksheets().get(1);

        //呼び出し方法をPDF形式に保存します
        sheet.saveToPdf("ToPDF2.pdf");
    }
}

効果図:
Excel to PDF 2.png

The END

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WEBサーバを作りながら学ぶ 基礎からのWEBアプリケーション開発入門

https://teratail.com/questions/65967
まったく同じ嵌まり方をした。

HTTPのリクエストヘッダーは改行コードのみの空行で終了します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

言語別int64素因数分解(試し割り法)処理時間比較

C言語、C#, Java8, Golang, Python3 で、
int64の範囲内の値を素因数分解したときの処理時間の比較してみました。

先に結果を見たい方はこちらへ。
処理時間一覧

C#, Java, Golangについては、bigintでも計算してみました。

利用した素因数分解のアルゴリズム

素因数分解のアルゴリズムについては、最も単純な試し割り法を利用しました。
2,3,5,7,13,17, ... (以下、+2, +4を交互に繰り返した値)で割れるかどうか試していきます。

処理の高速化のために、あらかじめ割れるかどうか照合する素数の表を用意しておく方法もありますが、
(63ビット程度のループ処理も10秒程度で終わったため、)今回は用意していません。

下記は、Javaでの試し割り処理の例です。

PrimeFactorization.java
private long[] trial_division(long n) {
    List<Long> prime_list = new ArrayList<>();
    long max = (long)(Math.sqrt(n)) + 1;

    // 2 で割っていく
    while (n % 2 == 0) {
        prime_list.add((long)2);
        n /= 2;
    }

    // 3 で割っていく
    while (n % 3 == 0) {
        prime_list.add((long)3);
        n /= 3;
    }

    // 5 ~ Math.sqrt(n) の数字で割っていく
    boolean flag = true;
    for (long i = 5; i < max; ) {
        while (n % i == 0) {
            prime_list.add(i);
            n /= i;
            if (n == 1)
                i = max;
        }
        if (flag)
            i += 2;
        else
            i += 4;

        flag = !flag;
    }

    if (n != 1) {
        prime_list.add(n);
    }
// (以下、省略)
}

使用言語

  • C言語 (gcc version 8.2.0(MinGW.org GCC-8.2.0-3))
  • C# (.NET Core 3.0)
  • Java8 (javac 11.0.4)
  • Golang (go version go1.12.7 windows/amd64)
  • Python3 (Python 3.7.4)

JavaとGolangについては、int64(long)型のほかに、
bigint(BigInteger)型でも同様の計算を行ってみました。

ちなみに、Python3については、整数型の上限がありませんね。

プログラムのソースは、下記に配置しています。
https://github.com/NobuyukiInoue/PrimeFactorization

計測に使用したPC

処理時間の計測は、以下のスペックもPCを使用しました。

CPU:   Intel Core-i7 8559U 2.7GHz(4.5GHz) 4コア/8スレッド
Memory: 16GB(LPDDR3 2,133MHz)
OS:    Windows 10 Professional

いわゆる、Macbook Pro 13インチ 2018モデルですね。

処理時間一覧

対象の値は、素因数分解したい値です。
("111..."が並んでいますが、2進数ではなく10進数です。)

試し割り法の場合、2,3,5,..などの小さな素数で割れた場合は、トータルのループ回数が少なくなるので、
値の大きさに比例して処理時間がかかるわけではありません。

int64の場合、64ビット程度の値でも、いまどきのPCであれば10秒以内で素因数分解ができてしまいます。

対象の値
(10進数)
C言語
long long int
C#
long
C#
BigInteger
Java
long
Java
BigInteger
Golang
int64
Golang
BigInt
Python3
int
11111111111111
(44ビット)
0.015[S] 0.004[S] 0.013[S] 0.000[S] 0.047[S] 0.003[S] 0.056[S] 0.030[S]
111111111111111
47ビット)
0.031[S] 0.008[S] 0.038[S] 0.016[S] 0.109[S] 0.007[S] 0.169[S] 0.088[S]
1111111111111111
(50ビット)
0.062[S] 0.015[S] 0.067[S] 0.031[S] 0.141[S] 0.015[S] 0.343[S] 0.176[S]
11111111111111111
(54ビット)
0.218[S] 0.257[S] 1.540[S] 0.250[S] 1.859[S] 0.271[S] 計測断念
(180秒以上)
5.021[S]
111111111111111111
(57ビット)
0.624[S] 0.002[S] 0.007[S] 0.000[S] 0.031[S] 0.001[S] 0.022[S] 0.016[S]
1111111111111111111
(60ビット)
2.156[S] 2.300[S] 15.025[S] 2.422[S] 15.688[S] 2.519[S] 計測断念
(180[S]以上)
48.322[S]
6111111111111111111
(63ビット)
4.938[S] 5.396[S] 41.263[S] 5.703[S] 38.781[S] 5.937[S] 計測断念
(180[S]以上
243.715[S]

(2019/12/30(Mon) 18:00更新)

感想

Golangのbigintは、ループ数が多いと、(ループ内の代入処理時にインスタンス生成が行っているため)、
とんでもなく時間がかかっています。

いくつかの言語で、57ビットの処理時間が44ビットよりも短くなっていますが、これは、

11111111111111(44ビット)の素因数[11, 239, 4649, 909091] の合計が913990に対し、
111111111111111111(57ビット)の素因数[3, 3, 7, 11, 13, 19, 37, 52579, 333667]の合計が386339であり、
ループ回数が少なく済んでいるからです。

bigintでの処理速度の比較では、C# > Java > Golang の順番といったところでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

java フレームワーク

今日は疎かになっていた
javaフレームワークについて投稿します。

選び方

前提としてどう選んでいくかというと

  • 何を作るか
  • 開発スケジュール
  • 技術者の人数および技術力
  • 保守性

などが挙げられますが
以下2点は抑えておきたいものです。

必要な機能を備えているか

例えば
Spring Frameworkは汎用性が高く、機能が充実していますが
Play Frameworkは軽量で機能が絞り込まれているという違いがあります。
したがって、大規模開発のために機能重視でフレームワークを選ぶ場合は
Spring Frameworkを採用することも1つの考え方です。

実績あるフレームワークであるか

次々と新しいフレームワークが生まれていますが
マイナーなフレームワークを選んでしまうと、対応できるエンジニアが少なく
学習コストや開発コスト、メンテナンスコストが余計にかかります。
そのため実績あるフレームワークを選ぶことをおすすめします。
有名なフレームワークであれば、開発コミュニティや日本語ドキュメントが充実してお
情報を入手しやすいというメリットもあります。

フレームワーク一覧

  • Spring Framework
  • Java EE
  • Apache Struts
  • Play Framework
  • Spark Framework

他にもありますが、有名どころのこれらを紹介して行きます。

Spring Framework

オープンソースフレームワークで、単にSpringと呼ばれることもあります。
データベースへのアクセスやトランザクション処理などをサポートするフレームワークをはじめ
多数のフレームワークの集合体です。

Java EE

Java標準仕様のフレームワーク。
かつてはJ2EE(Java 2 Platform, Enterprise Edition)と呼ばれていました。
大規模システムに採用されています

Apache Struts

Apache Software Foundation が提供する、2001年ごろから使われているフレームワークです。
Struts 1は既にサポート終了、Struts2が別バージョンでリリースされています。

Play Framework

Javaと、Javaとの連携が容易なプログラミング言語Scalaで書かれたオープンソースのフレームワーク。
Rubyのフレームワーク「Ruby on Rails」、pythonのフレームワーク「Django」の思想に大きく影響を受けています。

Spark Framework

シンプルな構成・軽量のマイクロフレームワーク。Rubyのフレームワーク「Sinatora」に影響を受けています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む