20210801のJavaに関する記事は12件です。

CodeSignal - firstNotRepeatingCharacterにチャレンジ

コーディングしなさすぎてコーディングを忘れたエンジニアがコーディングを思い出すためにコーティングします。 今回は、アメリカのコーデイング練習サイトCodeSignalの問題「firstNotRepeatingCharacter」を解いていきます。 サイトによると、この問題はAmazonの面接で出されたことがあるようです。 問題 "abacabaed"のような文字列sが渡されるので、最初に出てくる重複していない文字を探します。 上の例だと、重複していないし最初に出てくるのでcが答えです。 もし重複している文字がない場合は、'_'を返します。 考え方・解説 最初は単純に、文字を一つずつループして、もう一つループを回して他の文字と比較し、重複がない文字を探す方法が浮かびました。 しかしこの方法で回答すると、実行時間超過でテストが一部通りませんでした。 そこで、この解き方の無駄を探すと、それぞれのループで全ての文字をチェックしていたので、重複した文字を何度も比較してしまっていることに気づきました。 たとえば、文字列の中にaが1000回あれば、2回目のaでその文字は重複しているため答えではないのがわかるのに、その後の997個のaも比較をしている、という具合です。 そこで、ループで重複が検知できたらループを即座に抜け、次の確認する文字に移るという回答に書き換えたところ、テストに通ることができました。 Javaで書くとこんな感じです。 char firstNotRepeatingCharacter(String s) { boolean unique; for (int i = 0; i < s.length(); i++) { unique = true; for (int j = 0; j < s.length(); j++) { if (i != j && s.charAt(i) == s.charAt(j)) { unique = false; break; } } if (unique) { return s.charAt(i); } } return '_'; } コードの説明 2つのforは文字列の各文字を一つづつループしています。 1つめのifで2つのループそれぞれの文字を比較し、同じであればその文字は重複あり、つまり答えではないので、uniqueフラグをfalseにします。 と同時に、breakでループを抜け次の文字に移ります。 各文字がループし終わるたびに、2つめのifの部分で変数uniqueの結果を確認し、trueのままの文字があればそれは重複していなかったことを意味するので、答えとして返却します。 最後まで何も返却されなければ、重複なしのため'_'を返します。 感想 2つのループどちらもすべての文字をチェックするのは無駄かなとなにか改善しようと試みましたが、重複があればすぐにループがbreakされるので、結局問題ないようでした。 回答後、他の人の回答を見るともっと上手いやり方があって勉強になりました。 そのコードを載せるのは微妙なので仕組みだけ書くと、ループは一つで、各文字について文字列sの中で出現する位置を確認し、最初のインデックス(s.indexOf)と最後のインデックス(s.lastIndexOf)を取得します。 それらのインデックスが同じ値であれば、文字列の中に1個しか存在しないので、重複なし(=答え)。 インデックスが異なれば、複数存在しているので重複あり(=答えではない)、という感じでした。 自分にはまったく思い浮かびませんでした。 ネステッドループを無くすというアイデアをしつこく考えれば、もしかしたら思い付けたかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】新卒が初めてのコードレビューで受けた指摘5選

はじめに 新卒でSIerのJavaプログラマとして働き始めて2ヶ月が経ちました。 忙しい中、自分の拙いコードを見てくださる先輩方には感謝しかありません。 今後同じ指摘を受けないように、コードレビューでの指摘をまとめようと思います。 ①Nullぽを起こさないようにチェックを入れる。 Customer customer = getCustomer(request); System.out.println("ようこそ、" + customer.getName() + "さん"); もしgetCustomerで顧客情報が取得できなかった場合、customerはnullとなり、getNameメソッドを呼び出す時点でヌルポが発生します。 Nullチェックするときは、CollectionUtils.isEmptyとか、StringUtils.isEmptyとかを使いましょう。 ②Stringのequalsメソッドで比較する際は、定数を左に持ってきてヌルポを回避する String answer = getAnswer(); if(answer.equals("A")) return "正解です"; // (1) if("A".equals(answer)) return "正解です"; // (2) (1)の場合は、仮にgetAnswerで答えを取得できずanswerにNullが入ったらヌルポが起こります。 (2)の場合は、左側に定数があるためヌルポは起こりません。 ③マジックナンバーは使わない 数値とか、”01”みたいな文字列は、後々何を表すか分からなくなります。 以下のコードは、なんとなく”01”と”02”が春夏で、”03”と”04”が秋冬であると察せますが分かりにくいです。また、別の箇所で”01”が、全然違った意味で使われた場合、確実に混乱します。 String seasonNo = clothes.getSaleSeasonNo(); if("01".equals(seasonNo) || "02".equals(seasonNo)) return getThisYear() + "SS"; if("03".equals(seasonNo) || "04".equals(seasonNo) ) return getThisYear() + "AW"; マジックナンバーには名前をつけてあげましょう。 private final String SPRING = "01"; private final String SUMMER = "02"; private final String AUTUMN = "03"; private final String WINTER = "04"; ④定数を作る プログラム内で変わらない値がある場合は、定数として定義します。 必要であれば、どんな定数かコメントアウトをつけましょう。 また、変数名は大文字にします。 public class Sample { private static final String GREETING_MESSAGE = "Hello, world!"; System.out.println(GREETING_MESSAGE); } また、様々な場所で使われる定数の場合は、切り出しましょう。 constsパッケージ内のConstクラスに書けばいいかなと思っていましたが、Enumクラスというものを初めて知りました。 ※余談 private static finalについて目に止まった記事 ⑤いらないelseを書かない シンプルにこちらの記事がとても分かりやすかったです。 https://refactoring-brique.info/logic/else/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaで経過日数を計算したい

Javaで経過日数を計算したい場合、もっともシンプルなのはLocalDateとChronoUnitを利用するものです。 public static long diff(LocalDate localDate1, LocalDate localDate2) { return ChronoUnit.DAYS.between(localDate1, localDate2); } しかしライブラリや開発環境の都合上、DateやCalendarを利用せねばならないということもあります。この場合、DateやCalendarをLocalDateに変換してしまうのがもっともわかりやすいと思います。 public static long diff(Date date1, Date date2) { LocalDate localDate1 = date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate localDate2 = date2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); return ChronoUnit.DAYS.between(localDate1, localDate2); } public static long diff(Calendar calendar1, Calendar calendar2) { LocalDate localDate1 = calendar1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); LocalDate localDate2 = calendar2.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); return ChronoUnit.DAYS.between(localDate1, localDate2); } 利用しているJavaのバージョンやコーディングルールなどにより、そもそもLocalDateの利用ができない場合は、おとなしく秒数から計算しましょう(´・ω・`) public static long diff(Date date1, Date date2) { long time1 = date1.getTime(); long time2 = date2.getTime(); return Math.abs(time1-time2) / (1000*60*60*24); } public static long diff(Calendar calendar1, Calendar calendar2) { long time1 = calendar1.getTimeInMillis(); long time2 = calendar2.getTimeInMillis(); return Math.abs(time1-time2) / (1000*60*60*24); } 環境情報 D:\>javac --version javac 11.0.10 D:\>java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列の出現回数を数えたい

特定の文字列の出現回数を数えたい場合、たとえばPythonだと $ python3 Python 3.8.5 (default, Jul 28 2020, 12:59:40) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> "AABABAAAB".count("AB") 3 というように、非常に簡単に書けるのですが、Javaでは一工夫が必要になります。 public static int count(String string, String target) { int i = -1; int c = 0; while ((i = string.indexOf(target, i+1))!=-1) c++; return c; } 実際に使ってみると、次のようになります。 System.out.println(count("AABABAAAB", "A")); //=> 6 System.out.println(count("AABABAAAB", "AB")); //=> 3 System.out.println(count("AABABAAAB", "ABA")); //=> 2 System.out.println(count("AABABAAAB", "AAAA")); //=> 0 想定通りに動いていそうですね(´・ω・`) 環境情報 D:\>javac --version javac 11.0.10 D:\>java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.10+9) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.10+9, mixed mode) 2021/08/01 22:45追記 上記のロジックは「文字列の出現回数を重複ありで数える」もので、Python3と同じように「文字列の出現回数を重複なしで数える」場合は以下のようにすべきでした。 public static int count(String string, String target) { int c = 0; int i = 0; int len = target.length(); while ((i=string.indexOf(target, i))!=-1) { c++; i += len; } return c; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【俺的】COBOL→Java言語移行ガイド(導入)

はじめに 検証環境 PC: MacBook Air(mid2020) OS: macOS Big Sur 11.5.1 COBOL: GnuCOBOL 3.1.2.0 C: 12.0.5 (gcc -dumpversionの結果) Javaのタグを付けましたが、今回はあまり関係ありません。 COBOLからJavaへの移行について COBOLが生まれてから60年以上たった今、COBOLを採用している企業の一部では、Javaなどオープン系言語へのマイグレーションが進みつつあります。 実際ググると、なぜマイグレーションが進んでいるのかはあちこちに書かれているのですが、自身で調べたことや考えを、勉強としてまとめてみようと思います。 また、別記事(後日)では、COBOLとJavaで書き方がどう変わるのかも書いてみようと思います。 なぜCOBOLからJavaへの移行が進むのか これにはいくつか理由があります。 1. COBOLの運用ができる人がいない このご時世、入社していきなり「私COBOLできます!」なんて人は超超稀ではないでしょうか。自分自身も学生時代はCOBOLの存在は知っていたものの「そんなもんもう使ってる企業ないだろ」といった感じでしたので、入社してCOBOL研修があると聞いたときはかなりびっくりしました。 ただ、COBOL言語自体は事務処理言語だけあって、基本文法が単純な上、OpenCOBOLやGnuCOBOL等を使えばパソコンでも勉強できます。 どちらかというと、個人的に覚えるのに大変なのは運用で使うJCLだと思います。オンライン処理であれば、「ここをクリックすればこれが動く」といった感じで想像がつきやすいです。特にOpenCOBOLは実際、記述言語はCOBOLですが、コンパイルの際は、一度Cに変換してからgccで実行ファイルを作成します。なので、フロントエンドがサーブレット、バックエンドがOpenCOBOLの場合、実際やってることはJavaからgccでコンパイルした実行ファイルを動かすだけですので、まださらに想像がつきやすいと思います(リクエストデータをどうやってLINKAGEに渡すのかはおいといて)。 COBOLソースのC変換 たとえばこんな単純なCOBOLソースがあったとします。 *--1---------2---------3---------4---------5---------6 IDENTIFICATION DIVISION. PROGRAM-ID. ZAMA8722. DATA DIVISION. WORKING-STORAGE SECTION. 01 HELLO-MSG. 02 FILLER PIC X(12) VALUE "HELLO WORLD!". PROCEDURE DIVISION. EXAMPLE-ST. DISPLAY HELLO-MSG. EXAMPLE-EX. STOP RUN. これを cobc -Cコマンドで叩くと $ ls EXAMPLE.cbl $ cobc -C EXAMPLE.cbl $ ls EXAMPLE.c EXAMPLE.c.h EXAMPLE.c.l.h EXAMPLE.cbl Cソースが出来上がります。 話を戻しまして…。 では逆に、メインフレームで動くバッチだとどうでしょうか。 メインフレームで動くバッチ処理では通常、JCLを使ってCOBOLプログラムを動かします。ちなみに、 JCL 【Job Control Language】 ジョブ制御言語 JCLとは、メインフレームのOSなどで利用できるプログラミング言語の一種で、実行する処理(ジョブと呼ばれる)の名前や使用する装置などをシステムに対して指示することができるもの。 JCL(ジョブ制御言語)とは - IT用語辞典 e-Words です。まぁ今で言えばShell Scriptみたいな存在でしょう。 これこそ実践できる機会が少ないです。JCLはOpenCOBOLのようにパソコン上で動かせる環境がないので、実際の環境で動かす以外ありませんし、入出力の定義の書き方がテープですので、テープについても学ばなければいけません。 なので、個人的にCOBOLができる人というより、COBOLプログラムを運用できる人が少ないし、育成も大変だと思っています。 とはいえCOBOL自体もできる人がいないのも現状ですので、たとえ情報系大学・専門学校卒を採用しても、1から教育し直しになることがほとんどですので教える側も教えられる側も大変です。 2. メインフレームを売っているメーカーが少ない COBOLが市場からなくなっていくと同時に、メインフレーム本体やその部品を販売するメーカーは減ってきています。内、販売のみで生産委託するメーカーもありますので、生産しているメーカーはもっと少ないです。 日本のメーカーだと、例えば日立は2017年にメインフレーム製造を撤退し、開発はOSだけです。生産は全部IBMに丸投げ状態になっています。1 同記事によると、2017年現在で国内稼働台数が約150台、そこから年々10%ずつ減少しているそうです。2 もしいま使っているメインフレームが撤退したメーカーであれば、万が一故障したときのサポートもどうなるかわかりませんし、たとえ壊れなかったとしても、メインフレームが古くなったとき、販売しているメーカーから買うのも確かにありですが、メインフレームのメーカーを変えるとJCLなどが多少変わってきたりして、それはそれでマイグレーションが必要なので、そこまでするなら一層オープン系に...といった感じでしょうか。 マイグレーションのいいところ システム総入れ替えするくらいですから、いいことはあります。 1. 人材確保・教育が容易になる これは先程の「COBOLの運用ができる人がいない」の反対で、Javaなどのオープン系言語は、情報系大学・専門学校卒であれば、経験者も豊富です。 また、オープン系言語であれば資料や技術記事がネットにゴロゴロ転がっていますし、当然パソコンでも動きますので、教育やスキルアップも用意になります。 2. クラウド化が進められるようになる3 これはネット記事を見て「あ〜!確かに」ってなりました。 書きたいことは参照先が全てですので、そちらの記事をご覧ください。 3. COBOLにおいてのリコンパイルがなくなる これは大規模システムの開発を担うエンジニアにとっては超負担軽減になるのではないでしょうか。 そうです。COBOLにおいてのリコンパイルがなくなります。 COBOLにおいてのリコンパイル COBOLでは通常、テープのレイアウトを外部に持たせ(COPYファイル)、それを複数のCOBOL資源で共有します。 例えば、COPYファイルが *--1---------2---------3---------4---------5---------6 01 HELLO-MSG. 02 FILLER PIC X(12) VALUE "HELLO WORLD!". で、COBOLソースが *--1---------2---------3---------4---------5---------6 IDENTIFICATION DIVISION. PROGRAM-ID. ZAMA8722. DATA DIVISION. WORKING-STORAGE SECTION. COPY "./RAYOUT.cbl". PROCEDURE DIVISION. EXAMPLE-ST. DISPLAY HELLO-MSG. EXAMPLE-EX. STOP RUN. のとき、COBOLソースをコンパイルすると、出力結果は HELLO WORLD! ですが、これがもしCOPYファイルが *--1---------2---------3---------4---------5---------6 01 HELLO-MSG. 02 FILLER PIC X(9) VALUE "GOOD BYE!". となったとき、COPYファイルを変更しただけでは、出力は HELLO WORLD! のままです。そこで、最新のCOPYファイルを取り込むには、COBOLソースをもう一度コンパイルする必要があります。これがいわゆる「COBOLにおいてのリコンパイル」です。 リコンパイルすれば GOOD BYE! 最新のCOPYファイルが取り込まれ、出力メッセージが変わります。 1つのCOPYファイルを複数のCOBOLソースで使用ている場合、COPYファイルが変更になると、それを使用しているCOBOLソースは全部リコンパイル対象になります。大規模システムになると、1COPYファイルに対して、使用しているCOBOLプログラムは数十個では済まない時があり、そうなるともちろん、リコンパイル漏れが発生する可能性も出てきます。 しかし、オープン言語ではテープレイアウトも何も、COPYファイルに相当するものがないので、こういったリコンパイルはガサッとなくなります。 マイグレーションの課題・懸念点 ただし、いいことばかりではありません。 1. マイグレーションが原因のバグが起こりかねない マイグレーションする上で一番怖いのはおそらくこれです。 今まで安定し、稼働実績のあるプログラムを、違う言語で同じ処理を書き直すわけですから、「今まで安定していた処理にバグが起きる」といったリスクは当然上がってきます。 せっかく新しいシステムにしたのにバグだらけになってしまうと近代化改修大失敗です。 2. 信頼性が下がる メインフレームの売りは信頼性だと思っています。JANOGの資料によれば、メインフレームのMTBF(平均故障間隔)は99.999%と言われています。4これは、1年間で約5分程度しかシステムの停止を許さないことになります。 対してサーバの稼働率は、ググってみると99.99%であるところが大半でした。その差はわずか0.009%ですが、計算すると、99.99%だと1年間で53分弱システム停止することになり、その差は約10倍です。 これが通常のWebサービスならまだしも、お金を取り扱う金融や保険業界では痛手だと思います。 3. これはこれでコストがかかる COBOL人口が減っている今、COBOLエンジニアは希少価値となりつつあるため、必然的にコストが上がります。いわゆる運用コストです。オープン系へのマイグレーションでコストがかかるのは、反対に初期コストです。 オープン系に移行するということは、今まで使用してきたメインフレームをサーバに、COBOLソースをオープン系ソースに変えることになりますから、初期コストが大きくなります。また、今のCOBOLエンジニアをJavaエンジニアに育成する必要もありますから、そこでもコストが発生します。 ただ、オープン系のエンジニアはたくさんいますし、サーバも導入してしまえば、運用コストはCOBOLより抑えることができるのではないでしょうか。 本日はここまで 情報系の大学を卒業して、COBOL業務に就任して数年の視点で、なぜマイグレーションが進んでいるのかをまとめてみました。 もちろん、どこか間違っていることもあると思いますので、その際はコメントで教えていただけますと幸いです。 次は、COBOLとJavaで書き方がどう変わるのかを、基本文法あたりから書いてみようと思います。 閲覧、ありがとうございました。 日立がメインフレーム製造から完全撤退、開発はOSだけ | 日経クロステック(xTECH) ↩ 年々10%減少している件は、xTECHでなくて読めなかったので、この記事を参考生き残るメインフレーム|のぞえ/Hideki Nozoe|note ↩ COBOLからJavaへのマイグレーションの前に知っておくべきこと ⋆ ONETECH Blogs ↩ メインフレームサーバーの凄さについて ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@JvmStaticと@JvmFieldをobject(またはcompanion object)のフィールドに付与した場合の違いについて

基礎的な部分にはなりますが、一瞬迷ってしまうのでいろんなパターンを試してみました。 JvmStaticアノテーション ・メソッドに付与すると、INSTANCE・Companionオブジェクトを経由せずアクセスできるようになる。 ・フィールドに付与すると、getter/setterを作成してくれる。 JvmFieldアノテーション ・メソッドに付与はできない。(構文エラーが出ます。) ・フィールドに付与すると、INSTANCE・Companionオブジェクトを経由せずアクセスできるようになる。 上記を踏まえて とりあえず使ってみました。 適当なフィールドを持つクラスを作成。 JvmTestKotlinObject.ktの方はobjectキーワードで定義しているので、それ自体がシングルトン。 JvmTestKotlinClass.ktの方はclassキーワードで定義しcompanion objectを宣言しているので、シングルトンを内包しているクラスです。 JvmTestKotlinObject.kt object JvmTestKotlinObject { @JvmStatic var varField1: String = "" @JvmStatic val valField1: String = "" @JvmField var varField2: String = "" @JvmField val valField2: String = "" const val constValField2 = "" var field3: String = "" } JvmTestKotlinClass.kt class JvmTestKotlinClass { companion object { @JvmStatic var varField1: String = "" @JvmStatic val valField1: String = "" @JvmField var varField2: String = "" @JvmField val valField2: String = "" const val constValField2 = "" var field3: String = "" } } それらをJavaから見てみる。 まずはJvmTestKotlinObject.kt。 JvmTestJava.java public void testKotlinObject() { /** @JVMStatic var **/ JvmTestKotlinObject.getVarField1(); JvmTestKotlinObject.setVarField1("hoge"); /** @JVMStatic val **/ JvmTestKotlinObject.getValField1(); // これはだめ。 //JvmTestKotlinObject.setValFIeld1("hoge"); /** @JVMField var **/ String varField2 = JvmTestKotlinObject.varField2; JvmTestKotlinObject.varField2 = "hoge"; /** @JVMField val **/ String valField2 = JvmTestKotlinObject.valField2; // これはだめ。 //JvmTestKotlinObject.valField2 = "hoge"; /** const **/ String constValField2 = JvmTestKotlinObject.constValField2; // これはだめ。 //JvmTestKotlinObject.constValField2 = ""; /** アノテーションなしpublicなプロパティ **/ // INSTANCEオブジェクト経由ならアクセス可能。 JvmTestKotlinObject.INSTANCE.getField3(); JvmTestKotlinObject.INSTANCE.setField3("hoge"); } 次にJvmTestKotlinClass.kt。 JvmTestJava.java public void testKotlinClass() { /** @JVMStatic var **/ JvmTestKotlinClass.getVarField1(); JvmTestKotlinClass.setVarField1("hoge"); /** @JVMStatic val **/ JvmTestKotlinClass.getValField1(); // これはだめ。 //JvmTestKotlinClass.setValFIeld1("hoge"); /** @JVMField var **/ String varField2 = JvmTestKotlinClass.varField2; JvmTestKotlinClass.varField2 = "hoge"; /** @JVMField val **/ String valField2 = JvmTestKotlinClass.valField2; // これはだめ。 //JvmTestKotlinClass.valField2 = "hoge"; /** const **/ String constValField2 = JvmTestKotlinClass.constValField2; // これはだめ。 //JvmTestKotlinClass.constValField2 = "hoge"; /** アノテーションなしpublicなプロパティ **/ // INSTANCEオブジェクト経由でもアクセスは不可。 //JvmTestKotlinClass.INSTANCE.getField3(); //JvmTestKotlinClass.INSTANCE.setField3("hoge"); // もちろんこれもだめ。 //JvmTestKotlinClass.getField3(); //JvmTestKotlinClass.setField3("hoge"); // Companionオブジェクト経由ならアクセス可能。 JvmTestKotlinClass.Companion.getField3(); JvmTestKotlinClass.Companion.setField3("hoge"); } アノテーションは便利ですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NLP4J - 構文解析した結果からキーワードを抽出する

Index 構文解析した結果からキーワードを抽出する例 「車が高速道路で急に停止した。エンジンから煙がもくもくと出た。」 という文から 「なにがどうした(名詞 ... 動詞)」 の関係を抽出することにしてみます。 日本語話者であれば 「車が ... 停止する」 「煙が ... 出る」 という関係を見つけ出すことができると思います。これを自然言語処理で行ってみたいと思います。 ここでいう「キーワード」は「切り出したい意味のある文字列」です。 構文解析をする 構文解析のエンジンとして、構文解析器として著名な「Cabocha」を利用します。 NLP4JでCabochaを利用する方法については以下の記事で紹介しております。 NLP4J - Java で構文解析(Cabochaを利用) Cabochaで処理を行った結果、以下のような係り受け解析の結果(=構文解析の結果)を取得することができます。 -。 -た -する -停止 -が -車 -で -道路 -高速 -に -急 -。 -た -出る -から -エンジン -が -煙 -もくもくと 構文解析をした結果からキーワードを抽出する 「構文解析を行う」までは各種書籍やネット記事で紹介されているのですが、キーワード抽出の例は多くは紹介されていません。 NLP4Jでは構文解析結果からのキーワード抽出処理の機能を用意してあります。 UserPatternAnnotator という名称のクラスを用意しており、XML形式の設定ファイルを指定することでユーザーの定義による抽出を可能にしています。 XMLを用意することで、構文解析の結果から任意のパターン抽出ができるようになっています。(ライブラリにバグが無ければ!) <?xml version="1.0" encoding="UTF-8"?> <patterns lang="ja"> <!-- facet : 抽出したキーワードをどのファセットに割り当てるか --> <!-- value : 抽出したパターンからキーワードの正規形を定義する --> <!-- {0.lex} : id=0 のキーワードの正規形 --> <!-- {2.lex} : id=2 のキーワードの正規形 --> <!-- <w /> : 単語を表す --> <!-- <w lex="/(が|は)/" /> : 正規形が「が」または「は」である単語 --> <!-- <w upos="ADP" /> : Universal POSが ADP(助詞) である単語 --> <!-- <w upos="NOUN" /> : Universal POSが NOUN(名詞) である単語 --> <pattern facet="pattern.sv" value="{0.lex} ... {2.lex}"> <w id="2" upos="VERB"> <w id="1" lex="/(が|は)/" upos="ADP"> <w id="0" upos="NOUN" /> </w> </w> </pattern> <pattern facet="pattern.sv" value="{0.lex} ... {2.lex}"> <w id="3" lex="する"> <w id="2" upos="NOUN"> <w id="1" lex="/(が|は)/" upos="ADP"> <w id="0" upos="NOUN" /> </w> </w> </w> </pattern> </patterns> 以下が実際のファイルになります。 https://github.com/oyahiroki/nlp4j/blob/master/nlp4j/nlp4j-examples/src/main/java/nlp4j/pattern/examples/UserPatternAnnotatorExample.java 内部的な処理としては 1. 構文解析の結果を取得してツリー構造とする 2. パターン抽出の設定を読み込んでツリー構造とする 3. 構文解析の結果のツリー構造と、パターン抽出設定のツリー構造を比較して、マッチしたところをキーワードに変換 という仕組みになっています。 Maven Dependency Maven での設定は以下になります。 <dependency> <groupId>org.nlp4j</groupId> <artifactId>nlp4j-core</artifactId> <version>[1.3.1.0,)</version> </dependency> 以下がMavenレポジトリになります。 https://mvnrepository.com/artifact/org.nlp4j/nlp4j-core Java Code 以上で紹介したCabochaとキーワード抽出処理のコードは以下のようになります。 package nlp4j.pattern.examples; import nlp4j.Document; import nlp4j.Keyword; import nlp4j.KeywordWithDependency; import nlp4j.cabocha.CabochaAnnotator; import nlp4j.impl.DefaultDocument; import nlp4j.pattern.UserPatternAnnotator; public class UserPatternAnnotatorExample { public static void main(String[] args) throws Exception { String text = "車が高速道路で急に停止した。エンジンから煙がもくもくと出た。"; Document doc = new DefaultDocument(); doc.putAttribute("text", text); { // 係り受け解析 CabochaAnnotator ann = new CabochaAnnotator(); ann.setProperty("encoding", "MS932"); ann.setProperty("target", "text"); ann.annotate(doc); } { // 係り受けパターン抽出 UserPatternAnnotator ann = new UserPatternAnnotator(); ann.setProperty("file", "src/test/resources/nlp4j.pattern.examples/pattern-ja-sv.xml"); ann.annotate(doc); } // Expected Result // 車 ... 停止 // 煙 ... 出る for (Keyword kwd : doc.getKeywords("pattern")) { System.err.println(kwd.getLex()); } } } 結果 コードを実行すると以下の結果が出力されます。 構文解析の結果から、XMLの設定だけでキーワードの抽出ができました! 車 ... 停止 煙 ... 出る まとめ NLP4J を利用すると構文解析の結果からキーワードの抽出ができます。 Index
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java開発の技術用語

Javaサーブレット ・webサーバー上(バックエンド)で動くJavaプログラム サーブレットのコード(HelloWorld.java) import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException { response.setContentType(“text/html;charset=Shift_JIS”); PrintWriter out=response.getWriter(); out.println(“<html><head>”); out.println(“<title>Hello, World!!</title>”); out.println(“</head><body>”); out.println(“<p>Hello,World!!</p>”); out.println(“</body></html>”); } } JSP ・Webページ内にJavaプログラムを埋め込み、これをサーバ上で実行して結果を反映したページを動的に生成することができる技術。 JSPのコード(HelloWorld.jsp) <html> <head> <title>Hello, World!!</title> </head> <body> <% out.println(“<p>Hello,World!!</p>”); %> </body> </html> 近年では、JSPの代わりにテンプレートエンジンが活用されている。 テンプレートエンジンとは 役割 ・Servletの役割→データベースアクセスや計算処理のようなロジックを書き、JSPをデータを渡しつつ呼び出す ・JSPの役割→Servletからデータを受け取り、HTMLを動的に出力する 参考 サーブレット(Java Servlet)とは?【概要を5分で把握】 サーブレット&JSPとは? 概要と2つの役割の違いを解説 「Servlet」と「JSP」の違い MVCモデルで表すと 要素 役割 ファイル Model アプリケーションの主たる処理やデータの格納などを行う 一般的なクラス View ユーザーに対して画面の表示を行う JSPファイル Controller ユーザーからの要求を受け取り、処理の実行をモデルに依頼し、その結果の表示をビューに依頼する サーブレットクラス 参考 - MVCモデルの簡単な例 サーブレットコンテナ(Webコンテナ) HTMLなどのWebページを動的に生成するJavaサーブレットを動作させるためのソフトウェアのことである。 Java Servletを動かすときに必要なソフト Java Servletを動作させる実行環境のことをサーブレットコンテナと言う。  参考 サーバ構築のために知っておきたい「Apache Tomcat」について コンテナの種類 Tomcat Webサーバーの機能も含んだサーブレットコンテナ Glassfish Tomcatと同じような性質のアプリケーション Tomcatの大きな違いとして、Glassfishは有償での商用利用が可能 Jetty 軽量なWebサーブレットコンテナ 軽量であるため組み込み系のJavaシステムに利用することが多いです。 webサーバー WEBブラウザからのリクエストに応じて静的画面や画像などのホームページのデータを Web ブラウザーに送ってくれるサーバーのこと 参考 今さら聞けない WEBサーバとはどんなサーバ? WEBサーバの種類 Apache オープンソースでありながら高い信頼性と充実した機能を備えた世界中で最も多く使われているWEBサーバであります。Linuxで多く使われている。 Nginx(エンジンエックス) Apacheと同じくオープンソースであり、同時リクエストの処理に特価していて、軽量で処理が早いWEBサーバ IIS(Microsoft Internet Information Services) Microsoft Windowsの標準WEBサーバ ApacheとTomcatを連携させるメリット Tomcat単体でもWebサーバとしての機能を確保している。 Tomcatだけでサーバ処理を行えばシンプルだが、Apacheと比較すれば性能は劣るため、2つを連携させることで優位なサーバ構築が可能となる。 参考 サーバ構築のために知っておきたい「Apache Tomcat」について Javaのフレームワーク Spring Framework Seasar2 純国産のJavaフレームワーク Seasarの由来は、中心となって開発した比嘉康雄さんが沖縄出身だったため 2016年9月26日 をもって EOL (End of Life)となっている。 Struts Struts1・2ともに脆弱性が指摘されていて現象傾向となっている。 Strutsを採用していた大手の企業が、外部からの攻撃により多数の被害が出ている。 Spring Frameworkとは 2004年にリリースされたオープンソースのフレームワーク。 他のフレームワークと比較し、汎用性の高さに定評がある。 DI(依存性注入)やAOP(アスペクト指向プログラミング)など、システム開発やプログラムの修正がしやすくなる仕組みが導入されている。 Spring Frameworkはフレームワークの集合体で、機能に応じて多数のフレームワークで構成されている。これによって、幅広い開発ができるが、機能の使い分けが難しいというデメリットもあった。 DI(Dependency Injection:依存性の注入) DIは日本語で言うと「依存性の注入」になります。 意味的には「オブジェクトの注入」と理解すれば良いです。 クラスの外から依存性(オブジェクト)を注入してあげることで、クラス間の依存関係を解決するといった目的を持ちます。 参考 Spring Boot入門① ~DI~ 依存性注入(DIコンテナ)について AOP(Aspect Oriented Programming:アスペクト指向プログラミング)とは プログラムの特定の処理を「アスペクト」と呼ばれる機能単位として分離して記述し、プログラム中の様々な対象に適用できるようにする手法 参考 ざっくりとSpringで使うAOPの解釈をする Spring bootとは Spring Frameworkの機能の使い分けが難しいというデメリットを解決するために作られたフレームワーク Spring BootもSpring Frameworkをベースとするフレームワークの1つである。 Spring FrameworkとSpring bootの違い Spring Frameworkは、Java言語のアプリケーション開発のために作られたフレームワークの集合体。 Spring bootは、集合体となった機能を使いやすくするためのフレームワーク。 参考 Spring Bootとはなにか テンプレートエンジンとは テンプレートと呼ばれる雛形と入力データを合成し、成果ドキュメントを出力する仕組み 近年、JSPの代わりにテンプレートエンジンを活用して画面設計を行う事例が増えて来ている。 JSPの課題点 1.デザイナーとエンジニアの作業の境界が曖昧になる 2.最終的にブラウザで描画される結果と異なる 参考 テンプレートエンジンとは何か? Thymeleafを使ったテンプレートベースのJava Webアプリケーション開発について Spring Bootで利用可能なテンプレートエンジン Thymeleaf th:○○という属性をHTMLタグに追加する。独自タグを使用しないため、HTMLファイルを表示しても、HTMLを崩さず表示できる。 参考:https://www.nttdata.com/jp/ja/data-insight/2019/0805/ FreeMarker 表示する場所に${○○}を埋め込む。制御は<#○○>。HTMLファイルを表示すると、これらのタグはHTMLに影響を与えてしまう。 Groovy Java仮想マシン上で動作するスクリプト。HTMLとはまったく異なるコード体系なので、学習コストが高い。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java独習(第6章)

この記事ではJava独習の第6章(コレクションフレームワーク)についてのまとめを行います。 コンテンツは以下の通り ・コレクションフレームワークの基本 ・リスト ・セット ・マップ ・スタック/キュー 6-1 コレクションフレームワークの仕組み コレクション=オブジェクトの集合を表す仕組み 配列との違いは以下のようなものがあげられる ・既知のデータ構造やアルゴリズムを取り込むことができパフォーマンスが高い ・共通的な操作をインターフェイスとして定義しているのでデータ構造やアルゴリズムにとらわれず同じように操作可能 Collectionのサブインターインターフェイスとして以下のようなものがある ・リスト(順序を持ち重複OK)-ArrayListとLinked List ・セット(重複NG)-hashSetとLinkedHashsetとTreeSet ・マップ(キーと値の組み合わせで管理)-HahsMapとTreeMap ・スタック/キュー(戦闘と末尾の追加や削除が可能)-ArrayDequeとLinkedList コレクションのインターフェイス化は インターフェイス型 <要素型> = new 実装クラス型<>(引数....) List = new ArrayList<>(); ArrayList data = new ArrayList<>(); でもいいが、実数型を引数として渡すのは望ましくない もしLinkedList(別の実装)に変える場合全部変えなければいけないため ジェネリクス=汎用的なクラスやメゾットを特定の型に紐づけるための仕組み Listのように本来の汎用型のように<...>の形式で個別の型を割り当てることによってなんでもOkとするわけではなく、文字列だけになる的な 要素を順番に取り出すときはforを使用 内部的にはイテレータを利用している。イテレーターとはコレクションの要素を順番に取り出す仕組み イテレーターのインターフェイスは以下のようなものがある boolean has Next() 次の要素が存在するか E next() 次の要素の取得(Eは要素の型) void remove 現在の要素の削除 Java import java.util.ArrayList; public class IteratorRemove { public static void main(String[] args) { var data = new ArrayList<String>() { { add("バラ"); add("ひまわり"); add("あさがお"); } }; var ite = data.iterator(); while (ite.hasNext()) { System.out.println(ite.next()); ite.remove(); } System.out.println(data); } } ite.removeをコメントアウトするとこの場合バラ・ひまわり・あさがおと順番に出てきて最後に配列が表れるが、removeメゾットを使用することで配列が表れない 逆順から読み込むときはiteratorメゾットではなく、listIteratorメゾットでListIteratorメゾットを取得 Java import java.util.ArrayList; public class IteratorList { public static void main(String[] args) { var data = new ArrayList<String>() { { add("バラ"); add("ひまわり"); add("あさがお"); } }; var ite = data.listIterator(data.size()); while (ite.hasPrevious()) { System.out.println(ite.previous()); } } } 配列とコレクションの変換は以下のようにする Java import java.util.ArrayList; import java.util.Arrays; public class CollToArray { public static void main(String[] args) { var data = new ArrayList<String>(Arrays.asList("バラ", "ひまわり", "あさがお")); var strs = new String[data.size()]; data.toArray(strs); data.set(0, "チューリップ"); System.out.println(Arrays.toString(strs)); System.out.println(data); } } しかし、あくまでこれは配列であるのでリストの追加や削除は配列をコピーして新規にリストを作成 Colelction.addAllメゾットを利用 Java import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public class CollAddAll { public static void main(String[] args) { var data = new String[] { "バラ", "ひまわり", "あさがお"}; var list = new ArrayList<String>(); Collections.addAll(list, data); list.set(0, "チューリップ"); list.add("さくら"); list.remove(1); System.out.println(list); System.out.println(Arrays.toString(data)); } } コレクションから配列はtoArrayメゾットつかう Java import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public class CollAddAll { public static void main(String[] args) { var data = new String[] { "バラ", "ひまわり", "あさがお"}; var list = new ArrayList<String>(); Collections.addAll(list, data); list.set(0, "チューリップ"); list.add("さくら"); list.remove(1); System.out.println(list); System.out.println(Arrays.toString(data)); } } 変更不能にするためにはunmodifiablexxxx用いる unmodifiableListやunmodifiableMapなど 6-2 リスト リストの実装には以下のようなものがある ArrayList, LinkedList, Vector(なお今は使わない) ArrayListの基本メゾット Java public class ListArray { public static void main(String[] args) { var list = new ArrayList<Integer>(Arrays.asList(10, 15, 30, 60)); var list2 = new ArrayList<Integer>(Arrays.asList(1, 5, 3, 6)); var list3 = new ArrayList<Integer>(Arrays.asList(1, 2, 3)); for (var i : list) { System.out.println(i / 5); } System.out.println(list.size()); //4 System.out.println(list.get(0)); //10 System.out.println(list.contains(30)); //true System.out.println(list.indexOf(30)); //2 System.out.println(list.lastIndexOf(30)); System.out.println(list.isEmpty()); System.out.println(list.remove(0)); System.out.println(list); list.addAll(list2); System.out.println(list); list.removeAll(list3); System.out.println(list); list.set(0, 100); var data = list.toArray(); System.out.println(Arrays.toString(data)); } } LinkedListは個別の要素のアクセスは戦闘と末尾のリンクからたどる =インデックス値による要素の読み書きには不向きで挿入や削除は早い Java public class ListLinked { public static void main(String[] args) { var list = new LinkedList<String>(Arrays.asList("うさぎ", "たつ", "へび")); System.out.println(list); list.addFirst("とら"); list.addLast("うま"); System.out.println(list); System.out.println(list.getFirst()); System.out.println(list.getLast()); System.out.println(list.removeFirst()); System.out.println(list.removeLast()); System.out.println(list); } } 6-3 セット 重複した要素は無視で順番を持たない集合 HashSetとLinkedHashSetとTreeSetがある 2つのセットがあるとしたら retainAllは共通部分 removeAllは片方にあるもののみ addAllはすべての要素 Java public class SetBasic { public static void main(String[] args) { var hs = new HashSet<Integer>(Arrays.asList(1, 20, 30, 10, 30, 60, 15)); var hs2 = new HashSet<Integer>(Arrays.asList(10 ,20 ,99)); System.out.println(hs); System.out.println(hs.size()); System.out.println(hs.isEmpty()); System.out.println(hs.contains(1)); System.out.println(hs.containsAll(hs2)); System.out.println(hs.remove(1)); System.out.println(hs); hs.addAll(hs2); System.out.println(hs); hs.retainAll(hs2); System.out.println(hs); var hs3 = new HashSet<Integer>(Arrays.asList(1, 10 , 20)); hs.removeAll(hs3); System.out.println(hs); } } HashSetは一切並び順を管理しないが、TreeSetは並び順を管理 Java public class SetTree { public static void main(String[] args) { var ts = new TreeSet<Integer>(Arrays.asList(1, 20, 30, 10, 60, 15)); System.out.println(ts); System.out.println(ts.descendingSet()); System.out.println(ts.ceiling(15)); System.out.println(ts.lower(15)); System.out.println(ts.tailSet(15)); System.out.println(ts.headSet(30, true)); } } 6-4 Map 一意のキーと値で管理 HashMapを最も利用するので本記事ではいったんTreeHashはおいておく HashMapはキーの順序が保証されないので順番に意味があるものはTreemapをつかう HashMapは内部的にハッシュ票を持っていて要素を保存するときにキーから八種値を求めてどこに保存するか決める HashMapの基本動作 Java public class MapHash { public static void main(String[] args) { var map = new HashMap<String, String>(Map.of("Rose", "バラ", "Sunflower", "ひまわり", "Morning Glory", "あさがお")); System.out.println(map.containsKey("Rose")); System.out.println(map.containsValue("バラ")); System.out.println(map.isEmpty()); for (var key : map.keySet()) { System.out.println(key); } for (var value : map.values()) { System.out.println(value); } map.replace("Rose", "薔薇"); map.replace("Sunflower", "ひまわり", "向日葵"); for (var entry : map.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } } } mapはコレクションのサブインターフェイスではないためfor命令に渡すことはできない 6-5 スタック ArrayDequeは循環配列 Java public class DequeArray { public static void main(String[] args) { var data = new ArrayDeque<Integer>(); data.addLast(10); data.addLast(15); data.addLast(30); System.out.println(data); System.out.println(data.removeLast()); System.out.println(data); var data2 = new ArrayDeque<Integer>(); data2.addLast(10); data2.addLast(15); data2.addLast(30); System.out.println(data2); System.out.println(data2.removeFirst()); System.out.println(data2); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java再入門 第2回 モジュールシステムとクラスパス、Gradle

Java 9に導入されたモジュールシステムについて、モジュールシステムの登場以前のクラスパスの世界を振り返りながら、Gradleを使っている場合の話も含めて整理してみようと思います。 モジュールとは何か 複数のパッケージを関連するもので固めてグループ化したものです。 モジュールシステムによって以下が実現されました。 依存関係の明確化: モジュール同士の依存関係を宣言するので、依存性をモジュール単位で検証できます。 公開範囲の設定: モジュール内のパッケージは明示的にエクスポートされたもののみが他のモジュールからアクセスできます。 モジュールシステムの登場以前のクラスパスの世界 まずはモジュールシステムの登場以前のクラスパスの世界を確認しておこうと思います。 古い時代からある話なので、古いドキュメントを参照してみましょう。 Java5の公式資料:クラスの検索方法 Java5の公式資料:クラスパスの設定 まず前提Javaのクラスには位置づけの異なる以下の3種類が存在します。 ブートストラップクラス…主にjre/lib/rt.jarに存在するクラスのことです。 拡張機能クラス…jre/lib/ext以下の.jarに存在するクラスのことです。 ユーザクラス…クラスパスで指定された場所に存在しているクラスのことです。クラスパスはjava -classpathで指定するか、環境変数のCLASSPATHで指定します。 Java 8以前の環境がある場合はインストール先のjre/以下をみてみてください。rt.jarとかがありますよね。 実は自分で作ったJarもjre/lib/ext以下に放り込んでおくとパスが通った状態になります。普通はそんなことはしませんけど。 クラス検索の優先順位 最強はブートストラップクラスで、これが最優先で解決されます。次は拡張機能クラスのパスに保存されているのが強いです。 ユーザークラスは-jarで指定されているjarの中に入っているものがもっとも強く、次が-classpathで最後が環境変数のCLASSPATHです。 jre/lib/rt.jar > jre/lib/ext > -classpath > 環境変数のCLASSPATH -classpathに;区切りで複数のパスが指定されているときは前から順番に優先されるので、以下の例だとpath1が優先されます。 java -classpath '/home/testuser/path1/*;/home/testuser/path2/*' Main ややこしいのはTomcatのようなものを使っている場合でもっと複雑になります。 以下の図は公式ページからの抜粋ですが、クラスローダーが以下のような階層構造を持っていて、Webapp1とWebapp2が相互にお互いは見えないように専用のクラスローダーを持ちつつ、Tomcatとして共有するクラスはCommonクラスローダーが読み込む仕組みになっています。 これまで説明してきたブートストラップクラスとか拡張機能クラスはこの図でいうBootstrapクラスローダーが読み込んでいて、Systemクラスローダーが普通に-classpathを指定した場合などで使われるクラスローダーです。 モジュールシステム Project Jigsaw: Quick Start Guide 【Java】モジュールシステムめも【Java Silver】 - くまごろーのプログラミングメモ Javaのモジュールシステム入門 | tyablog.net Java JMODファイルとは、JARファイルとの違い。 | tyablog.net モジュールシステムの話になったときに登場する用語、ファイルをまず整理してみます。 module-info.java module-info.javaファイルはモジュールシステムの中核に存在するファイルです。 依存する自分以外のモジュールと、モジュール内のパッケージで他のモジュールに公開する範囲を記述します。 モジュールシステムはモジュールのバージョンの管理を役割に含めていないので、バージョンの指定はしません。 JARファイル JARファイルは健在です。モジュールを一つのファイルにまとめる手段として引き続き利用されます。 JMODファイル モジュールという単位で、クラスファイルとネイティブコードなどをZipもので、コンパイル、リンク時に利用します。 JARのようにランタイム時に参照はできません。 かつて存在していたブートストラップクラスとか拡張機能クラスはJMODに組み込まれました。 module-info.java 難しいことはなくてこれだけといえばこれだけです。 module moduleb { // modulecに依存している requires modulec; // transitiveがあるとmoduledをmodulebをrequiresしたモジュールからも参照できるようになる requires transitive moduled; // モジュール内のパッケージの公開範囲 exports jp.co.module.b; } 自動モジュールと無名モジュール 自動モジュール 自動モジュールはmodule-info.java を含まないjarファイルをモジュールPATHに配置したものです。 この場合、モジュール名はManifestファイルにAutomatic-Module-Name属性があればその値になり、なければjarファイル名をから作られます。 Jarの中のパッケージは、全てexportsされたものとして扱われます。 無名モジュール jarファイル、classファイルを、クラスPATHに配置したものが無名モジュールです。 モジュール名はなく、この状態のモジュールは自動モジュールからしかアクセスできません。 まとめると以下のようになります。 引用:イマドキのJava徹底入門(5) Javaのモジュールシステムを理解しよう(その2) | TECH+ ビルド方法と依存関係の調べ方 Gradleなどを使わずにコンパイルを手でやることはあまりないかもしれませんが、以下のようにします。 javac -d ..\クラスファイルの出力先 javaファイルのパス javaファイルのパス javaファイルのパス たとえば以下のような感じです。 javac -d ..\mods src\module-info.java src\jp\co\module\cprivate\HelloCCommon.java src\jp\co\module\c\HelloC.java 標準ライブラリのモジュール以外に依存するモジュールがある場合は以下のようにします。 javac -d ..\クラスファイルの出力先 --module-path 依存するモジュールのパス javaファイルのパス javaファイルのパス javaファイルのパス たとえば以下のような感じです。 javac -d ..\mods --module-path ..\mods src\module-info.java src\jp\co\module\bprivate\HelloBCommon.java src\jp\co\module\b\HelloB.java 実行するときはこんな感じになります。 java --module-path モジュールのパス -m モジュール名/モジュール内のメインメソッドを持つクラスのフルパス たとえば以下のような感じです。 java --module-path ..\mods -m modulec/jp.co.module.c.HelloC 依存関係を調べることもできます。 jdeps --list-deps hello.jar モジュールシステムとGradle 7と7以前で違うのですが、7の場合で書きます。 7ではjava.modularity.inferModulePathがデフォルトで有効になりました。 以下公式の抜粋ですが、モジュールの宣言とGradleの設定は以下の対応関係にするべきとされています。ただ、本当にこの設定になっているかどうかはチェックされません。また、モジュール未対応のライブラリの扱いは残念な感じになっていて、そのままでは利用することができません。 以下のようにモジュールシステムに対応していない'commons-cli:commons-cli:1.4'を使うのはとっても面倒です。 plugins { id 'java' } group 'org.example' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { implementation 'com.google.code.gson:gson:2.8.6' implementation 'org.apache.commons:commons-lang3:3.10' implementation 'commons-cli:commons-cli:1.4' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0' } test { useJUnitPlatform() } module modtest.main { requires com.google.gson; requires org.apache.commons.lang3; } この状態になってしまうので、モジュールシステムに対応していないライブラリに依存している場合は、アーティファクト変換を使用して、既存のJarをモジュール対応させるか、モジュールシステムを使うのをあきらめるかとかが公式では案内されています… つまりどうしたらいいのか? これからアプリケーションを開発するときどうしたら良いのかですが、私は以下のように理解しています。 過渡期で仕方ないのかもしれないですけど、中途半端な感じですね… モジュールシステムに対応していないライブラリに依存している場合 自作のアプリケーションは自動モジュールにします。 モジュールシステムに対応していないライブラリに依存していない場合 自作のアプリケーションは名前付きモジュールにします。 シリーズJava再入門 いまからJava8にしよう!だってJava11よりサポート長いし!という判断もあると思いますが、11に引っ越そうとしている方や、11の次のLTSになることを予定されている17への移行を目指している方向けのJava再入門です。 Java7以降の歴史 機能追加とサポート期間 モジュールシステムとクラスパス、Gradle - Qiita ~Java11 ジェネリクス、ダイアモンド演算子 ~Java11 日付時刻API ~Java11 リソース付きtry文 ~Java11 ラムダ式、メソッド参照、Stream、Optional、ローカル変数型推論 ~Java11 ExecutorService/Future、Fork/Join 今後:テキストブロック 今後:switch式、パターンマッチングinstanceof 今後:record
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot で Ajax を実装する単純なサンプル

はじめに 初心者用の丁度よい記事が見当たらなかったので、Spring Boot で Ajax を動かすためのサンプルコードを残しておきます。 文字列を送信すると、2回繰り返して表示されるだけの簡単なものです。 Ajax は jQuery を使用して書きますが、参考としてネイティブ Java Script も併記しておきます。 <参考サイト> ・Ajax:Jsonで受け取り画面遷移なしに更新する ・SpringBoot × Ajax通信による画面遷移 ・どのようにしてajaxでSpring Controllerからデータを取得できますか? 1. プロジェクトの作成 プロジェクトは、どのように作成しても構いませんが、一応書いておきます。 サンプルでは、ビルドツールは「Maven」、Java バージョンは「11」、パッケージングは「Jar」を選んでいます。とはいえ、何でも構いません。 特別なライブラリも入れる必要はありません。 次のように、「Spring Web」と「Thymeleaf」だけ選んでおきます。 Thymeleaf は直接には使用しませんが、HTML ファイルの読み込みで必要になるので入れておきます(参考)。 2. サンプルコード 以下、POST メソッドで HTTP リクエストを送信して、サーバからデータを受け取るサンプルコードです。 2-1. 作成するファイル ファイルは、赤枠の3つを作成します。 js ファイルは、/src/main/resources/static に「js」フォルダを作成した上で格納します。 2-2. HTML ファイル HTML は次のように記述しておきます。 /src/main/resources/templates/index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Ajax Test</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="/js/note.js"></script> </head> <body> <form method="post" action="/test" id="note_form"> <input type="text" id="note"> <button type="submit">送信</button> </form> <div class="notes"></div> </body> </html> <補足> src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js" で jQuery をGoogleのCDN経由で読み込んでいます(参考)。 src="/js/note.js" で使用する JavaScript ファイルを指定します。 <div class="notes"> のところに Ajax で取得した文字列を表示します。 2-3. JavaScript ファイル 2-3-1. jQuery で実装 jQuery で書くと次のようになります。 /src/main/resources/static/js/note.js $(function() { $("#note_form").on("submit", function(e) { e.preventDefault(); // デフォルトのイベント(HTMLデータ送信など)を無効にする $.ajax({ url: $(this).attr("action"), // リクエストを送信するURLを指定(action属性のurlを抽出) type: "POST", // HTTPメソッドを指定(デフォルトはGET) data: { note: $("#note").val() // 送信データ } }) .done(function(data) { $(".notes").append(`<div>${data}</div>`); // HTMLを追加 $("#note").val(""); // 入力欄を空にする }) .fail(function() { alert("error!"); // 通信に失敗した場合の処理 }) }); }); <補足> $(this).attr("action") で、action 属性のURL文字列("/test")を取り出しています。 type: "POST" で、HTTP メソッドを「POST」と指定しています。 data: { note: $("#note").val() } で、リクエストで送るデータを「キー/値のペア」で指定しています。id="note" で取得する値を特定しています。 $(".notes").append([HTML文]); で、class="notes" のタグの子要素として [HTML文] を追加しています。 jQuery による Ajax のひな形を簡単に示すと次のとおりです(詳細はこちらを参照してください)。 jQueryのAjax実装のひな形 $.ajax({ url: /* リクエストを送信するURLを指定 */, type: /* HTTPメソッドを指定 */, data: /* 送信データを指定 */, dataType: /* レスポンスデータの形式を指定 */ }) .done(function(data) { // (1) リクエストが成功した場合に行う処理 }) .fail(function() { // (2) リクエストが成功しなかった場合に行う処理 }) .always(function() { // (3) リクエストの成功・失敗に関わらず行う処理 }); <参考サイト> ・JavaScript 日本語リファレンス jQuery $.ajax() ・一般的なAjax通信を実装するには?($.ajax) 2-3-2. (参考)ネイティブ javaScript で実装 なお、参考までにネイティブ javaScript で書くと次のようになります(無視してよい内容なので、折りたたんでいます)。 javaScript のソースコード /src/main/resources/static/js/note.js window.addEventListener("load", function() { document.getElementById("note_form").addEventListener("submit", function(e) { e.preventDefault(); // デフォルトのイベントを無効にする let inputText = document.getElementById("note").value; // 入力値を取得 let url = document.getElementById("note_form").getAttribute("action"); // action属性のurlを抽出 let data = "note=" + encodeURIComponent(inputText); // URIエンコード let httpRequest = new XMLHttpRequest(); // XMLHttpRequestのインスタンス作成 httpRequest.open("POST", url, true); // open(HTTPメソッド, URL, 非同期通信[true:default]か同期通信[false]か) httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); // リクエストヘッダーを追加(URIエンコードで送信) httpRequest.send(data); // sendメソッドでサーバに送信 httpRequest.onreadystatechange = function() { if (httpRequest.readyState === 4) { // readyStateが4になればデータの読込み完了 if (httpRequest.status === 200) { // statusが200の場合はリクエストが成功 let divElm = document.createElement("div"); // 追加用のdivタグを作成 divElm.appendChild(document.createTextNode(httpRequest.response)); // レスポンスで得た値をdivタグの子要素に追加 document.getElementsByClassName("notes")[0].appendChild(divElm); // class="notes" の子要素として追加 document.getElementById("note").value = ""; // テキストエリアを空白に戻す } else { // statusが200以外の場合はリクエストが適切でなかったとしてエラー表示 alert("error"); } } }; }, false); }); 補足は、コメント部分を参照してください。 なお、Spring Security を導入すると、CSRF 対策への対応も必要となると思います。 JavaScript の Ajax については、次の記事で細かく書いていますのでご参考となれば幸いです。 ・RailsにおけるAjaxの実装(JavaScriptとjQueryのコード比較) ・JavaScriptの多次元ハッシュ(連想配列)を一括でURIエンコードする 2-4. Controller コントローラーは、次のように書きます。 /src/main/java/com/example/demo/TestController.java package com.example.demo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping public class TestController { @GetMapping public String index() { return "index"; } @PostMapping("/test") @ResponseBody public String note(@RequestParam String note) { return note + note; } } ① リクエストデータを受け取る クライアントから送信されたリクエストパラメータを受け取るには、@RequestParam アノテーションを使用します。 @RequestParam String note とすることで、キーが note のデータを取得することができます。 ② メソッドの戻り値をレスポンスデータにする @Controller のメソッドの戻り値は、基本的に view の遷移先となります。 メソッドに @ResponseBody アノテーションを付けることで、戻り値を HTTP レスポンスのコンテンツとすることができます。 なお、@RestController を使用すると、メソッドの戻り値がそのままレスポンスのコンテンツとなります(後述「3-2. @RestController を使用する場合」に記載)。 サンプルコードは以上です。 これで、Ajax を使用したデータのやり取りができます。 3. 補足 補足として、別のパターンによる実装にも触れておきます。 3-1. レスポンスデータを JSON で返す場合 レスポンスデータは JSON で返すことの方が多いと思いますので、そのパターンを書いておきます。 3-1-1. JavaScript ファイル JavaScript ファイルでは、レスポンスデータを JSON 形式で受け取れるように修正します。 3-1-1-1. jQuery で実装 /src/main/resources/static/js/note.js $(function() { $("#note_form").on("submit", function(e) { e.preventDefault(); $.ajax({ url: $(this).attr("action"), type: "POST", data: { note: $("#note").val() }, dataType: "json" // レスポンスデータをjson形式と指定する }) .done(function(data) { $(".notes").append(`<div>${data.note}</div>`); // JSON形式のレスポンスからnoteを取得 $("#note").val(""); }) .fail(function() { alert("error!"); }) }); }); 修正したのは、コメントを付した2行です。 dataType: "json" で、レスポンスデータをjson形式と指定しています。 ${data.note} で、JSON 形式で受け取った data から、キーが note の値を取得しています。 3-1-1-2. (参考)ネイティブ javaScript で実装 ネイティブ javaScript で書くと次のとおりです。 javaScript のソースコード /src/main/resources/static/js/note.js window.addEventListener("load", function() { document.getElementById("note_form").addEventListener("submit", function(e) { e.preventDefault(); let inputText = document.getElementById("note").value; let url = document.getElementById("note_form").getAttribute("action"); let data = "note=" + encodeURIComponent(inputText); let httpRequest = new XMLHttpRequest(); httpRequest.open("POST", url, true); httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); httpRequest.setRequestHeader("Accept", "application/json"); // リクエストヘッダーを追加(クライアントが理解できるコンテンツタイプをサーバに通知) httpRequest.responseType = "json"; // レスポンスデータをjson形式と指定 httpRequest.send(data); httpRequest.onreadystatechange = function() { if (httpRequest.readyState === 4) { if (httpRequest.status === 200) { let divElm = document.createElement("div"); divElm.appendChild(document.createTextNode(httpRequest.response.note)); // JSON形式のレスポンスからnoteを取得 document.getElementsByClassName("notes")[0].appendChild(divElm); document.getElementById("note").value = ""; } else { alert("error"); } } }; }, false); }); 修正したのは、コメントを付した部分です。 ① レスポンスデータをjson形式と指定 レスポンスデータをjson形式と指定するには、次の2行が必要です. Sample httpRequest.setRequestHeader("Accept", "application/json"); httpRequest.responseType = "json"; 最初の行では、クライアントが受け取るデータタイプを JSON と指定しています(参考記事)。 2行目は、表示形式を JSON 形式に指定しています(指定しなければ"note":"hogehoge"、指定すればnote: "hogehoge" という形のデータになるだけです)。 ② JSON 形式のデータから値を取得 次の1行で、受け取ったデータ httpRequest.response から、キーが note のデータを取り出して、HTML コードを生成しています。 Sample divElm.appendChild(document.createTextNode(httpRequest.response.note)); なお、Spring Security を導入すると、CSRF 対策への対応も必要となると思いますので、ご留意ください。 3-1-2. Controller コントローラーでは、次のようにして、レスポンスデータを単純に JSON 形式にしています。 これにより、戻り値は "{"note":"テストテスト"}" というような文字列になります。 /src/main/java/com/example/demo/TestController.java // 略 @Controller @RequestMapping public class TestController { // 略 @PostMapping("/test") @ResponseBody public String note(@RequestParam String note) { String json = "{\"note\":\"" + note + note + "\"}"; return json; } } <参考サイト> ・JSONとは?データフォーマット(データ形式)について学ぼう! 3-2. @RestController を使用する場合 @Controller に代えて @RestController を使用すると、メソッドの戻り値がそのままレスポンスのコンテンツになります。 Ajax のみのコントローラーを作成する場合は、こちらを使用した方がシンプルに記述できます。 /src/main/java/com/example/demo/TestController.java // 略 @RestController @RequestMapping public class TestController { @GetMapping public ModelAndView index() { ModelAndView mav = new ModelAndView(); mav.setViewName("index"); return mav; } @PostMapping("/test") public String note(@RequestParam String note) { return note + note; } } <参考サイト> ・Spring BootのRestControllerからHTMLを生成する方法 さいごに とりあえず、順序通りにコードを書けば動くものを記事として残しておきました。 参考として書いたネイティブ Java Script は、CSRF 対策を考慮していないので、暇があれば調べて書いてみようと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java × GoogleCalendarAPI でタスク管理アプリ

はじめに ポートフォリオとして制作したアプリケーションのポイントを投稿させていただきます。 ポートフォリオURL:https://task-time.net (恐れ入りますがまだGoogleCloudPlatformの審査中のため、ログインページから先は進めません…m(_ _)m) GitHub:https://github.com/Nakamura9310/TaskTime アプリケーションのコンセプト 「①全タスクをメモし②所要時間を見積もり③全てカレンダーに入れる」ことで 多忙な時に真価を発揮する、タイムマネジメントアプリ 前職時に本当に多忙な毎日を送る中で、タイムマネジメントの重要性を知りました。 タスクをメモするだけではダメです。 どのタスクに何時間かける予定か、カレンダーに落とし込むことができていれば、 タイムマネジメントができているといえます。 ①〜③ができていると →新しいタスクが発生した時に、いつ実行可能なのか瞬時に判断できる。 ①〜③ができていないと… ▲下手にタスクを受け、期限内に終わらない量のタスクを抱えることになる。 →一人でタスクを抱え込み、チーム内で自分のタスクが過多となる。 ▲期限延長を依頼するべきか or 周囲を頼るべきなのか等を適切に早く判断できない。 →最悪期限を守れなかった場合は社内外に迷惑をかける。 理想 (今を月曜の午前中とすると) 先輩「悪いけどこの見積やっといてもらえる?多分30分くらいで終わるからさ。」 自分「今タスクが↓こんな感じの状況なので最速でも取りかかれるのが水曜の13時からですね。それでもOKですか?」 先輩「あ〜じゃあ他の人に任せるわ。」 自分「了解です〜。」(勝った…!) アプリの概要 以下の機能を備えています。 1.SpringSecurity と GoogleOAuthを用いたログイン機能があります。 2.シンプルなタスクのCRUD処理ができます。 3.各タスクに設定した見積時間から、今週の各日のタスク山積状況が分かります。 4.GoogleCalendarAPIを用いてタスク情報からGoogleCalendarに予定を追加できます。 環境・使用技術 macOS BigSur Java (AdoptOpenJDK 11.0.10) SpringBoot 2.5.2 MySQL 14.14 MyBatis 2.2.0 Thymeleaf 2.5.4 SpringSecurity(Spring Boot Starter Security » 2.5.4) Google OAuth 2.0 GoogleCalendar API HTML / CSS / MDBootstrap JavaScript(画面の一部のみ) AWS(VPC/EC2/RDS/Route53/ALB/ACM) 画面イメージ・使い方 ログインページ Topページ タスク追加デモ (タスクの予定日が昨日以前の場合は背景が薄赤色になります。) (当日は赤字、翌日は太字になります。) タスクは「予定日順」と「優先度順」で並び替えができます。 Googleカレンダーに予定としてタスクを追加できます。 タスク追加のコツ ・見積時間や予定日は仮でもOKなので入れてください。 ・「週間タスク状況」欄には今週のタスク山積状況が表示されます。 ・こちらを見ながら予定日を決めていってください。 ・ほとんど予定日が確定したら、Googleカレンダーに予定として追加してください。 タスクが完了したら緑色の「✓」ボタンを押してください。 (Doneページにタスクが移動します。) Todayページ Doneページ 最も苦労したポイント Googleカレンダーに予定を追加する機能を実装する際、 「Java×SpringBootでログイン機能中のユーザーのカレンダーに予定を追加する」方法については公式ドキュメントに記載がなく、また解説している記事等も無かったため、 GoogleAPIの公式Javadocから必要な情報を読み解いた点が最も苦労しました。 注意点 動作確認はGoogleChromeのみで行っています。 連動できるGoogleカレンダーは、タイムゾーンAsia/Tokyoのプライマリカレンダーのみです。 課題・改善点 タスクとGoogleカレンダーのイベントは、追加時以外リンクしていない状況なので、編集/削除時にも連動するようにしたい。 FullCalendar(JavaScriptのライブラリ)などを利用して、Googleカレンダーの表示を綺麗にしたい。 以上です! お読みいただきありがとうございましたm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む