- 投稿日:2021-04-29T22:33:15+09:00
Logic AppsとAzure Functions(Java)を連携する
Azureのノーコードツール「Logic Apps」を触っていて、Azure FunctionsやAzure App Servicesなどと連携できることを知りました...(今更)!これは便利そう。Azure FunctionsはJavaでも書けるので、基本はノーコードで組み立てて、難しい処理だけJavaで書くみたいなことが簡単にできそうです。ということで早速、連携を試してみました。 あるツイートを検知したら、それに対する情報をつぶやくというものを(かなり雑に)作りました。 まずはLogic AppsのデザイナーでTwitterのコネクタを作って、Azure Functionsを呼び出します。 デプロイしたFunctionが見えるのでポチポチと選んでいくだけです。 最後にFunctionsの結果をツイートします。本文をそのまま出すだけです。 ちなみに今回のFunctionsの処理はこれだけです。 @FunctionName("HttpExample") public HttpResponseMessage run( @HttpTrigger( name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request, final ExecutionContext context) { return request.createResponseBuilder(HttpStatus.OK).body("桃ちゃんのブログはこちらです https://blog.nogizaka46.com/momoko.oozono/").build(); } 実際にツイートしてみました。 しばらくするとFunctionsの応答内容がちゃんとツイートされました。 今回の例はFunctionsの処理がシンプルでしたが、Logic Appsで実現しにくい処理やJavaでゴリゴリ書きたい処理などを書いて連携すればノーコードベースでも複雑な処理を実現できそうです。
- 投稿日:2021-04-29T21:53:51+09:00
Junit備忘録【サービス・ロジック層】
現場で何回も調べてしまうので備忘録して内容をまとめる Mock 返り値にdummyの変数をセット import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @Autowired DemoService demoService; @Mock DemoLogic demoLogic; @Test void demoTest() { when(demoLogic.DemoLongLogic()).thenReturn(1L); // 引数ある場合はany()で when(demoLogic.DemoLongLogic(any())).thenReturn(1L); } Mock 返り値なし voidの場合 doNothing().when(demoLogic).DemoVoidLogic(); Mock 返り値が配列 Users users1 = new Users(); Users users2 = new Users(); //配列が一つの場合 when(demoLogic.DemoListLogic(any())).thenReturn(Collections.singletonList(users1)); //配列が複数の場合 when(demoLogic.DemoListLogic(any())).thenReturn(Arrays.asList(users1, users2)); Mock CompletableFuture 非同期処理メソッド 基本的には他のMockと変わらないが返却値にCompletableFuture.completedFutureが必要 when(demoLogic.DemoLongLogicAsync()).thenReturn(CompletableFuture.completedFuture(1L)); Mock 強制的にException エラー発生させる when(demoLogic.DemoLongLogic()).thenThrow(new RuntimeException()); 強制的にException エラーメッセージの確認 ターゲットメソッドで使用 Exception exception = assertThrows(Exception.class, () -> demoService.DemoLongService()); assertEquals("エラーが発生しました", exception.getMessage()); テスト中対象のメソッドが何回呼ばれたか監視する verify(demoLogic, times(1)).DemoVoidLogic(); any()についての注意 NullpointerException ~~~argument matchers: any() なんでもかんでもany()にすれば良いというわけではなく 引数がLongならanyLong() StringならanyString()と設定する必要がある when(demoLogic.DemoLongLogic(any())).thenReturn(1L); //ではなく when(demoLogic.DemoLongLogic(anyLong())).thenReturn(1L); @MockBeanの使いどころ 基本的には@Mockで問題ない インタフェースでBeanなどを読み込んでいる部分などには@MockBeanでモック化が可能になる @MockBeanを使いすぎると重くなのでなるべく量は減らすように public class DemoBean implements InitializingBean { // 省略 } まだまだ量が多くなりそうなのでここまで。。。(更新予定) 次回は ・Daoのテスト ・APIのテストJsonでスタブをひっかけるテストを書いていこうかな
- 投稿日:2021-04-29T18:33:50+09:00
オブジェクト指向について学習(アウトプット)
オブジェクト指向について学べる記事を紹介して頂いたので、自身のアプトプット用にまとめました。 こちらの記事を参考に勉強したいと思います! オブジェクト指向の3大要素 1. 継承 クラス同士の関係性を「AはBである」と表現することができる。 例)トラックは車である。 継承は親クラスの要素を受け継ぐという意味と解説されることが多いが、継承の本質は「交換可能なパーツを作成するために共通点を「規格」としてまとめることができるインターフェース」である。 例)車の部品(エンジン、ハンドル、タイヤ等)はどの車でも使うので、共通部品としておく。 Engine engine = new JetEngine(); Handle handle = new QuickHandle(); Brake brake = new AntilockBrake(); Wheel wheel = new StudlessWheel(); Car car = new Car(engine, handle, brake, wheel); そうすることで部品は何度も1から作る手間がなくなり、かつハンドルを変えたりすることが容易になりプログラムに柔軟性が生まれる。 2. ポリモーフィズム(多様性) 抽象クラスやインターフェースなどを利用して、メソッドの呼び出し方法を共通化し、「オーバーライド」させることによって、インスタンスごとに違う挙動をさせる。 例)犬でも猫でも動物として扱い、鳴くという処理を犬「わんわん」猫「にゃー」とさせる。 //動物の鳴くの処理はこれだけで済む //なんの動物か書いてないので、抽象的である。※1 animal.bark(); //犬クラス public void bark() { System.out.println("いぬ:ワンワンワン"); } ポリモーフィズムは「インターフェース(抽象・規格)に対してプログラムする」(※1) 3. カプセル化 情報を隠蔽することによって、不整合を起こすような操作ができないようにする仕組み。 フィールドをprivateにすると外部から操作できないので、使いたいときはgetterとsetterにしていたやつですね。 カプセル化とは抽象化のことであり、無駄を省き、外から見ても複雑でない状態を作ること。 実現させるためにはクラスの役割は1つにし、正しい名前を付ける必要がある。 よくわからなかったのでこちらの記事を参考にしました。 カプセル化はどうすればよい感じに実現できるのか? getter,setter使いまくってたら意味ないよね? 例えば満腹度が上下するプログラムの時、満腹度が直接いじれてしまうとまずいですよね。 急に満腹になったりお腹が空いたりしてしまいます。 外部から隠蔽するためにカプセル化(private)しておきます。 満腹度を直接いじるのではなく、走ったり食べたりするメソッドにより満腹度が上下するようにします。 これならいきなり満腹度をいじることはできなくなります。 public class Human{ private static int count_Human = 0; private String name; private int birthday; private int manpukudo; // getter,setter省略 public int getManpukudo(){ return this.manpukudo; } public void eat(){ this.manpukudo += 60; } public void walk(){ this.manpukudo -= 10; } public void run(){ this.manpukudo -= 20; } } オブジェクト指向の目的 オブジェクト指向の目的は「変更に対して柔軟に対応する」ためです。 プログラムには変更がされない箇所と、頻繁に変更されるであろう箇所が出てきます。 オブジェクト指向は頻繁に変更される箇所をクラスに抽出するプログラミングスタイルです。 例)ハンバーガーファストフード店で新商品が発売される前提でシステム開発をすると、新しいポテトが発売しても柔軟に対応できます。 しかし、PCも販売するかも!と設計するのは過剰実装です。 頻繁に変更されるであろう「食品の新商品」は加味して設計する必要がありますが、自動車製品の販売をすることは変更の可能性が低いです。 変更の可能性が低い箇所を土台に、高い箇所に気を配って設計する必要があります。 感想 オブジェクト指向は、頻繁に変更される事項を共通点としてをまとめておいて、使いやすい、分かりやすいを実現させようということでしょうか。 まだ理解はできていないですが、実務でなるほどと思えると嬉しいです。
- 投稿日:2021-04-29T17:44:19+09:00
明細1行に複数のレコードを取得している場合のページ切り替えを考える
はじめまして。主にエンハンスで活動しております。 今回はフレームワークにMyBaisを用いたWebページの話です。 既存の画面にも明細1行に複数行のレコード情報を取得しているページはあったのですが、せっかくのMyBatisですから、一度に複数のレコードを取得したいと思い、200行くらいのレコードをMyBatisの機能で1オブジェクトに変換しました。200行が1オブジェクトになるんですよ。1オブジェクトの中にListがあって、それが10サイズのリストになっていて、さらにListを含んでいる。そういうオブジェクトがMyBatisのresultMapでできちゃうんですから驚きです。 画面にも、200オブジェクトではなく1オブジェクトとして送られてくるので、1行分です。嬉しいですね。 ここで、明細エリアの説明をしたいと思います。明細エリアにはページ切り替えがついていて、1ページの表示件数は10件です。DB上では200行取得できていましても、行数としては1行分にあたりますので、ページ切り替えは発生しません。ところが、崩れました。ページ切り替えの部品が。 ページ切り替えのオブジェクトはPageDtoというものを使っていまして、中身はこんな感じです。 PageDto.java /** 件数 */ private Integer count; /** 表示している明細の開始行数 */ private Integer indexFrom; /** 表示している明細の終了行数 */ private Integer indexTo; /** 表示している明細欄の次ページ */ private Integer indexNext; /** 表示している明細欄の前ページ */ private Integer indexPrev; /** 表示している明細欄の最終ページ */ private Integer indexMax; 今回は、1行が複数レコードで構成されるので、それが原因だろうと考え、このPageDtoにステップ数を追加しようと思いました。ただし、その改修方法ですと、既存のPageDtoにも影響が出るため、試験範囲を広げなければなりません。 そこで、このPageDtoに値を設定する共通サービスを修正することにしました。それでは、その共通サービスを見てみましょう。 PageService.java public PageDto createPageDto(int pageCnt, int current, int count) { PageDto page = new PageDto; page.setCount(count); int max = Mah.ceil(count / pageCnt); page.setIndexMax(max); page.setIndexNext(current + 1 > max ? max : current + 1); page.setIndexPrev(current - 1 > 1 ? current - 1 : 1); page.setIndexTo(current * pageCnt > max ? max : current * pageCnt); return page; } 実際はこんな風に三項演算子なんかは使われていないですけど、記事を読むにはシンプルでわかりやすいと思いましたので使用しました。既存のPageDtoについて理解するのに、ここまでで2時間以上、費やしてしまった気がします。最初から、JSPの方でどのように見ればすぐわかったのに、Javaの設定方法やJUnitのケースを見たり、遠回りしてしまいました。 ということで、今回の改修案です。 PageService.java public PageDto createPageDto(int pageCnt, int current, int cnt, int stepCnt) { PageDto page = new PageDto; int count = cnt / stepCnt; page.setCount(count); int max = Mah.ceil(count / pageCnt); page.setIndexMax(max); page.setIndexNext(current + 1 > max ? max : current + 1); page.setIndexPrev(current - 1 > 1 ? current - 1 : 1); page.setIndexTo(current * pageCnt > max ? max : current * pageCnt); return page; } 引数が異なるので、既存のメソッドにも修正が入らないし、安心ですね。JUnitがあるので、既存のメソッドから↓下のように呼び出しても良いのですが、今回のお客様との距離感を考えるとやらないですね。 PageService.java /** 既存のメソッドから新規メソッドを呼び出す */ public PageDto createPageDto(int pageCnt, int current, int count) { return createPageDto(pageCnt, current, count, 1); } 以上です。最後まで、ご覧いただきありがとうございました。
- 投稿日:2021-04-29T15:48:27+09:00
Javaの変数・データ型についてのまとめ
Javaでよく使われる変数についてまとめた個人的なメモです。 Javaでは、基本的に 型 + 変数名 で変数を定義します。 文字... char 一文字だけの文字を扱う場合は、charを使います。 MyApp.java char a = 'a'; char b = '花'; charを用いる時は変数の値をシングルクオーテーション '' で囲わなければならない点に注意が必要です。 文字列... String MyApp.java String message = "Hello World!"; String変数に代入する値はダブルクオーテーション "" で囲います。 また、特殊文字として\nの改行や、\tでタブを使う事が可能です。 MyApp.java String message = "Hello\nWorld\t!"; バックスラッシュ\は、Macの場合option + ¥を押すことで入力できます。 整数... byte / short / int / long データ型 値 byte 8ビット整数 -128~127 short 16ビット整数 -32,768~32,767 int 32ビット整数 -2,147,483,648~2,147,483,647 long 64ビット整数 -9,223,372,036,854,775,808~9,223,372,036,854,775,807 通常は int 型 がよく使われます。 MyApp.java int x = 10; long y = 1234567891234L; long 型の変数に int 型の範囲を超える数値を代入する場合は末尾に「L」を記述する必要があります。 浮動小数点数... float / double データ型 値 float 32ビット単精度浮動小数点数 double 64ビット倍精度浮動小数点数 通常は double 型がよく使われます。 MyApp.java double d = 1234.567; float f = 3.14F; float 型の変数に数値を代入する場合、それぞれのデータ型の扱える範囲の数値であってもそのまま記述するとエラーとなる為、 float 型の値に浮動小数点数を記述する場合は末尾に「F」を付ける必要があります。 論理値... boolean true もしくは false の値を格納する際に使用します。 MyApp.java boolean flag = true; 型を変更する(キャストする) double 型の値をint 型にする場合 MyApp.java public class MyApp { public static void main(String[] args) { double d = 5242.88; int x = (int)d; System.out.println(x); } } % javac MyApp.java % java MyApp 5242 int 型の値をdouble 型にする場合 MyApp.java public class MyApp { public static void main(String[] args) { /* 以下の方法だと整数の状態で計算されてしまうため、結果 2.5 ではなく 2 が返ってきてしまうため注意。 int i = 10; double y = i / 4; System.out.println(y); */ // 正しく処理するためには以下のように記述する int i = 10; double y = (double)i / 4; System.out.println(y); } } % javac MyApp.java % java MyApp 2.5 以上になります。 【参考サイト】 - Let'sプログラミング -基本のデータ型
- 投稿日:2021-04-29T14:23:12+09:00
【Java アルゴリズム修行⑬】シェルソート
シェルソートとは 前回まででバブル、単純選択、挿入ソートと基礎的なソートを学習しましたが、 O(nの二乗)という速度のため、あまり速いソートとは言えないそうです。 今回からは、速さが改善されたシェルソート、クイックソート、マージソート、ヒープソートたちを学んでいこうと思います! シェルソートというと、 単純挿入ソートの長所を活かしつつ、その短所を補うことで、高速にソートを行うアルゴリズム とされており、単純挿入ソートをグレードアップした形のものらしいです。 単純挿入ソートというと、先頭+1の要素を一時変数とし保存し、それより小さい値を見つける位置まで代入を繰り返し、 適切な位置に値を挿入するというものでした。 例えば、{1,2,3,4,5,0,6}という数列があるとすると以下のようになります。 ※実際の挿入ソートコードはこちらを確認してください! ■ i = 1から始まる tmp変数には a[1]の2が代入される もし j > 0 かつ、a[1-1] > 2 だったら代入操作を行うが、 この時点で既に a[0] の1よりも 2の方が大きいので代入操作は行わずにfor文を抜ける a[1] = 2でそのまま変わらない → {1,2,3,4,5,0,6} ■ i =2 なので tmp変数にはa[2]の3が代入される もし j > 0 かつ a[2-1] > 3 だったら代入操作を行うが、 この時点で既にa[1]の2の方が小さいので、代入操作は行わずにfor文を抜ける a[2] =3でそのまま変わらない → {1,2,3,4,5,0,6} ■ i = 3 なので tmp変数にはa[3]の4が代入される もし j > 0 かつ a[3-1] > 4 だったら代入操作を行うが、 この時点で既にa[2]の3の方が小さいので、代入操作は行わずにfor文を抜ける a[3] = 4なので変わらない → {1,2,3,4,5,0,6} ■ i = 4 なので tmp変数にはa[4]の5が代入される もし j > 0 かつ a[4-1] > 5 だったら代入操作を行うが、 この時点で既にa[3]の4の方が小さいので、代入操作は行わずにfor文を抜ける a[4] = 5なので変わらない →{1,2,3,4,5,0,6} ■ i = 5 なので tmp変数にはa[5]の0が代入される もし j > 0 かつ a[5-1] > 0 だったら代入操作を行うので、 a[4] の5の方が大きいため、代入操作を行い、 a[5] = a[4]の5が代入され、 {1,2,3,4,5,5,6} もし j > 0 かつ a[4-1] > 0 だったら代入操作を行うので a[3] の4の方が大きいため、代入操作を行い、 a[4] に a[3]の4を代入して {1,2,3,4,4,5,6} もし j > 0 かつ a[3-1] > 0 だったら代入操作を行うので a[2] の3の方が大きいため、代入操作を行い、 a[3] に a[2]の3を代入して {1,2,3,3,4,5,6} もし j > 0 かつ a[2-1] > 0 だったら代入操作を行うが、 a[1] の2の方が大きいため、代入操作を行い、 a[2]にa[1]の2を代入して {1,2,2,3,4,5,6} もし j > 0 かつ a[1-1] > 0 だったら代入操作を行うが、 a[0] の1の方が大きいため、代入操作を行い、 a[1]にa[0]の1を代入して {1,1,2,3,4,5,6} この時点で j > 0 はfalseなので for文を抜けて a[0]に 0が代入され、{0,1,2,3,4,5,6}として0が先頭に持ってこられる という流れになります。 とは言え、0を前に持ってくるだけのソートに6回もの代入はあまり効率がいいとは言えません。 ここから、単純挿入ソートが以下のような特徴を持っていることが分かります。 ① ソート済あるいは、それに近い状態(i = 1 ~4まで)では高速で処理できる ② 挿入先が遠く離れている場合は、一つずつ比較、代入を行うので回数が増える ①は長所・②は短所として、この①を活かしつつ、②を補うのがシェルソート 君ということになるようです。。 最初に等間隔の要素をグループ化して、グループごとにソートを行い そのグループを縮小していきながらソートを繰り返すことで、移動回数を減らそうというアイデアみたいですね! 試しに、{4,3,5,7,1,8,6,2}という数列で図にしながら考えてみましょう。 まずは4間隔で取り出した4つのグループに分けると、 {4,1}{3,8}{5,6}{7,2}となります その後、各グループをそれぞれソートすると、{1,4}{3,8}{5,6}{2,7}という順番になり、 数列に戻すと {1,3,5,2,4,8,6,7}として、 この時点でソートは完了していないものの、ソート済の状態に近付いていくのが分かります。 次に、2個間隔で取り出した{1,5,4,6}{3,2,8,7}の2つのグループに分けます。 そのまま各グループをソートすると、{1,4,5,6}{2,3,7,8}とソートされるので 結果的には{1,2,4,3,5,7,6,8}という並びになります。 得られた配列はさらにソート済の状態に近づいたので、最後に1個感覚で取り出した並び、 つまり配列全体となるので、そのままソートしてみましょう。 無事に{1,2,3,4,5,6,7,8}という並びになり、シェルソートが完了 しました。 {4,3,5,7,1,8,6,2}という数列から、 2個の要素に対して、4-ソートを行う× 4グループ →4回 {4,1}{3,8}{5,6}{7,2} 4個の要素に対して、2-ソートを行う× 2グループ →2回 {1,5,4,6}{3,2,8,7} 1個の要素に対して、1-ソートを行う × 1グループ →1回 {1,2,4,3,5,7,6,8} こうして整理してみると、計7回のソートで完了させることができます。 単純挿入ソートを頭からいきなり適用せずに、 4-ソートや、2-ソートによる地ならしを行い 、ソート済に近い状態にしておき、 それから単純挿入ソートを最後に行うことで、ソートを完了させる ソートの回数は増えても、 全体としての要素の移動回数は少なくなることが期待できそうです。 ここまで流れが分かってきたので、早速コードに落とし込んでみましょう。 コードで再現してみる ShellSort.java static void shellSort(int[] a, int n) { //要素数/2でグループ分けする等間隔を求める for (int h = n / 2; h > 0; h /= 2) { for (int i = h; i < n; i++) { int j; int tmp = a[i]; //等間隔を持った値同士の大小を比較し、先頭寄の要素より小さければ交換を行う for (j = i - h; j >= 0 && a[j] > tmp; j -= h) { a[j + h] = a[j]; } a[j + h] = tmp; } } } (引数のaはソートしたい配列、nは要素数です) 単純挿入ソートを行う部分は同じですが、 着目要素と比較要素が、隣接した要素ではなく、h個だけ離れた要素に変わっています 。 そのhの初期値は h/2として求めており、 for文による繰り返しを行う度に2で割っているので、 4→2→1と半分の値になっていきます。 for (j = i - h; j >= 0 && a[j] > tmp; j -= h)部分で 間隔に応じた比較を行なっていますが、 i - h の初期値は常に0となるので、先頭要素から比較を行います。 j >= 0 && a[j] > tmpの条件式の初期段階では、 tmpの値はa[i(h)]となるため、先頭から、h個分離れた要素の値を比較しています。 もし先頭要素の方が大きければ、h個離れた部分に、先頭要素を代入し、(a[j + h] = a[j]) その後の更新文でj -= hとして、jが-1になってしまった場合は 先頭を超えているので、次の要素へと比較対象を移していくいくと言うことになります。 {8,1,4,2,7,6,3,5}という 8要素で考えてみましょう まずは要素数の半分である 4間隔、2要素ずつに分けて繰り返し処理を行います ■ i = 4(h)なので、 tmpには a[4]である7が代入される そこから、j = 4-4で0スタート jが0以上かつ a[0]が7よりも大きければ交換を行うが a[0]の8は大きいので、a[4]にa[0]の8を代入→ {8,1,4,2,8,6,3,5} j - h は -4となるので、繰り返しは終了し、a[-4 + 4]に 7が代入→ {7,1,4,2,8,6,3,5}となる ■ i = 5なので、 tmpにはa[5]の6が代入される そこから j = 5-4で1スタート、 jが0以上かつa[1]が6よりも大きければ交換を行うが、 a[1]の1は6よりも小さいので、繰り返し処理は施さず a[1+4]にそのまま6が代入→ {7,1,4,2,8,6,3,5}となる ■ i = 6なので、 tmpにはa[6]の3が代入される そこから j = 6-4で2スタート、 jが0以上かつa[2]が3よりも大きければ交換を行うが、 a[2]の4の方が大きいので、a[6]にa[2]の4を代入 → {7,1,4,2,8,6,4,5} j - h は-2となるので、繰り返しは終了し [-2 + 4]に3を代入し {7,1,3,2,8,6,4,5}となる ■ i = 7なので、 tmpにはa[7]の5が代入される そこから j = 7-4で3スタート jが0以上かつa[3]が5よりも大きければ交換を行うが a[3]の2は小さいので、a[4+3]にそのまま5を代入して、{7,1,3,2,8,6,4,5}となる ここでiは8となり i < 8 はfalseなので、次のループに入る。 4 / 2 で hは2となる 2間隔でソートを行う ■ i = 2(h)なので、 tmpには a[2]である3が代入される そこから、j = 2-2で0スタート jが0以上かつ a[0]が3よりも大きければ交換を行うが a[0]の7は大きいので、a[2]にa[0]の7を代入 →{7,1,7,2,8,6,4,5}となる j - h は -2となるので、繰り返しは終了し、a[-2 + 2]に 3が代入→{3,1,7,2,8,6,4,5}となる ■ i = 3なので、 tmpには a[3]である2が代入される そこから、j = 3-2で1スタート jが0以上かつ a[1]が2よりも大きければ交換を行うが a[1]の1は小さいので、そのままa[3]に2が代入されて終了 →{3,1,7,2,8,6,4,5}となる ■ i = 4なので、 tmpには a[4]である8が代入される そこから、j = 4-2で2スタート jが0以上かつ a[2]が8よりも大きければ交換を行うが a[2]の7は小さいので、そのままa[4]に8が代入されて終了→{3,1,7,2,8,6,4,5}となる ■ i = 5なので、 tmpには a[5]である6が代入される そこから、j = 5-2で3スタート jが0以上かつ a[3]が6よりも大きければ交換を行うが a[3]の2は小さいので、そのままa[5]に6が代入されて終了→{3,1,7,2,8,6,4,5}となる ■ i = 6なので、 tmpには a[6]である4が代入される そこから、j = 6-2で4スタート jが0以上かつ a[4]が4よりも大きければ交換を行うが a[4]の8の方が大きいので、a[6]に8を代入する→{3,1,7,2,8,6,8,5}となる j - h は 2なので、そのまま a[2] > tmpを確かめると、 a[2]の7の方が大きいので、a[4]に7を代入する→{3,1,7,2,7,6,8,5}となる j-h は 0なので繰り返し処理を抜けて a[2]に4を代入する→ {3,1,4,2,7,6,8,5}となる ■ i = 7なので、 tmpには a[7]である5が代入される そこから、j = 7-2で5スタート jが0以上かつ a[5]が5よりも大きければ交換を行うが a[5]の6の方が大きいので、a[7]に6を代入する→ {3,1,4,2,7,6,8,6}となる j - h は 3なので、そのまま a[3] > tmpを確かめると a[3]の2は5よりも小さいので、繰り返し処理を抜けて、a[5]に5を代入する→{3,1,4,2,7,5,8,6}となる iはインクリメントされて8となり i < 8 はfalseなので、次のループに入利、 2/2で h は1となるので、ここからは1個ずつ挿入ソートを行うという流れですね。 最初は4個離れた値同士をソート(8要素あれば2要素ずつ) 次に2個離れた値同士をソート(8要素あれば4要素ずつ) 最後は1個離れ値どうしなので、8要素をソート ということで 要素を2で割った数だけ間隔を持った値同士だけでソートを繰り返し、ソート済の状態に近づけた上で、 最後に1要素ずつをソートすることによって、ソート回数を減らしています。 h/2で繰り返すなら、要素数が奇数だったらどうすんのよ!?って 一瞬自分でも思ったんですが、間隔ごとに分けた要素数が変わるだけで、やっていることは同じです。 試しに要素数5の{5,4,3,2,1}で考えてみましょう。 まずは 5 / 2 で2となるので、 ■ i = 2(h)から始めます。 tmpには a[2]である3が代入される そこから、j = 2-2で0スタート jが0以上かつ a[0]が3よりも大きければ交換を行うが a[0]の5は大きいので、a[2]にa[0]の5を代入→ {5,4,5,2,1} j - h は -2となるためこのまま終了し、 a[-2 + 2]に3が代入され、 {3,4,5,2,1}となります ■ i = 3なので tmpにはa[3]である2が代入される そこから、 j = 3-2で1スタート jが0以上かつ a[1]が2よりも大きければ交換を行うが a[1]の4は大きいので、a[3]にa[1]の4を代入→ {3,4,5,4,1} j - h は -1となるためこのまま終了し、 a[-1 + 2]に2が代入され、 {3,2,5,4,1}となります ■ i = 4なので tmpにはa[4]である1が代入される そこから、 j = 4-2で2スタート jが0以上かつ a[2]が1よりも大きければ交換を行うが a[2]の5は大きいので、a[4]にa[2]の5を代入→ {3,2,5,4,5} j - h は 0となるためそのまま続けると a[0]が1よりも大きければ交換を行うため a[0]の3は大きいのでa[2]にa[0]の3を代入→{3,2,3,4,5} j - h は -2となるため終了し、 a[-2 + 2]に1が代入され、{1,2,3,4,5}となります。 この時点でソートは完了してるため書きませんが、この次に ■ i = 1(h)も処理自体は行われます。 やっていることは変わらず2間隔ずつ、1,3,5番目と2,4番目でそれぞれ単純挿入ソートを行なっている感じですね! 学んだこと 単純挿入ソートは無駄なソートを行なっていることがあるため、遅い場合もある 要素数/2の間隔ごとの値の塊で単純挿入ソートを行うことで、 限りなくソート済に近い状態で1要素ずつソートさせることができる 間隔ごとに分けてソートしていく、というと複雑な分岐処理が必要なのでは、、と感じましたが やっていることは先頭要素から、間隔の数分だけ離れた要素のみで単純挿入ソートを繰り返す というシンプルなことなので、3重ループであっても、何のためのfor文でそれぞれ どういった場合に繰り返し処理を行うのかという条件文の理解が大切なことに気付けました! 次回はクイックソート。。ということで引き続き頑張っていきます!
- 投稿日:2021-04-29T12:07:00+09:00
MacでJavaの環境構築(VSCode)
Javaの学習を開始するため、普段利用しているmacのVSCodeでJavaの環境を構築した個人的な後書きメモになります。 作成日:2021年4月29日 macOS:macOS BigSur 11.2.3 JDK:AdoptOpenJDK 11.0.7 (HotSpot) JDKをインストール まずは開発に必要なJDK(今回は AdoptOpenJDK を導入)を手順に従ってインストールしていきます。 AdoptOpenJDKのサイトにアクセスし、OpenJDK11・HotSpot を選択して Latest Release よりダウンロードします。 ダウンロードが完了したら.pkgファイルを実行し、手順に従ってインストールします。 インストールが無事完了したらターミナルを起動し、環境変数 JAVA_HOME を設定します。 % cd ~ % echo JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home >> .zshrc % source .zshrc % echo $JAVA_HOME /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home 続いてターミナルで PATH を設定します。 % cd ~ % echo PATH=$PATH:$JAVA_HOME/bin >> .zshrc % source .zshrc % echo $PATH ... /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home/bin 以上でJDKの設定は完了です。最後にバージョンを確認してみます。 % java --version openjdk 11.0.11 2021-04-20 OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9) OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode) % javac --version javac 11.0.11 VSCodeの設定 VSCodeを起動し、command + shift + x で拡張機能一覧を表示させます。 検索フォームでJava Extension Packを検索し、インストールします。 インストールが完了したら、command + ,(カンマ)で設定画面を開きます。 検索フォームでjava.homeと入力し、検索結果からEdit in settings.jsonの表示をクリックし、jsonファイルで上記で設定した Java_Home を記述してJDKにパスを通します。 { "java.home": "/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home", } 以上で設定は完了です。 VSCodeを再起動して試しに Hello World! を出力してみます。 任意のフォルダでHelloWorld.javaを作成してコードを記述します。 HelloWorld.java public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } } ターミナルでコンパイルし実行します。 % cd 任意のフォルダ % ls HelloWorld.java % javac HelloWorld.java % ls HelloWorld.class HelloWorld.java % java HelloWorld HelloWorld! 以上でJavaのVSCodeでの開発環境構築は終了です。 まだ学習を開始して1日目なので、至らない点があると思いますが、これから学習を深め修正点を見つけ次第適宜修正します。 参考にさせていただいた記事・サイト↓: - MacにJava開発環境をインストールする - VSCodeでJavaの開発環境構築する方法 - VSCodeでJavaの開発環境を構築する
- 投稿日:2021-04-29T10:47:08+09:00
スラスラわかるJava
やって見て ・総称型 ・Streamは今後の課題 コードが動かしにくかったです。 理由・背景 Javaをマスターするため。 特にJava8のやり方を身に付けたいと思います。 テキスト スラスラわかるJava 第2版 中垣 健志 https://www.amazon.co.jp/dp/B07K3YFN2J/ref=cm_sw_r_tw_dp_FS7C538H7ZVG6220CHPV やり方 重点項目を決める 13 14 18 19 20 基本サンプルコードの写経や見たりとか・・・ chap4まで 一読 問題を回答見てみる。 用語チェック TakeAway 文字列のメソッドのリンク https://java-code.jp/category/string サンプルコード chap 1 単なる例なので省略 chap6-12まで 一読 問題を回答見てみる。 用語チェック TakeAway コレクション等については以前まとめたのを参照 https://qiita.com/JoeB/items/cd1861f447da132476da#%E3%81%82%E3%82%8C%E3%81%A3%E3%81%A6%E3%81%AA%E3%82%93%E3%81%A0%E3%81%A3%E3%81%91 サンプルコード chap7 一旦いいと思います。 chap13 サンプルコード interface Stockable { String getName(); } class Bike implements Stockable { @Override public String getName() { return "二輪車です"; } } /* もの「T」を扱えるクラス(ジェネリクス利用) */ class Warehouse<T extends Stockable> { private T item; /* ものを預かる */ public void stock(T item) { this.item = item; } /* ものを取り出す */ public T leave() { T item = this.item; this.item = null; return item; } /* ものの名前を取得する */ public String getItemName() { if (item == null) { return "何もありません"; } else { return item.getName(); /* 正常にコンパイル */ } } } public class Main { public static void main(String[] args) { Warehouse<Bike> carWarehouse = new Warehouse<>(); Bike bike = new Bike(); carWarehouse.stock(bike); } } chap14〜17 コードを動かしたりした。 chap18から20 理解に努めた。 20はサンプルコードを見るようにした。
- 投稿日:2021-04-29T01:07:18+09:00
Microsoft Graph API を利用した Azure AD B2C のユーザー操作(作成編)
Microsoft Azure 上でクラウドネイティブなシステムを作る際は、ユーザー情報管理や認証処理を Azure AD B2C を利用して実現することが一般的です。 本記事では、Microsoft Graph API を利用して Azure AD B2C 上のユーザー情報を作成、更新、削除する方法を紹介します。 前提条件 Azure AD B2C テナントが作成済みであること クライアント側は Spring-Boot ベースの Java アプリケーション Microsoft Graph アプリケーションを登録する Microsoft Graph API の実行には、まず Microsoft Graph アプリケーションを Azure AD B2C テナントに登録します。 以下の公開情報を参考に実施してください。 Microsoft Graph アプリケーションを登録する pom.xml pom.xml <dependency> <groupId>com.microsoft.graph</groupId> <artifactId>microsoft-graph</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.microsoft.graph</groupId> <artifactId>microsoft-graph-auth</artifactId> <version>0.2.0</version> </dependency> Azure AD B2C ユーザーの作成 TestGraphApi.java @Test void createUser() { /** * ユーザーインスタンスの作成 */ User user = new User(); /** * パスワード属性の設定 */ PasswordProfile passwordProfile = new PasswordProfile(); passwordProfile.password = PASSWORD; passwordProfile.forceChangePasswordNextSignIn = false; //(*1) user.passwordProfile = passwordProfile; /** * ユーザー属性の設定(必須項目のみ) */ user.displayName = DISPLAY_NAME; user.accountEnabled = true; user.mailNickname = NICKNAME; user.userPrincipalName = USER_PRINCIPAL_NAME; //(*2) ObjectIdentity objectIdentity = new ObjectIdentity(); objectIdentity.signInType = "userName"; //(*3) objectIdentity.issuer = ISSUER; //(*4) objectIdentity.issuerAssignedId = ISSUER_ASSIGNED_ID; List<ObjectIdentity> identities = new ArrayList(); identities.add(objectIdentity); user.identities = identities; /** * Azure AD B2C 上にユーザーを作成 */ User response = createIGraphServiceClient( CLIENT_ID, CLIENT_SECRET, DOMAIN_NAME) .users() .buildRequest() .post(user); //(*5) } (*1) 初回サインイン時の強制パスワード変更の有無 (*2) ユーザープリンシパル名は、{任意の文字列}@{テナント名} onmicrosoft.com にする (*3) ユーザー名認証(username)、メールアドレス認証(emailAddress)、フェデレーション連携認証(federated)を選択可能 サインインタイプ (*4) {テナント名} onmicrosoft.com を指定する (*5) DOMAIN_NAME は、{テナント名} onmicrosoft.com を指定する createIGraphServiceClient private IGraphServiceClient createIGraphServiceClient( String clientId, String clientSecret, String tenantName) { List<String> scopes = new ArrayList(); scopes.add(SCOPE); ClientCredentialProvider authProvider = new ClientCredentialProvider(clientId, scopes, clientSecret, tenantName, NationalCloud.Global); IGraphServiceClient graphClient = GraphServiceClient .builder() .authenticationProvider(authProvider) .buildClient(); return graphClient; } (*6) スコープは、https://graph.microsoft.com/.default を指定する 実行結果 上記を実行すると、Azure AD B2C 上に指定した属性のユーザーが作成されていることが確認できます。 次回は、ユーザー情報の更新方法について記述します。 読んで頂きありがとうございました。 検証コード Graph API リファレンス