- 投稿日:2019-12-30T23:08:33+09:00
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クラスの説明
- 投稿日:2019-12-30T22:45:24+09:00
javaで文字列の比較
- 投稿日:2019-12-30T22:16:20+09:00
正規表現 \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
は無視する)も採用していない。
- 投稿日:2019-12-30T19:05:48+09:00
インスタンス変数、クラス変数の違い
1.はじめに
インスタンス変数とクラス変数の違いが曖昧だったので今後のためにまとめてみました。
参考になれば嬉しいです。2.インスタンス変数とは
それぞれのインスタンスに属する変数となります。具体的には、newキーワードによってインスタンス変数領域(インスタンス)が確保され、初期値代入やコンストラクタ(インスタンス生成時に実行される処理)によって、インスタンス変数が初期化されます。なお、作成されたインスタンスはそれぞれ別々のものとして認識されるので、インスタンス変数の値はインスタンス毎に異なります。そのため、オブジェクト毎に異なる情報を扱いたい場合に使用します。
3.クラス変数とは
staticキーワードを付けて宣言される変数であり、すべてのインスタンスで共有される共通の情報となります。あるオブジェクトからクラス変数の値を変更すると、その他のすべてのオブジェクトに影響が及びます。そのため、オブジェクト全体に共通する情報を扱いたい場合に使用します。
4.それぞれの特徴
インスタンス変数 クラス変数 特徴 作成されたオブジェクトそれぞれに対してインスタンス変数領域が確保される。そのため、オブジェクト毎に値が異なる。 1つのクラスに対して1つだけ作成され、すべてのオブジェクトに対して共通の値が使用される 生存期間 オブジェクトが作成されてから消滅するまで プログラム開始から終了まで 呼び出し方 オブジェクトを代入した変数.インスタンス変数 1.クラス名.クラス変数
2.オブジェクトを代入した変数.インスタンス変数宣言 アクセス修飾子 型 変数名 アクセス修飾子 staticキーワード 型 変数名 変更時の影響範囲 インスタンス変数に紐づくオブジェクトだけ すべてのオブジェクト 5.具体例
インスタンス変数
Main.javapublic 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.javapublic 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
がクラス変数の値として出力されます。参考記事
- 投稿日:2019-12-30T17:10:15+09:00
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.javapackage 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.javapackage 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.javapackage 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.javapackage 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 を実現してみた。あくまで勉強目的の実験という感じで、実用性はないと思うのだけど、頭を整理するために記事にした。実用的なコードだと変性や型境界についての記述も必要になると思うしね。まあ何かの参考になれば幸い。
- 投稿日:2019-12-30T13:32:23+09:00
Integerを敢えて「==」で比較すると・・・
Integerの値が等しいかどうか確認したい場合、どうしますか?
もちろん、「==」を使用するようなことはせずに「equals」メソッドを使用して比較しますよね。
なぜならば、Javaにおいては、「==」はその参照が指し示すインスタンスが同一なものかどうかを確認するものであって、インスタンスの値が等しいかどうかは「equals」メソッドを使用して確認すべきだからですね。では、敢えて「==」を使用してIntegerインスタンスの値を比較するとどうなるでしょうか。
以下のコードをご覧ください。Sample.javapublic 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の小ネタとして新人研修で話してみてウケがよかったので書いてみました。
- 投稿日:2019-12-30T11:18:58+09:00
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.jar3. データベースへのデータ登録
サンプルデータをデータベースに登録するための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.javaimport 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.jsp5. 動作確認
Tomcatサービスを再起動する # service tomcat restart Redirecting to /bin/systemctl start tomcat.service ブラウザからWebサーバの「パブリックDNS:8080/world/world.jsp」に接続し、 作成したWebページの表示及び、処理の動作が正しく行われることを確認する。
- 投稿日:2019-12-30T10:44:24+09:00
JavaはExcelをPDFに変えます
本論文では、JavaプログラムでExcelブックをPDF文書に変更する方法を紹介します, 含む
*Workbook全体をPDFに変更します。
*指定されたシートをPDFに変更します。使用ツール:Free Spire.XLS for Java (無料版)
https://www.e-iceblue.com/Introduce/free-xls-for-java.htmlJarファイルの取得と導入:
Method 1:ホームページを通じてjarファイルのカバンをダウンロードします。ダウンロード後、ファイルを解凍して、libフォルダの下のSpire.xls.jarファイルをJavaプログラムに導入します。
https://www.e-iceblue.com/Download/xls-for-java-free.htmlMethod 2:maven倉庫設置による導入:
https://www.e-iceblue.com/Tutorials/Licensing/How-to-install-Spire.PDF-for-Java-from-Maven-Repository.htmlExcelテストドキュメントは次の通りです。二つのシートが含まれています:
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); } }効果図:
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"); } }The END
- 投稿日:2019-12-30T09:11:00+09:00
WEBサーバを作りながら学ぶ 基礎からのWEBアプリケーション開発入門
https://teratail.com/questions/65967
まったく同じ嵌まり方をした。HTTPのリクエストヘッダーは改行コードのみの空行で終了します。
- 投稿日:2019-12-30T01:32:24+09:00
言語別int64素因数分解(試し割り法)処理時間比較
C言語、C#, Java8, Golang, Python3 で、
int64の範囲内の値を素因数分解したときの処理時間の比較してみました。先に結果を見たい方はこちらへ。
処理時間一覧C#, Java, Golangについては、bigintでも計算してみました。
利用した素因数分解のアルゴリズム
素因数分解のアルゴリズムについては、最も単純な試し割り法を利用しました。
2,3,5,7,13,17, ... (以下、+2, +4を交互に繰り返した値)で割れるかどうか試していきます。処理の高速化のために、あらかじめ割れるかどうか照合する素数の表を用意しておく方法もありますが、
(63ビット程度のループ処理も10秒程度で終わったため、)今回は用意していません。下記は、Javaでの試し割り処理の例です。
PrimeFactorization.javaprivate 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 intC#
longC#
BigIntegerJava
longJava
BigIntegerGolang
int64Golang
BigIntPython3
int11111111111111
(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 の順番といったところでしょうか。
- 投稿日:2019-12-30T00:25:07+09:00
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」に影響を受けています。