- 投稿日:2020-02-20T23:27:06+09:00
Java言語で学ぶデザインパターン入門のはじめにから1章までのまとめ
お久しぶりです。白々です。
先日エンジニアの先輩にデザインパターンをご存知でない!?という冷静なツッコミを頂き、その後結城浩さんが書かれた「Java言語で学ぶデザインパターン入門」を渡されたので勉強することにしました。
ただ、本を一読しても覚えられないので覚書として記事にしようと思いました。
完走できるように頑張ります。
また、「Java言語で学ぶデザインパターン入門」には、サンプルプログラムも有りますが、著作権の都合上省かせて頂きます。御了承ください。そもそもデザインパターンって?
「Java言語で学ぶデザインパターン入門」では、
デザインパターンは、日々書いているプログラムを新しい観点から見直し、再利用しやすく、機能拡張しやすいソフトウェアを作るための有益な技法なのです。
と記載が有りました。
この記事を読んでいる皆さんは、プログラムをたくさん書いていると似たようなコードを書くことは有りませんか?私はあります。なので良く部分的にコピペしながらコードを書いています。
このようにプログラムでよく使われるパターンってたかだか23パターンしか無いよね?と言った4人の人がいて、the Gang of FourやGoFと呼ばれているそうです。
GoFは、23個のパターンに名前をつけてカタログと整理した本が「Design Patterns: Elements of Reusable Object-Oriented Software」だそうです。
該当の本について触れているリンクは以下です。
https://en.wikipedia.org/wiki/Design_Patternsグダグダ書きましたが、再利用性と機能拡張しやすいプログラムを書くために、プログラムの書き方のパターンを覚えようねってことだと思います。
第1部 デザインパターンに慣れる
第1章 Iterator —1つ1つ数え上げる
for文やwhile文のように、順序をつけて繰り返し処理するプログラムがあります。
このように繰り返し処理していくパターンをIteratorパターンと言うそうです。なんでこんなパターンが必要なの?
「Java言語で学ぶデザインパターン入門」にも記載が有りましたが、配列を使って処理するようなものは、for文を使えばいいという意見もあると思います。
結論を言えば、順に実行するようなものでも抽象化しておけば、実行対象のデータ構造が変わった場合でも、順に実行するメソッドは変更しないで済むということです。以下は、私が考えた例です。ただし、IteratorインタフェースとAggregateインタフェースは、「Java言語で学ぶデザインパターン入門」に記載があったものを使用しています。
例えば、大学で学生を管理しているとします。
「次を探すメソッド」と「次のオブジェクトを取得するメソッド」を宣言しているIteratorのインタフェースがあるとします。
また、このIteratorのインタフェースを宣言している、Aggregateインタフェースがあるものとします。1つの学科には複数人の学生がいて、学生は、名前と学籍番号を持っているとします。
学生の学籍番号と名前を管理する学生クラスがあるとし、学科に所属している学生をList型で管理している学科クラスがあるとします。
学科クラスは、Aggregateインタフェースを実装したものとします。
学科クラスの中には、「List型の変数に入っている学生を取得するメソッド」と、「何人の学生がいるのかを取得できるメソッド」と、「学生を追加するメソッド」と、後述の「Iteratorクラスのインスタンスを返しているメソッド」があるものとします。学科内の学生を学籍番号順に取得する学科Iteratorクラスがあるとします。
この学科Iteratorクラスは、Iteratorのインタフェースを実装したものとします。
この学科Iteratorクラスには、現在どこまで検索したかを管理する整数型の変数aと、「次の学籍番号を持っている学生がいるかどうかを判断するメソッド」と、「学生を取得し、変数aの値にプラス1するメソッド」があるものとします。
この学科Iteratorクラスが、まだ学生がいるかどうか判断するために、学科クラスが持っている「何人の学生がいるかを取得できるメソッド」の値と学科Iteratorクラスの変数aを比較して、変数aの値の方が小さいときまだ学生がいるものとします。
また、学科Iteratorクラスが、学生を取得するときは、学科クラスの「List型の変数に入っている学生を取得するメソッド」を使用するものとします。このとき、学科クラスで学生の管理方法をList型からArrayList型に変えたとしても、学科Iteratorクラスの実装には影響が無いため、影響範囲が学科クラス内のみとなります。
このようなときにIteratorのデザインは必要とされています。「Java言語で学ぶデザインパターン入門」には、
>抽象クラスやインターフェースを使ってプログラミングをする考え方を頭の隅に置いておいてください。という記載が有ったので、忘れないようにします。
クラス図
このクラス図は、PlantUMLというもので記載しています。
私が、書いたPlantUMLのコードは以下のGitHubに記載がありますのでReadMeを読んでお使いください。
該当ファイル名は、iterator.txtです。
https://github.com/sirajirasajiki/design_pattern_umlPlantUMLのインストールの時にお世話になったサイトは以下です。
https://qiita.com/kohashi/items/1d2c6e859eeac72ed926
PlantUMLを書くときにお世話になっているサイトは以下です。
https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48イテレータの種類
順序つけて繰り返し処理するものは、最初から順に処理する以外にも以下のような種類があります。
- 逆順
- 双方向
- 途中から最後まで処理するまとめ
集合体をひとつづつ数えるパターンをIteratorというということが分かりました。
第1章感想
簡単な処理でも抽象化しておくことで、変更箇所が少なくて済むので楽だなと思いました。
また、クラスを日本語で表現しようと頑張りましたが、割と簡単な概念なのにすごく難しかったので、プログラムって偉大だなと思いました。
後日Pythonでそれっぽく書きます。最後に
何か間違っているところがあれば、ご指摘いただけると嬉しいです!
更新履歴
2020/2/22 クラス図の項目を追加
- 投稿日:2020-02-20T20:30:45+09:00
SpringBoot-JPA-Hibernateのハマりメモ
この記事について
この記事は自分がアプリケーションの開発中にハマった点と、発見できた場合は原因、解決方法をまとめたものです。
この記事で問題としているものの、そもそもの利用法が間違っているケースや、解決方法にもっと良いものがあるかもしれません。環境
- Spring Boot 2.2.4
- Hibernate 5.4.10
- MySQL 8.0.16
遭遇した問題たち
PK以外をキーにした関連を作るとエラーが出る
データベース上では普通にあり得る話なのですが、Hibernateの実装上問題が発生することがあります。
ユースケース
@Entity class Parent { @Id Long id; @NatulalId Integer key; String firstName; String lastName; } @Entity class Child { @Id Long id; @ManyToOne @JoinColumn(name = "parent_key",referencedColumnName = "key") Parent parent; }上記のような、Hibernate自身が推すPKをサロゲートキー、
@NaturalId
で自然キーを指定するような利用法のとき、JPA仕様の範囲内だとkeyがUniqueを指定されており、idの代わりに外部キーとして機能させたい場合です。
自然キーで関連付けを行うとchild.getParent()などの実行時にキャスト絡みでエラーが発生します。1原因
解決
参照されるエンティティ(上記だとParent)をSirializableにする。
これを鑑みるに、Hibernateで使用するエンティティはすべてSirializableである方が無難かもしれません。issue trackerをざっと眺めたところ、PKではないUniqueカラムで関連付けするとこれ以外の不具合にも遭遇する可能性がある模様です。
FetchType.LazyのエンティティがJacksonによるシリアライズ時にエラーを出す
原因
Jacksonがシリアライズを行う時にアクセスしているLazy指定のプロパティの実体はHibernateのプロキシであるため
解決
Hibernateのバージョンに対応するJacksonのプラグインを追加する。
初期状態でSpringBootはHibernateとJacksonに依存しているのになぜこれは入っていないんだろう。2
Stack Overflow:No serializer found for... って?
Stack OverFlow:SpringのJacksonでLazy属性を抑制する設定
みんな苦労してます。Spring JPAエンティティのJacksonシリアライズがLazyもトリガする問題の解決
上記が非常にわかりやすく説明と解決方法を示してくれます。というわけで念のためにプラグインのGitHubです。内容はあまり親切じゃないです。
ついでに、上記GitHubで紹介されているJacksonのアノテーションによる出力制御解説(無限ループ回避策)カラム名やテーブル名関連のエラーが出る(追記・修正2020-02-21)
これは仕様の話ですね。バグではありません。
原因
デフォルト命名戦略はJPAアノテーションよりも後に処理される
単に優先順位の問題で、JPA(≒Hibernate)のテーブル名やカラム名を指定するアノテーションが使用されている場合、フィールド名からのカラム名変換が処理されていないため、エラーになります。仕様の理解が足りていませんでした。
Hibernateでは、2段階の変換を経てDBのカラム名が生成されます。
- エンティティ名、フィールド名から論理名を生成する
- 論理名を物理名に変換する
現在のSpringのデフォルトでは、
論理名の生成
- クラス名、フィールド名を論理名として取得する
@Column
や@Table
で指定されていれば、その文字列を論理名にする物理名の生成
- ピリオドをアンダースコアに置き換える
- キャメルケースをスネークケースに置き換える
という設定になっています。
@Column
や@Table
で指定された文字列を論理名にするため、フィールドやクラス名をキャメルケースで指定しているなら、アノテーションで指定する名前もキャメルケースにしなくてはなりません。ここで物理名を(つまりスネークケース)を指定していると、"Table [table name] contains physical column name [column name] referred to by multiple logical column names:"(テーブルの物理カラム名に対応する論理名2個あるんですけど)とエラーが出ます。解決
JPAのアノテーションでカラム名等を指定している場合、フィールドに3@Column
でnameを指定しておかなくてはなりません。思ったより手抜きができません。
仕様とエラーメッセージはきちんと読みましょう。
物理名の生成ですべて小文字にしてしまうため、大文字のカラム名やテーブル名を使用するために使用するのが、apprication.yamlspring: jpa: hibernate: naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImplという設定(論理名をそのまま物理名にする)になります。
ただし、Springがデフォルトで使用しているSpringImplicitNamingStrategyはHibernateのデフォルトであるImplicitNamingStrategyJpaCompliantImplをほぼそのまま使用しているため、キャメルケースのプロパティ名を使用しているとそのままキャメルケースのカラム名になってしまいます。
stackoverfrow:Hibernateの命名戦略の実例を参考に最も近いものを選んでアノテーションで調整することになるかと思います。
Hibernateの戦略を流用するだけでは足りない場合には、戦略を自分で実装することになります。
参考:
PhysicalNamingStrategyで調整
ImplicitNamingStrategyJpaCompliantImplで調整関連エンティティとキーの実値とをそれぞれ別プロパティに持ちたい
これは問題ではなくTipsですね。
キーの実値をプロパティに保持しつつ、必要になったときだけ関連をたどりたいという都合のいいエンティティの作り方です。解決
@JoinColumn
で関連エンティティをupdate,insert不可にする@Entity class Parent { @Id Long id; @NatulalId Integer key; String firstName; String lastName; } @Entity class Child { @Id Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "parent_key",referencedColumnName = "key", insertable = false, updatable = false) Parent parent; @Column(name="parent_key") Integer parentKey; }上記のようなクラスにすることでキーを直接保持しつつ親の参照が可能になり、子が親を更新してしまうのも防げます。
参照先を更新する際にはキーになっているフィールドの値を直接変更します(この例だとparentKey)。
マスターデータを参照するオブジェクトのとき、この形がとてもしっくりきます。
- 投稿日:2020-02-20T18:52:11+09:00
Spring Bootについて学んだことのまとめ
はじめに
去年の11月~12月にSpringBootを初めて触った時に学んだことを備忘録として残します。
※あくまで備忘録です対象読者
Spring Bootをこれから触ろうって方向けです。使ったことのある方には既知の内容になるかと思います。
作ったもの
受け取った入力値をもとに情報を返すだけの簡単な外部公開API。
使用した諸々
- Java(1.8)
- Spring Boot(2.1.0)
- Spring Data JPA
- Maven(3.5.3)
- Lombok(1.18.2)
今回実装したアプリの構成
各種クラスの概要
クラス名 概要 RestControllerクラス APIのインターフェース部分を実装するクラス。 Formクラス クライアントからのリクエストの内容や返すレスポンスの情報を保持するクラス。 RestControllerAdviceクラス RestController共通の処理を実装するクラス。 Serviceクラス 業務ロジックを実装するクラス。 Repositoryクラス DBアクセスを行うクラス。 Entityクラス DBから取得したデータやDBに投入するデータを保持するクラス。 各種クラスの説明とサンプル
Mainクラス
@SpringBootApplication
を付与する。
※以下の3つのアノテーションを付与した状態と等価。
アノテーション 概要 @EnableAutoConfiguration
Spring Bootの自動設定がオンになり、定義した依存関係に従って自動で各種設定を行ってくれる。 @ComponentScan
コンポーネントスキャンが実行され、スキャン対象パッケージ内の @Component
が付与されたクラスが自動で読み込まれてDIコンテナで管理される。コンポーネントスキャンしたクラス同士は@Autowired
により変数にセットすることが出来る。※後述のサンプルコード参照@Configuration
付与することで、個別にBeanを登録したり、設定クラスを読み込むことが出来る。
@ComponentScan
と@EnableAutoConfiguration
により各種クラスの読み込み、設定が行われ、アプリケーションが実行される。
※デフォルトだと@ComponentScan
が付与されたクラスと同階層のクラス、及びそれより下の階層のパッケージがスキャン対象になるため、@ComponentScan
が付与されたクラスより上の階層のクラスは読み込まれない。SampleApplication.java@SpringBootApplication public class SampleApplication { public static void main (final String[] args) { SpringApplication.run (SampleApplication .class, args); } @Override protected SpringApplicationBuilder configure (final SpringApplicationBuilder builder) { return builder.sources (SampleApplication .class); } }RestControllerクラス
@RestController
アノテーションを付与する。@ComponentScan
の対象。- APIのインターフェース部分を実装するクラス。入力値の精査や各種コンポーネントの呼び出しを行い、クライアントにレスポンスを返す。
- 各メソッドにHTTPメソッドやURLのマッピングを定義し、リクエストと呼び出されるメソッドのマッピングを行う。
- 受け取ったリクエストや返すレスポンスを扱うためにどのFormクラスを使うかは各メソッドごとに定義する。
SampleController.java/** * サンプルコントローラ。 */ @RestController @Validated public class SampleController { @Autowired private SampleService service; //使用するServiceクラスを紐づける。 @PostMapping (path = "/sample", //メソッドにマッピングするパスを指定する。 consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, //受け取るリクエストのMediaTypeを指定する。 produces = MediaType.APPLICATION_JSON_UTF8_VALUE) //返すレスポンスのMediaTypeを指定する。 @CrossOrigin public ResponseEntity<SampleForm> sampleMethod ( @RequestHeader (value = "sample_key") //ヘッダーから受け取る値のキー値を定義する。 @NotEmpty (message = "sample_key is empty.") //Formを使用せずに精査する場合はここに精査内容を定義する。 @Valid String sampleKey, //ヘッダーから受け取った値を格納する変数を定義する。 @RequestBody @Valid SampleForm form //ボディから受け取った値を格納するFormクラスと格納先の変数を定義する。 ) { return new ResponseEntity<> (service.sampleMethod (form.getSampleUserId), HttpStatus.OK); } }Formクラス
- クライアントからのリクエストの内容や返すレスポンスの情報を保持するクラス。
- 精査内容等はここに記述する。
- アノテーションによる精査定義のほか、独自の精査用メソッドを定義することも出来る。
SampleForm.java@Data @ToString public class SampleForm implements Serializable { private static final long serialVersionUID = 1L; @JsonProperty ("sample_user_id") //JSON上でのキー値を定義する。 @NotEmpty (message = "sampleUserId is empty") //精査内容を定義する。 private String sampleUserId; }RestControllerAdviceクラス
@RestControllerAdvice
を付与する。@ComponentScan
の対象。- RestControllerクラスに共通の処理を実装出来る。(事前処理やエラーハンドリング等)
@ExceptionHandler
を付与したメソッドを用意することで、指定した例外が送出された際に自動でエラーハンドリングしてくれる。SampleExceptionHandler.java@RestControllerAdvice public class SampleExceptionHandler { @ExceptionHandler (Exception.class) //キャッチする例外を定義する。 @ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR) //上記例外発生時に返すレスポンスのHTTPステータスコードを定義する。 protected ErrorForm handleInternalServerError (Exception e) { logger.error (e); return new ErrorForm ("Internal Server Error"); } @RequiredArgsConstructor private class ErrorForm implements Serializable { private static final long serialVersionUID = 1L; private final String message; } }Serviceクラス
@Service
を付与する。@ComponentScan
の対象。- 業務ロジックを実装するクラス。RestControllerクラスから受けとった入力値をもとに、各種計算やDBアクセスを行う。
- 今回はSpring Data JPAを使ったのでリポジトリクラスを呼び出すことでDBアクセスを行う。
SampleService.java@Service @Transactional //例外発生時に自動ロールバックする。 public class SampleService { @Autowired private SampleUserRepository sampleUserRepository; //使用するRepositoryクラスを紐づける。 public SampleForm returnSampleForm (String sampleUserId) { return convertToSampleForm(sampleUserRepository.findBySampleUserId(sampleUserId)); } }Repositoryクラス
@Repository
を付与する。@ComponentScan
の対象。- DBアクセスを行うクラスで、Spring Data JPAを使うとinterfaceだけ実装すれば簡単なCRUDなら実行することが出来る。
- JpaRepositoryを継承させ、アクセスするテーブルを定義する。
- 命名規約に従ってメソッド名を付けることで、自動的にCRUDのどれなのか、どんな検索条件かを読み取ってくれる。
例)findByUserId(String userId) とすると、userIdで検索を行うメソッドとなる。- クエリを実装したりテーブル結合を伴う検索をしようとするとここまで単純にはいかないが、今回は使用しなかったため割愛。
SampleUserRepository.java@Repository public interface SampleUserRepository extends JpaRepository<SampleUser, String> { //アクセス先のテーブルと主キーの型を定義する。 SampleUser findBySampleUserId (String sampleUserId); //命名規約に従ってメソッド名と引数を定義する。 }Entityクラス
@Entity
を付与する。- DBから取得したデータやDBに投入するデータを保持する。
- 今回はテーブルと1対1に紐づかせた。
SampleUser.java@Entity @Table (name = "sample_user") //紐づけるテーブル名を定義する。 @Data public class SampleUser implements Serializable { private static final long serialVersionUID = 1L; /** サンプルユーザID */ @Id //主キーの項目に付与する。 @GeneratedValue (strategy = GenerationType.IDENTITY) //自動採番させる場合は採番方法を定義する。 private String sampleUserId; /** サンプルユーザ名*/ private String sampleUserName; }参考サイト
- 投稿日:2020-02-20T18:21:30+09:00
Jenkins Pipelineのpipeline {...}の"pipeline"と"{...}"は何か(Groovy初心者向け、他言語経験者向け)
背景
お仕事でJenkins Pipelineを書くことになりました。早速サンプルをコピーし、Hello Worldが表示されてうれしい一方、こんな疑問が。
この、
pipeline
と{...}
とは何なのか?どういう文法規則なのか?Jenkinsfilepipeline { agent any stages { stage('Build') { steps { sh 'echo "Hello World"' } } } }Jenkinsfile自体は単なるデータ構造(JSONやYAMLの仲間)に見えます。ということは何か規則があるはずです。しかしそれが書かれているページが見つからない。
職場のつよつよエンジニアに聞いたところ、JenkinsfileはGroovyのプログラムということが分かりました。その後自分で調べたことと、他の言語との対比を書いておきます。
"pipeline"と"{...}"は何かの答え
pipeline
はメソッド名
{...}
はクロージャ
pipeline {...}
は、pipeline
メソッドに、{...}
というクロージャを引数として渡す、という構文です。
pipeline
はメソッドなので、Jenkinsfileの実行時にdef pipeline(Closure cl) { // 前処理 // クロージャを呼び出す cl(); // 後処理 }のように定義されたメソッドが(どこかから)読み込まれています。(上記コードは正確には少し違います。補足を参照してください)
もっと詳しく
groovyのメソッド呼び出し
上記の
pipeline
の呼び出しを冗長に書くと、pipeline({ -> // 処理 })です。
groovyのクロージャは{x, y -> x + y}
のように、{引数 -> 処理}
のように書きます。引数がない場合は、引数 ->
の部分は省略可能です1。引数を省略すると、pipeline({ // 処理 })の形になります。
また、メソッド呼び出しのカッコは、引数がある場合は省略可能なので、pipeline { // 処理 }になります。Jenkinsfileと同じ形になりました。
クロージャ
クロージャとは、メソッドの呼び出し側で動いているような振る舞いをする無名関数です。
こちらの説明が分かりやすいです。
クロージャ - Apache Groovyチュートリアル他の言語で書いてみると
Groovyのクロージャを他の言語と対比します。(ここに書ききれない言語につきましてはコメントで補足お願いします)
Ruby
Groovyの
pipeline { // 処理 }という記法は、Rubyの
pipeline { # 処理 }という記法と対応します。見た目は同じですね。GroovyはRubyに強い影響を受けていることがわかります。
Rubyのブロックを、Groovyではクロージャと言います。ブロックとクロージャの違いは、こちらのページが分かりやすいです。
Rubyist のための他言語探訪 【第 5 回】 GroovyJavaScript
Groovyの
pipeline { // 処理 }という記法は、JavaScriptの
pipeline(() => { // 処理 })という記法に対応します。JavaScriptではメソッド呼び出しのカッコは省略できず、引数がない場合も引数を囲むカッコを省略できないですが、Groovyでは省略が可能です。
Java
Groovyの
pipeline { // 処理 }という記法は、Javaの
pipeline(() -> { // 処理 })に対応します。JavaScriptと同様Javaでもメソッド呼び出しのカッコと、引数がない場合は引数を囲むカッコを省略できません。Groovyではこの2つは省略可能です。
Python
Groovyの
pipeline { // 処理 }という記法は、Pythonで対応する記法がないのですが、あえて書くとしたら
pipeline(lambda: 処理)です。Pythonの場合、lambda式は複数行にまたがることができませんが、Groovyでは可能です。
補足
上で、
pipeline
メソッドはdef pipeline(Closure cl) { // 前処理 // クロージャを呼び出す cl(); // 後処理 }のようになっていると書きましたが、正確にいうと少し違います。クロージャ内で使用できる関数を制限するため、
delegate
(移譲)を行なっています。pipeline
メソッドのソースが見つからなかったのですが、より実情に沿ったコードは以下です。def pipeline(@DelegatesTo(PipelineSpec) Closure cl) { def pipelineSpec = new PipelineSpec() def code = cl.rehydrate(pipelineSpec, this, this) code.resolveStrategy = Closure.DELEGATE_ONLY code() }本文中では分かりやすさを優先するため、クロージャをそのまま呼び出す書き方にしています。
参考資料
あとがき
Jenkinsfileを作成している最中、「コピペで動いてはいる、ただ、なんで動いているのか分からない」という状態でしたが、Groovyのプログラムということを理解し、実態がつかめました。
Jenkins Pipelineの記事がまだまだ少ないので、分かったことを今後も書けたらと思います。
正確に言うと、
{-> 処理}
と{ 処理 }
では挙動が変わります。 ↩
- 投稿日:2020-02-20T17:16:27+09:00
[Java] Javaは + で文字列連結しても大丈夫と言われていたので確かめた
試したこと
こんなファイルを作り
PlainString.javaimport java.time.LocalDateTime; class PlainString { String getString() { // え!文字列 + で連結しちゃうの!? return "abc" + LocalDateTime.now(); } }コンパイルし
javac PlainString.java
結果
逆コンパイルしてみました。
javap -c PlainString Compiled from "PlainString.java" class PlainString { PlainString(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return java.lang.String getString(); Code: 0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 7: ldc #4 // String abc 9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: invokestatic #6 // Method java/time/LocalDateTime.now:()Ljava/time/LocalDateTime; 15: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 18: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 21: areturn }しっかり
StringBuilder
に置き換えられていました。結論
イマドキのJavaは、とくに気を使わずに + 使って文字列連結して大丈夫そうです。
2020/2/22追記 @cfm-art さんからコメントいただきました。ありがとうございます。
単純に連結するだけであれば + で大丈夫。
ループのなかで + を使うと、その度にStringBuilder
が作られるのでダメ。環境
- Ubuntu 18.04.4 LTS
- OpenJDK 8u242-b08-0ubuntu3~18.04
【2020/2/22追記】 JDK13でも文字列連結を試した
JDK11 以降は StringBuilder 使われていないと教えていただいたので、試してみました。
JDK13における結果
javacしてからjavapしました。
javap -c PlainString Compiled from "PlainString.java" class PlainString { PlainString(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return java.lang.String getString(); Code: 0: invokestatic #7 // Method java/time/LocalDateTime.now:()Ljava/time/LocalDateTime; 3: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/time/LocalDateTime;)Ljava/lang/String; 8: areturn }
StringBuilder
が消えて、makeConcatWithConstants
というものに変わっていました。JDK13における結論
文字列連結にどこでも + 使って大丈夫そう。
環境
openjdk version "13.0.2" 2020-01-14
OpenJDK Runtime Environment AdoptOpenJDK (build 13.0.2+8)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 13.0.2+8, mixed mode, sharing)
- 投稿日:2020-02-20T14:25:54+09:00
【binarysearch.io】Codewars と併用すると良いかもしれないオンラインプログラミング学習サイト
Twitter でのつぶやきで知ったオンラインのプログラミング学習サイトです。
#朝ダイジェスト
— Alyson //东京黑客/แฮกเกอร์โตเกียว (@Alyson__T) January 28, 2020
競技プログラミングとかリートコードよりもまずはcodewarsやこういうのでまったりやってからだよな。
Binary Search | Master algorithms togetherhttps://t.co/jvb7UhSdym
- Java, Ruby, Python, C++, JavaScript に対応しています (2020/02/20現在)。
- チャットしながら複数人で解答を作っていくという醍醐味があるみたいですが、今回は一人で(他人のチャットに邪魔されずに)学習する方法をお教えします。
- Codewars よりも問題の質が安定しているという印象を受けました。(公式が作成した問題のみ)
- 競技プログラミングではないので、プログラミング学習のドリルとして使う、プログラミングのレベルアップに使う等の用途に向いていると思います。
1. https://binarysearch.io/ にアクセスし「Get Started」をクリックしてサインアップを行います。
2. 「Full Name」「Username」「Email」「Password」を入力して「Sign up for Binary Search」をクリックします。
3. 2で登録した「Email」に登録確認用メールが送信された旨が表示されるので、メールアプリを開いて確認用のリンクをクリックします。
4. メールアドレスの確認が完了したら「Start」ボタンをクリックします。
5. https://binarysearch.io/ のログイン後の画面が表示されます。問題を解くには「Create room」ボタンを押します。
6. Difficulty で問題の難易度を選びます。外国人の方からチャットで話かかけられたりしないように Capacity を「1 player」にしておく(または Private にしておく)と良いかもしれません。
7. room を作成したら「Start Room」ボタンを押して問題を開始します。(今回は Easy で作成しました)
8. 「引数として与えられた配列の長さが1かどうかを返す」よう関数を完成させなさい、という問題です(注記:こんな簡単すぎる問題ばかりではありません)。解答言語としてJavaScriptを 選択しました。
9. 解答を作成したら「Run▶」ボタンをクリックします。「Correct!」と表示されたら「Submit code」ボタンをクリックします。
10. 問題によっては、「Solution」というタブに解法・解説が提示されることがあります。(この問題は簡単すぎなので解説がないんだと思います)
11. 別の問題を解くには「Start Room」ボタンを再度押して次の問題を開始します。部屋を抜けるには画面右上の×ボタンをクリックします。
最後に
公式の問題ばかりなので(?)問題の質が安定している(均一)なのが良いですね。気になったのは同じレベルで学習を続けていると同じ問題が出てくることがあることですが、そのまま解いちゃえば良いので大きな問題ではないと思います。
- 投稿日:2020-02-20T14:25:54+09:00
【binarysearch.io】Codewars と併用すると良いかもしれないオンラインプログラミング学習サイト (Master algorithms together)
Twitter でのつぶやきで知ったオンラインのプログラミング学習サイトです。
#朝ダイジェスト
— Alyson //东京黑客/แฮกเกอร์โตเกียว (@Alyson__T) January 28, 2020
競技プログラミングとかリートコードよりもまずはcodewarsやこういうのでまったりやってからだよな。
Binary Search | Master algorithms together https://binarysearch.io/
- Java, Ruby, Python, C++, JavaScript に対応しています (2020/02/20現在)。
- チャットしながら複数人で解答を作っていくという醍醐味があるみたいですが、今回は一人で(他人のチャットに邪魔されずに)学習する方法をお教えします。
- Codewars よりも問題の質が安定しているという印象を受けました。(公式が作成した問題のみ)
- 競技プログラミングではないので、プログラミング学習のドリルとして使う、プログラミングのレベルアップに使う等の用途に向いていると思います。
1. https://binarysearch.io/ にアクセスし「Get Started」をクリックしてサインアップを行います。
2. 「Full Name」「Username」「Email」「Password」を入力して「Sign up for Binary Search」をクリックします。
3. 2で登録した「Email」に登録確認用メールが送信された旨が表示されるので、メールアプリを開いて確認用のリンクをクリックします。
4. メールアドレスの確認が完了したら「Start」ボタンをクリックします。
5. https://binarysearch.io/ のログイン後の画面が表示されます。問題を解くには「Create room」ボタンを押します。
6. Difficulty で問題の難易度を選びます。外国人の方からチャットで話かかけられたりしないように Capacity を「1 player」にしておく(または Open to public を Private にしておく)と良いかもしれません。
7. room を作成したら「Start Room」ボタンを押して問題を開始します。(今回は Easy で作成しました)
8. 「引数として与えられた配列の長さが1かどうかを返す」よう関数を完成させなさい、という問題です(注記:こんな簡単すぎる問題ばかりではありません)。解答言語としてJavaScriptを 選択しました。
9. 解答を作成したら「Run▶」ボタンをクリックします。「Correct!」と表示されたら「Submit code」ボタンをクリックします。
10. 問題によっては、「Solution」というタブに解法・解説が提示されることがあります。(この問題は簡単すぎなので解説がないんだと思います)
11. 別の問題を解くには「Start Room」ボタンを再度押して次の問題を開始します。部屋を抜けるには画面右上の×ボタンをクリックしてから「Leave」を選択します。
最後に
公式の問題ばかりなので(?)問題の質が安定している(均一)なのが良いですね。気になったのは同じレベルで学習を続けていると同じ問題が出てくることがあることですが、そのまま解いちゃえば良いので大きな問題ではないと思います。
- 投稿日:2020-02-20T00:10:21+09:00
COTOHA API の構文解析 を Java で100回呼んでみた
CotohaNlpService クラスの作成
と
をベースに、COTOHA APIを簡単に利用できるように、以下のクラスを作成しました。
(後日、Maven Repository にて公開予定です)CotohaNlpService.java
https://github.com/oyahiroki/nlp4j/blob/master/nlp4j/nlp4j-cotoha/src/main/java/nlp4j/cotoha/CotohaNlpService.javaこのクラスを利用すると以下のような感じで、簡単に形態素解析結果を得ることができます。
(API呼び出しに必要な変数は環境変数としてセットしておきます)
別途、JSONを整形して改行区切りでテキストファイルに保存するクラスを作成しましたので、呼び出し回数の節約も可能になりました。CotohaNlpService service = new CotohaNlpService(); DefaultNlpServiceResponse response = service.nlpV1Parse("今日は学校に走って行きました。"); // レスポンスのJSON が出力される。キーワードクラスとして返すメソッドも用意している。 System.err.println(response.getOriginalResponseBody());データの用意
国土交通省「自動車のリコール・不具合情報」からデータをダウンロードして、100件分を利用しました。
手でHTMLをコピペするのは当然面倒ですので、ごにょごにょ しました。^_^;国土交通省「自動車のリコール・不具合情報」
http://carinf.mlit.go.jp/jidosha/carinf/opn/index.html100回呼んでみる
100回呼んでみた結果は以下のとおりです。
横軸が試行回数で、縦軸が処理時間です。
クライアント側でのJSONパースの処理を含んでいますが、数msと考えてよいでしょう。
最初に2000 msくらいかかっていますが、これはTokenの取得も含んでいます。
だいたいが 200-300 msに収まっているのですが、時々突発的に遅くなるようです。
DEV環境向けのは遅いという可能性も高いと思いますし、もちろんネットワーク環境にもよると思います。次に、文字列の長さと処理時間のグラフです。
見たところ、文字列の長さと処理時間に相関はなさそうです。(極端に長い文字などは変わる可能性が高いと思います)以下、生データです。
length,time 61,2001 73,337 54,310 79,349 58,274 51,269 41,660 21,263 38,283 74,295 52,4472 70,1138 68,3074 31,243 39,251 15,219 11,258 14,259 62,293 66,276 27,272 18,220 63,278 62,428 68,284 50,288 43,250 45,264 70,273 58,250 157,593 88,280 66,264 26,272 38,1514 8,237 42,256 53,1472 42,2668 35,230 32,235 36,241 116,325 17,254 102,309 59,268 21,220 43,278 64,249 32,246 31,247 27,252 70,3698 61,340 51,233 23,225 20,226 60,310 50,1685 72,281 37,270 45,253 13,224 54,243 64,302 52,1876 90,3251 30,9501 73,2323 70,3689 70,1304 61,303 67,262 17,3032 128,302 63,272 33,238 32,257 106,3906 57,261 103,299 82,270 71,268 158,803 41,255 36,284 62,304 36,234 38,1778 19,1478 90,345 22,239 62,310 72,2555 66,256 25,927 33,242 39,283 24,237 42,247以上
- 投稿日:2020-02-20T00:10:21+09:00
COTOHA API の構文解析 を Java で100回呼んでパフォーマンス測定しました。
CotohaNlpService クラスの作成
と
をベースに、COTOHA APIを簡単に利用できるように、以下のクラスを作成しました。
(後日、Maven Repository にて公開予定です)CotohaNlpService.java
https://github.com/oyahiroki/nlp4j/blob/master/nlp4j/nlp4j-cotoha/src/main/java/nlp4j/cotoha/CotohaNlpService.javaこのクラスを利用すると以下のような感じで、簡単に形態素解析結果を得ることができます。
(API呼び出しに必要な変数は環境変数としてセットしておきます)
別途、JSONを整形して改行区切りでテキストファイルに保存するクラスを作成しましたので、呼び出し回数の節約も可能になりました。CotohaNlpService service = new CotohaNlpService(); DefaultNlpServiceResponse response = service.nlpV1Parse("今日は学校に走って行きました。"); // レスポンスのJSON が出力される。キーワードクラスとして返すメソッドも用意している。 System.err.println(response.getOriginalResponseBody());データの用意
国土交通省「自動車のリコール・不具合情報」からデータをダウンロードして、100件分を利用しました。
手でHTMLをコピペするのは当然面倒ですので、ごにょごにょ しました。^_^;国土交通省「自動車のリコール・不具合情報」
http://carinf.mlit.go.jp/jidosha/carinf/opn/index.html100回呼んでみる
100回呼んでみた結果は以下のとおりです。
横軸が試行回数で、縦軸が処理時間です。
クライアント側でのJSONパースの処理を含んでいますが、数msと考えてよいでしょう。
最初に2000 msくらいかかっていますが、これはTokenの取得も含んでいます。
だいたいが 200-300 msに収まっているのですが、時々突発的に遅くなるようです。
DEV環境向けのは遅いという可能性も高いと思いますし、もちろんネットワーク環境にもよると思います。次に、文字列の長さと処理時間のグラフです。
見たところ、文字列の長さと処理時間に相関はなさそうです。(極端に長い文字などは変わる可能性が高いと思います)以下、生データです。
length,time 61,2001 73,337 54,310 79,349 58,274 51,269 41,660 21,263 38,283 74,295 52,4472 70,1138 68,3074 31,243 39,251 15,219 11,258 14,259 62,293 66,276 27,272 18,220 63,278 62,428 68,284 50,288 43,250 45,264 70,273 58,250 157,593 88,280 66,264 26,272 38,1514 8,237 42,256 53,1472 42,2668 35,230 32,235 36,241 116,325 17,254 102,309 59,268 21,220 43,278 64,249 32,246 31,247 27,252 70,3698 61,340 51,233 23,225 20,226 60,310 50,1685 72,281 37,270 45,253 13,224 54,243 64,302 52,1876 90,3251 30,9501 73,2323 70,3689 70,1304 61,303 67,262 17,3032 128,302 63,272 33,238 32,257 106,3906 57,261 103,299 82,270 71,268 158,803 41,255 36,284 62,304 36,234 38,1778 19,1478 90,345 22,239 62,310 72,2555 66,256 25,927 33,242 39,283 24,237 42,247以上