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

JavaのpathとPackageに関するメモ

①前提
構成ディレクトリ
- stage11
- test10
- BondTest.java
- issues
- Position.java
- Bond.java
現在地
カレントディレクトリはtest10となっている.

実行ファイルと呼び出すファイル
 実行ファイルはBondTest.java
 実行ファイルから呼び出すファイルはPosition.javaとBond.java

②package名に関して
package名はソースファイルが入っているフォルダ名1つをつけてあげる

BondTest.java
 package test10;
Position.java
 package issues;
Bond.java
 package issues;

③クラスパス
packageのルートディレクトリをつけることが肝であった.
今回はBondTest.javaはpackage test10であるからstage11を指定する必要がある.
また呼びだすファイルはpackage issuesであるから一つ上のtest10を指定する必要がある.
クラスパスは以上の2つある.

④import
import文はBondTest.javaからPosition.javaとBond.javaを呼びだす際に必要になる.BondTest.javaのpackageがtest10であるから相対パスは,issues.~となる.よって

BondTest.java
 import issues.Position;
 import issues.Bond;

⑤javacとjava
いよいよjavacでコンパイルしてjavaで実行するわけである.
javac -cp ..:. BondTest.java
が正解である.
.. は実行ファイルのpackageのルートディレクトリである.
. はPosition.javaとBond.javaのpackage issuesのルートディレクトリ.
ファイル名はBondTest.javaであるのも意外と間違えてtest10.BondTest.javaとかやりたくなるが書かない.

次にjavaコマンドで実行する.-cpはそのままであるからほぼ変わらないが,重要な点が一点あった.
javac -cp ..:. test10.BondTest.java
が正解なのである.javaコマンドでは,package名から書くことを忘れがちになるので注意が必要である.

最後にまとめると,

 ディレクトリが揃っていること
 実行ファイルのディレクトリにいること
この前提のもとで,
 package名のルートディレクトリをクラスパスに指定すること
 package名をjavacではつけずjavaではつけることになること

このあたりが要点になる.ディレクトリ構成が異なる人はうまくいかないことがあるかもしれないので検索して自分の形に合ったディレクトリを見つけることが重要である.

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

GCSにアップロードされているCSVファイルを扱う

csvファイルを読み込む

blob.getContentでファイルの中身が取得できるが、ファイルサイズが大きい場合は困ってしまう

Bucket bucket = getBucket(bucketName);
Storage.BlobListOption option = Storage.BlobListOption.prefix(directory);

Page<Blob> blobs = bucket.list(option);
Iterator<Blob> iterator = blobs.iterateAll().iterator();

List<String> csvList = new ArrayList<>();
while (iterator.hasNext()) {
    Blob blob = iterator.next();
    byte[] content = blob.getContent(Blob.BlobSourceOption.generationMatch());
    csvList.add(new String(content));
}

for (String csv : csvList) {
    // process...
}

csvファイルを1行ずつ読み込む

getContentの代わりに、以下のようにすれば1行ずつ読みこんで処理できる

ReadChannel readChannel = blob.reader();
BufferedReader br = new BufferedReader(Channels.newReader(readChannel, "UTF-8"));

String line;
while((line = br.readLine()) != null){
    // process...
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenCV+AndroidStudioで顔認識アプリ

やりたいこと

OpenCVを使った顔認識アプリの作成
・目標
 ・とりあえずAndroidStudioの導入、使い方を学ぶ(初心者)
 ・OpenCVをAndroid上で動かす
 ・カメラで撮影した画像、ギャラリーに保存されている画像のどちらも使用可能に

参考

OpenCVを使って、簡単顔認識Androidアプリを作ってみた
https://qiita.com/Kuroakira/items/094ecb236da89949d702
インテントでカメラを呼び出す方法の補足(主に、Xperia 2.1問題対応)
https://gabu.hatenablog.com/entry/20101125/1290681748

使ったもの

Android Studio 3.5.3
OpenCV 4.2.0

大まかな流れ

実装についてはほぼ上記の先人たちを参考にしているのでざっと書きます。

カメラ、ギャラリーボタンを押下

カメラ、ギャラリーから画像を取得

取得した画像をbitmap→Mat形式に変換

OpenCVへMat形式の画像を渡す

顔認識情報を取得し、画面へ表示

結果

それっぽい座標情報が出てきました。

result1.png

今後も使えそうなこと

・カメラ
 カメラで撮影した画像を高解像度で取得したい場合、いったんURLへ書き出し、URLから画像を取得する。
 getImg = (Bitmap) data.getExtras().get("data");ではサムネイル用の低解像度画像しか取得できない。
 (画像認識用の画像はgetImg = (Bitmap) data.getExtras().get("data");で取得しているが、人数が増えたり、引きの画だと不都合がでそう)

MainActivity.java
   getImg = (Bitmap) data.getExtras().get("data");
MainActivity.java
    /**略**/
    imageView.setImageURI(mImageUri);
    /**略**/

    /**
     * 写真撮影用
     * 撮影した写真をいったんURLに書き出して、書き出し先のURLをmImageUriに格納
     */
    protected void takePhoto(){
        String filename = System.currentTimeMillis() + ".jpg";

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE,filename);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        mImageUri = getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values
        );

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
        startActivityForResult(intent, RESULT_CAMERA);
    }

今後、実現したいこと

・顔認識部分だけクラス分割
 当初、クラス分割しての実装を試みたが、以下の部分がクラス分割した場合の方法がわからず断念。
 (別クラスからでは、MainActivity.javaと同様の記述ではのres/rawへアクセスができない?)

MainActivity.java
        File cascadeDir = getDir("cscade", Context.MODE_PRIVATE);
        File cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");

・ImageViewから画像取得
 ImageVIewから画像取得することでカメラ、ギャラリーの場合、それぞれの場合での実装が不要になるためコードを削減できるのではないか

・Kotlinでの実装
 とりあえず慣れてみるためだったので書きなれたJavaで実装を行ったが今後Android向け開発を行う場合はKotlinを使用したい

ソースコード

MainActivity.java
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import  java.io.InputStream;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.objdetect.CascadeClassifier;

public class MainActivity extends AppCompatActivity {
    private final static int RESULT_CAMERA = 1001; // カメラ用
    private final static int REQUEST_GALLERY = 1000; // ギャラリー用
    private Uri mImageUri; // 画像のURLを格納するインスタンス変数
    private Bitmap getImg = null; // カメラ、またはギャラリーから取得する画像
    private ImageView imageView;//イメージビューの宣言文

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.image_view);//先にImageViewをレイアウトビューのIDと紐づけ

        Button cameraButton = findViewById(R.id.camera_button);
        cameraButton.setOnClickListener(new View.OnClickListener() {//普通のインナークラスを使っての実装
            @Override
            public void onClick(View v) {
                takePhoto();
            }
        });

        Button galleyButton = findViewById(R.id.gallery_button);
        galleyButton.setOnClickListener(new View.OnClickListener() {//普通のインナークラスを使っての実装
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(intent, REQUEST_GALLERY);
            }
        });
    }

    //これからImageViewにとった写真を張り付け。
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == RESULT_CAMERA || requestCode == REQUEST_GALLERY) {
            TextView textView = findViewById(R.id.text1); // テキストビュー(表示するテキストを格納するビュー)
            String outputText = ""; // 表示するテキスト
            MatOfRect faceRects; // 顔認識データを格納する

            // カメラからの画像選択の場合
            if (requestCode == RESULT_CAMERA) {
                // cancelしたケースも含む
                if (data.getExtras() == null) {
                    Log.d("debug", "cancel ?");
                    return;
                } else {
                    imageView.setImageURI(mImageUri);
                    getImg = (Bitmap) data.getExtras().get("data");
                }
            }
            // ギャラリーからの画像選択の場合
            if (requestCode == REQUEST_GALLERY && resultCode == RESULT_OK) {
                try {
                    InputStream in = getContentResolver().openInputStream(data.getData());
                    getImg = BitmapFactory.decodeStream(in);
                    in.close();
                    // 選択した画像を表示する
                    imageView.setImageBitmap(getImg);
                } catch (Exception e) {
                    // ファイルが無かった時
                    textView.setText("ファイルが見つかりません");
                }
            }
            // imageViewに張り付けられた画像(カメラ、ギャラリーから取得)から顔認識
            try {
                faceRects = checkFaceExistence(getImg);
                outputText = makeOutputText(faceRects);\
                // 選択した画像を表示する
                imageView.setImageBitmap(getImg);
            } catch (IOException e) {
                outputText = "顔認識エラー";
                e.printStackTrace();
            }

            Toast.makeText(this,outputText,Toast.LENGTH_LONG).show();
            textView.setText(outputText);
        }
    }

    /**
     * 写真撮影用
     * 撮影した写真をいったんURLに書き出して、書き出し先のURLをmImageUriに格納
     */
    protected void takePhoto(){
        String filename = System.currentTimeMillis() + ".jpg";

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.TITLE,filename);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        mImageUri = getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values
        );

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
        startActivityForResult(intent, RESULT_CAMERA);
    }

    /**
     * 渡された画像を元に顔認識を行う
     * @param bitmap 顔認識対象の画像
     * @return text 顔認識情報
     * @throws IOException
     */
    protected MatOfRect checkFaceExistence(Bitmap bitmap) throws IOException{
        System.loadLibrary("opencv_java4");

        // 送られた画像データ(bitmap)をMat形式に変換
        Mat matImg = new Mat();
        Utils.bitmapToMat(bitmap, matImg);
        String text =  "";   // 顔認識情報を格納するtext

        // 顔認識を行うカスケード分類器インスタンスの生成(一度ファイルを書き出してファイルパスを取得)
        // 一度raw配下に格納されたxmlファイルを取得
        InputStream inStream = getResources().openRawResource(R.raw.haarcascade_frontalface_alt);
        MatOfRect faceRects = new MatOfRect(); // 顔認識データを格納する

        try {
            // 出力したxmlファイルのパスをCascadeClassfleの引数に
            CascadeClassifier faceDetetcor = outputCascadeFile(inStream);
            // カスケード分類器に画像データを与えて、顔認識
            faceDetetcor.detectMultiScale(matImg, faceRects);
        }
        catch (IOException e){
            Toast.makeText(this,"解析情報ファイルオープンに失敗しました。",Toast.LENGTH_SHORT).show();
        }

        return faceRects;
    }

    /**
     * あらかじめ用意されたopenCV分類器を一度取り込んで、書き出し使用可能にする。
     * @param inStream 分類器の元データ
     * @return faceDetector 分類器データ
     * @throws IOException
     */
    protected CascadeClassifier outputCascadeFile(InputStream inStream) throws IOException {
        File cascadeDir = getDir("cscade", Context.MODE_PRIVATE);
        File cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");

        // 取得したxmlファイルを特定ディレクトリに出力
        FileOutputStream outputStream = new FileOutputStream(cascadeFile);

        byte[] buf = new byte[2048];
        int rdBytes;

        while ((rdBytes = inStream.read(buf)) != -1) {
            try {
                outputStream.write(buf, 0, rdBytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 出力したxmlファイルのパスをCascadeClassfleの引数に
        CascadeClassifier faceDetetcor = new CascadeClassifier((cascadeFile.getAbsolutePath()));

        outputStream.close();

        return faceDetetcor;
    }

    /**
     * 出力用テキスト作成
     * 顔認識情報を元に座標情報をtextにする
     * @param faceRects
     * @return
     */
    protected String makeOutputText(MatOfRect faceRects){
        String text = "";

        // 顔認識結果をStringでreturn
        if(faceRects.toArray().length > 0){
            for(Rect face: faceRects.toArray()) {
                text = "顔の縦幅:" + face.height + "\n" +
                        "顔の横幅" + face.width + "\n" +
                        "顔の位置(Y座標)" + face.y + "\n" +
                        "顔の位置(X座標)" + face.x;
            }
        }
        else{
            text = "顔が検出されませんでした。";
        }

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

java バブルソート

ソート

今回からソートについて投稿していこうと思います。
※自分のアウトプット用の記事です。間違いなどがあったら指摘してください。

バブルソート

BubbleSort.java
public class BubbleSort {
    public static void sort(int[] array) {
        for(int i=0;i<array.length;i++) {
            for (int j=0;j<array.length-1;j++) {
                if(array[j]>array[j+1]) {
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
    }
    public static void main(String args[]) {
        int[] array = {5,4,3,2,1};
        sort(array);
        for(int i=0;i<array.length;i++) {
            System.out.print(array[i]);
        }
    }
}

今回はバブルソートを試してみました。次回は選択ソートを投稿しようと思います。

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

mutable(可変な)オブジェクトとimmutable(不変な)オブジェクト

Java Silver SE11の学習をしていて、「Stringオブジェクトは不変なオブジェクトである」の意味がよくわからなかったので、覚書ついでに。

mutableとimmutableの違い

そもそもmutable(可変)とimmutable(不変)の違いとはなんぞやから。
簡単に言うと、一度セットした値を後から変更できるオブジェクトがmutableオブジェクト。
一度セットした値を後から変更できないオブジェクトがimmutableオブジェクト。そのままですね。

immutableオブジェクトの定義方法は下記らしいです。

  • すべてのフィールドをprivateで修飾する
  • オブジェクト内部の状態を変更可能なメソッド(setterなど)を提供しない
  • クラスをfinalで宣言し、メソッドがオーバーライドされないことを保証する(サブクラスから変更させない)
  • 内部に可変オブジェクトを保持している場合、そのオブジェクトを外部に提供(getterなど)しない

次項より、immutableなクラス:Hogeを作成することでそれぞれの詳細を確認します。
(その他のクラスも記載しますが、これらはimmutableでないものとします)

すべてのフィールドをprivateで修飾する

これはまぁ、わかりやすいですよね。publicで修飾してたら変え放題ですものね。

Test.java
public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("ダイゴウジ", "ガイ" ,18);
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }

    public Foo getFoo() { return foo; }
    public void setFoo(Foo foo) { this.foo = foo; }

    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

クラスHogeのフィールド、name、age、fooはアクセス修飾子がprivateになっています。はい。

オブジェクト内部の状態を変更可能なメソッド(setterなど)を提供しない

これもわかりやすいですね。せっかくprivateで修飾しても、メソッド経由で変更可能だったら何も意味ないです。

Test.java
public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("ダイゴウジ", "ガイ" ,18);
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }

    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

メソッド経由での変更ができなくなりました。

クラスをfinalで宣言し、メソッドがオーバーライドされないことを保証する(サブクラスから変更させない)

クラス:Hogeを継承したHogeSubを作ってみます。

Test.java
public class Test {
    public static void main(String args[]){
        HogeSub hogesub = new HogeSub("ダイゴウジ", "ガイ", 18);
        System.out.println("lastName:" + hogesub.getFoo().getLastName() + " firstName:" + hogesub.getFoo().getFirstName());
        System.out.println("-----------------");
        hogesub.setLastName("山田");
        hogesub.setFirstName("二郎");
        hogesub.dummy();
        System.out.println("lastName:" + hogesub.getFoo().getLastName() + " firstName:" + hogesub.getFoo().getFirstName());
    }
}

class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }
    public void dummy(){}
}

class HogeSub extends Hoge {
    private String lastName;
    private String firstName;

    HogeSub(String lastName, String firstName, int age) {
        super(lastName, firstName, age);
        this.lastName = lastName;
        this.firstName = firstName;
    }

    @Override
    public void dummy() {
        super.getFoo().setFirstName(this.firstName);
        super.getFoo().setLastName(this.lastName);
    }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

実行結果は次の通りです。

実行結果
lastName:ダイゴウジ firstName:ガイ
-----------------
lastName:山田 firstName:二郎

HogeSubは、Hogeが持っているdummyメソッドをオーバーライドし、fooのフィールドを変更しています。
こういった実装を回避するため、Hogeをfinalで宣言します。

Test.java
public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("ダイゴウジ", "ガイ", 18);
    }
}

final class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
    public Foo getFoo() { return foo; }
    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

オーバーライドによるフィールドの変更ができなくなりました。

内部に可変オブジェクトを保持している場合、そのオブジェクトを外部に提供(getterなど)しない

前段は、フィールド:fooのgetterを経由し、オブジェクトの値を変更していました。
Hoge.getFoo().setXXXName(”xxx”);
このような操作をされてしまうと、不変とは言えませんね。getterも消します。

Test.java
public class Test {
    public static void main(String args[]){
        Hoge hoge = new Hoge("ダイゴウジ", "ガイ", 18);
    }
}

final class Hoge{
    private String name;
    private int age;
    private Foo foo;

    Hoge(){}
    Hoge(String lastName, String firstName, int age){
        foo = new Foo(lastName, firstName);
        this.name = lastName + firstName;
        this.age = age;
    }
    public void dummy(){}
}

class Foo{
    private String lastName;
    private String firstName;
    Foo(String lastName, String firstName){ this.lastName = lastName; this.firstName = firstName; }

    public void setLastName(String lastName) { this.lastName = lastName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public String getFirstName() { return firstName; }
}

これで、immutableなクラス:Hogeが完成しました。
完成したHogeは実質何もできませんが、そこはサンプルということでお許しください…。

せやかてString型変数の内容変えられるやん

※この項は推測です。誤りがあればご指摘ください。

Test2.java
public class Test2 {
    public static void main(String args[]){
        String str = "すとりんぐ1";
        System.out.println(str);
        System.out.println("-----------------");
        str = "すとりんぐ2";
        System.out.println(str);
    }
}
実行結果
すとりんぐ1
-----------------
すとりんぐ2

確かにstrの内容は変わっています。
注意が必要なのは、変数strの値(参照先)が変わっているだけであって、インスタンスの値は変わっていないという点です。

デバッガで追ってみます。以下のようにブレークポイントを設定します。
image.png

まずは、一つ目のブレークポイント時の内容から。
image.png

次に、二つ目のブレークポイント時の内容。
image.png

strの参照先が変わっていることが確認できます。

Stringクラスは特殊で、new演算子を使わずとも、代入演算子でインスタンスの生成が可能です。
(str = "すとりんぐ2";str = new String("すとりんぐ2"); はどちらも同じ処理ということ)
つまり、String型変数に代入演算子で文字列を設定するということは、都度インスタンスを生成していることと同義です。
(厳密には、本当に都度都度生成しているとは限りませんが)

使い方によっては、JVM上のメモリをどんどん食いつぶすことになりかねません。
こういった問題を解消するために、SringBuilderクラスが作られた…のだと、推測します。

最後に

やはりといいますか、(推測交じりですが)アウトプットすることで理解が深まると感じました。
Silver/SE11の取得を目指して引き続き頑張ります。

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

Java 14新機能まとめ

Java 14もRCフェーズに入りました。
うまくいけば2020/3/17にリリースされます。
https://jdk.java.net/14/

JEP

大きめの変更はJEPでまとまっています。
https://openjdk.java.net/projects/jdk/14/

今回は、16ものJEPが取り込まれました。影響が大きいものも多いです。
305: Pattern Matching for instanceof (Preview)
343: Packaging Tool (Incubator)
345: NUMA-Aware Memory Allocation for G1
349: JFR Event Streaming
352: Non-Volatile Mapped Byte Buffers
358: Helpful NullPointerExceptions
359: Records (Preview)
361: Switch Expressions (Standard)
362: Deprecate the Solaris and SPARC Ports
363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector
364: ZGC on macOS
365: ZGC on Windows
366: Deprecate the ParallelScavenge + SerialOld GC Combination
367: Remove the Pack200 Tools and API
368: Text Blocks (Second Preview)
370: Foreign-Memory Access API (Incubator)

分野ごとにまとめていきます。

言語仕様

言語仕様にかかわる変更としては次のようなものがあります。
359: Records (Preview)
305: Pattern Matching for instanceof (Preview)
368: Text Blocks (Second Preview)
361: Switch Expressions (Standard)

359: Records (Preview)

データ保持用のクラスとしてrecordがpreview機能として入りました。
Java 15でフィードバックを反映した改善版がpreviewされてJava 16で正式化という流れになりそうです。

recordとして定義します。

record Foo(int x, int y) {}

他のクラスは継承できません。

record レコード名(状態) { 定義 }

これは次のようなクラスになります。

class Foo extends Record {
  // 状態がprivate finalとして定義
  private final int x;
  private final int y;
  // 追加のインスタンスフィールドは定義できない

  // 状態をフィールドに設定するコンストラクタが定義される
  public Foo(int x, int y) {
    this.x = x;
    this.y = y;
  }

  // 状態と同じ名前のメソッドが定義される
  public int x() {
    return x;
  }
  public int y() {
    return y;
  }

  // 状態を反映するhashCodeが定義される
  public int hashCode() { ... }
  // 状態を比較するequals が定義される
  public boolean equals() { ... }
  // 状態を表示するtoStringが定義される
  public String toString() { ... }
}

取得用メソッドは定義されますが、値設定用のメソッドは定義されません。つまり、イミュータブルなオブジェクトとなります。また、get/setではないことからJava Beanとの互換性もありません。
hashCode()メソッドやequals()メソッドなどは実際にはinvokeDynamicで実行時に実装コードが生成されます。

次のように、状態の検査や正規化にコンストラクタが定義できます。

record Range(int lo, int hi) {
  public Range {
    if (lo > hi) throw IllegalArgumentException();
    // 設定されなかったフィールドはあとで設定される
  }
}

APIの拡張

recordはjava.lang.Recordを継承したクラスになります。

次のようにRecordを継承するコードを書こうとするとrecords cannot directly extend java.lang.Recordというエラーになります。

class Foo extends Record {
}

staticではないinner classの中でrecordを定義することはできません。
次のようなコードをコンパイルするとrecord declarations not allowed in inner classesというエラーになります。

public class NestedRecord {
  class Foo {
      record Bar(int x){}
  }
}

次のようなコードはコンパイルできます。

public class NestedRecord {
  static class Foo {
      record Bar(int x){}
  }
}

Classクラスにはレコード関連のメソッドが追加されています。
isRecordメソッドで型がrecordかどうか判定できます。またgetRecordComponentsでrecordで定義されたコンポーネントを取得することができます。ただし、値の取得はRecordComponent経由では行えないので、Field経由で行うようです。

jshell> Foo.class.isRecord()
$9 ==> true

jshell> Foo.class.getRecordComponents()
$10 ==> RecordComponent[2] { int x, int y }

jshell> String.class.isRecord()
$11 ==> false

jshell> String.class.getRecordComponents()
$12 ==> null

型名としてのrecordの制限

recordという名前を型(クラス・インタフェース・レコード型など)につけることは制限されています。
--enable-previewを付けた状態ではエラーになります。

$ jshell --enable-preview
|  JShellへようこそ -- バージョン14-ea
|  概要については、次を入力してください: /help intro

jshell> class record{}
|  エラー:
|  ここでは'record'は許可されません
|    リリース13から'record'は制限された型名であり、型の宣言に使用できません
|  class record{}
|        ^

jshell> String record=""
record ==> ""

jshell> record Rec(int record){}

jshell> Rec record=new Rec(3)
record ==> Rec[record=3]

enumと違いキーワードではないので、変数名やフィールド、recordのコンポーネント名にはrecordを使えます。
--enable-previewを付けない状態では、警告が表示されます。

$ jshell
|  JShellへようこそ -- バージョン14-ea
|  概要については、次を入力してください: /help intro

jshell> class record{}
|  警告:
|  'record'は将来のリリースで制限された型名になる可能性があり、型の宣言での使用、または配列の要素タイプとしての使用はできなくなる可能性があります
|  class record{}
|        ^
|  次を作成しました: クラス record

今後の改善

当初はSealed Typesと同じJEPでしたが分離されました。
クラスの継承を限られたクラスに制限できる機能です。Java 15でpreviewに入ると思われます。
JEP 360: Sealed Types (Preview)

Preview 2での改善点はこちらで提案されています。
https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html

  • java.util.Recordはクラスですが、Valhallaのinline classはinterfaceしか継承できないので、これをinterfaceにしたほうがいいかどうか。
  • 必須メンバーへの可視性(private recordのメンバがpublicなのはおかしい?)
  • 現状ではstaticではないinner classではrecordを入れ子にすることができないけど対応したい
  • abstract record
  • パターンマッチでのdeconstructionへの対応
  • recordコンポーネントへの@Deprecated

305: Pattern Matching for instanceof (Preview)

パターンマッチングです。
まずはinstanceofを使ったパターンマッチが14にプレビューとして入ります。
http://openjdk.java.net/jeps/305

値 instanceof パターンで、値をマッチさせることができます。
パターンは、定数か変数定義です。変数定義の場合には、型が一致していた場合にtrueになりその変数に値が割り当てられます。

if (x instanceof Integer i) {
    // can use i here
}

switchで使えるようになれば便利ですが、これは別のJEPで定義されていて、Java 15に持ち越されています。
JEP draft: Pattern matching for switch (Preview)

また、recordを分解する機能(deconstruction)やその入れ子が導入されてPreview 2としてJava 15に入るようです。
http://openjdk.java.net/jeps/8235186

妥当なスケジュールとしては

  • 14 Pattern Matching for instanceof(Preview)
  • 15 Pattern Matching for instanceof(Preview 2)
    Pattern matching for a switch (Preview)
  • 16 Pattern Matching for instanceof(Standard)
    Pattern matching for a switch (Preview 2)
  • 17LTS Pattern matching for a switch(Standard)

となるか

  • 14 Pattern Matching for instanceof(Preview)
  • 15 Pattern Matching for instanceof(Preview 2)
    Pattern matching for a switch (Preview)
  • 16 Pattern Matching for instanceof(Preview 3)
    Pattern matching for a switch (Preview 2)
  • 17LTS Pattern Matching for instanceof(Standard)
    Pattern matching for a switch(Standard)

となるかですが、17LTSにはパターンマッチングの機能がひととおり標準機能として入る目途がついてきました。

368: Text Blocks (Second Preview)

改行などを含んだ文字列を定義できます。"""で囲みます。
JDK13にPreviewとして入ったものにJDK14では改行のエスケープなど少し仕様変更が入りました。JDK15でstandardになる予定。

// same as "You can write\ntwo line string.\n"
var str = """
  You can write
  two line string.
  """;

開始の"""のあとには文字列を続けれません。また、インデントは"""や内部の文字列で一番浅いところが基準になります。

var str = """
..You can write
..two line string.
  """;
var str = """
..  You can write
..  two line string.
  """;

改行をエスケープすることもできます。

var str = """
  You can write \
  two line string, \
  but this is single.
  """;

これは"You can write two line string, but this is single."になります。

行末のスペースは削除されます。
そこで、行末にスペースが必要なときは\sを入れてスペースが必要なことを示します。

var str = """
  test\s
  test \s
  """;

これは"test_\ntest__\n"になります。(Qiitaでは複数スペースをいれてもスペースひとつになってしまう)

文字列への変数の埋め込みはできません。その代わりにformattedメソッドがインスタンスメソッドとして用意されて、次のように書けるようになりました。

var str = """
  こんにちは、%sさん。
  今日はいい天気ですね。
  """.formatted("きしだ");

361: Switch Expressions (Standard)

いままでステートメントであったswitchを式として使えるようになります。Java 12でプレビューとして導入され、Java 13で仕様変更、そしてJava 14で正式機能として導入されます。
https://openjdk.java.net/jeps/361

switchはステートメントでしたが、多くのswitchで同一の変数に値を割り当てたりすべてのcaseでreturnしたりといった使いかたがされていたため、効率的に書けるように式としても使えるようになります。

つまり、こう。

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

yieldで値を返すということもできます。

int result = switch (s) {
    case "Foo":
        yield 1;
    case "Bar":
        yield 2;
    default:
        System.out.println("Neither Foo nor Bar, hmmm...");
        yield 3;
}

基本的な形はyieldで値を返すものです。

case LABEL: yield expression;

そのシンタックスシュガーとしてラムダっぽく書けるということのようです。

case LABEL -> expression;

また、caseに複数の値を指定できるようにもなります。

switch (day) {
    case MONDAY, FRIDAY, SUNDAY: 
        numLetters = 6;
        break;
    ...
};

このようなcaseの拡張は、既存のswitchステートメントでも有効です。

JVM

JVMの挙動変更としては、NullPointerExceptionでの詳細メッセージがあります。
358: Helpful NullPointerExceptions

358: Helpful NullPointerExceptions

JavaプログラマのみなさんはNullPointerExceptionが大好きだと思います。
しかし、メッセージがそっけないという悩みがありました。
Java 14では、NullPointerExceptionについて詳細なメッセージを表示できるようになりました。

例えば次のようなコードがあるとします。

public class Sample {
  static String s;
  public static void main(String... args) {
    s.length();
  }
}

Java 14でも普通に実行すると以前のバージョンと同様に特別なメッセージは表示されません。

$ java Sample.java
Exception in thread "main" java.lang.NullPointerException
        at Sample.main(mysample.java:4)

-XX:+ShowCodeDetailsInExceptionMessagesオプションをつけて実行すると次のように何に対して何を呼び出したときにエラーになったかということが表示されます。

$ java -XX:+ShowCodeDetailsInExceptionMessages Sample.java
Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "String.length()" because "Sample.s" is null
        at Sample.main(mysample.java:4)

このメッセージ構築はメッセージ取得時に行われるので、パフォーマンスへの影響はほとんどないようです。

JShellの場合は-R-XX:+ShowCodeDetailsInExceptionMessagesをつけます。

$ jshell -R-XX:+ShowCodeDetailsInExceptionMessages
|  JShellへようこそ -- バージョン14-ea
|  概要については、次を入力してください: /help intro

jshell> String[] strs = {null}
strs ==> String[1] { null }

jshell> strs[0].toUpperCase()
|  例外java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "REPL.$JShell$11.strs[0]" is null
|        at (#2:1)

API

APIに関する変更は次の3つのJEPです。
349: JFR Event Streaming
370: Foreign-Memory Access API (Incubator)
352: Non-Volatile Mapped Byte Buffers
この他にCompactNumberFormatでの複数形対応とStrictMathでの便利メソッドの追加がありました。
また、あとでツールの項で取り上げるPackagingツール関連のAPIがIncubatorとして追加されたのと、Pac200関連のAPIが削除されています。

349: JFR Event Streaming

Flight Recorderはメトリクス収集ツールで、障害発生時の解析に役立ちますが、ダンプファイルの解析が必要で、モニタリング用途には不便でした。
JFR Event Streamingでは、イベント登録ができるなど、モニタリングに使いやすい機能が追加されています。

JEPのサンプルではイベントの登録の例が載っていますが、これにGCログの出力をつけくわえてみました。

import java.io.IOException;
import java.time.Duration;
import jdk.jfr.consumer.RecordingStream;
public class JFRStreamTest {

    public static void main(String[] args) throws IOException  {

        try (var rs = new RecordingStream()) {
            rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
            rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
            rs.onEvent("jdk.CPULoad", event -> {
                System.out.println(event.getFloat("machineTotal"));
            });
            rs.onEvent("jdk.JavaMonitorEnter", event -> {
                System.out.println(event.getClass("monitorClass"));
            });
            rs.onEvent("jdk.GarbageCollection", System.out::println);
            rs.start();
        }
    }
}

-XX:StartFilghtRecordingをつけて実行すると、Flight Recorderが起動して、定期的にメトリクスが表示されます。

$ java -XX:StartFlightRecording JFRStreamTest.java
Started recording 1. No limit specified, using maxsize=250MB as default.

Use jcmd 79660 JFR.dump name=1 filename=FILEPATH to copy recording data to file.
[1.715s][warning][os] Unable to resolve PDH index: (230)
[1.716s][warning][os] Please check the registry if this performance object/counter is disabled
{
  classLoader = null  name = "jdk/jfr/internal/PlatformRecorder"
  package = {
    name = "jdk/jfr/internal"
    module = {
      name = "jdk.jfr"
      version = "14-ea"
      location = "jrt:/jdk.jfr"
      classLoader = null    }
    exported = true
  }
  modifiers = 49
}

jdk.GarbageCollection {
  startTime = 13:51:48.973
  duration = 12.5 ms
  gcId = 1
  name = "G1New"
  cause = "G1 Evacuation Pause"
  sumOfPauses = 12.5 ms
  longestPause = 12.5 ms
}

JDK Mission Control(JMC)で見ることができます。
JFRを動かしておくと、イベントブラウザにイベントが記録されているのがわかります。
image.png

370: Foreign-Memory Access API (Incubator)

ヒープ外のメモリをアクセスする方法としては、ByteBufferを使う方法やUnsafeを使う方法、JNIを使う方法がありますが、それぞれ一長一短があります。
ByteBufferでdirect bufferを使う場合、intで扱える範囲の2GBまでに制限されたり、メモリの解放がGCに依存したりします。
Unsafeの場合は、性能もいいのですが、名前が示すとおり安全ではなく、解放済みのメモリにアクセスすればJVMがクラッシュします。
JNIを使うとCコードを書く必要があり、性能もよくないです。

ということで、ヒープ外のメモリを直接扱うAPIが導入されたわけです。
次のようなコードになります。

VarHandle intHandle = MemoryHandles.varHandle(int.class);

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   MemoryAddress base = segment.baseAddress();
   for (int i = 0 ; i < 25 ; i++) {
        intHandle.set(base.offset(i * 4), i);
   }
}

352: Non-Volatile Mapped Byte Buffers

ByteBufferを不揮発メモリに対応します。
jdk.nio.mapmodeといモジュールが導入されて、同名のパッケージにExtendedMapModeクラスが用意され、新しいMapModeとして

  • READ_ONLY_SYNC
  • READ_WRITE_SYNC

が追加されます。

var fc = FileChannel.open(path);
fc.map(ExtendedMapMode.READ_WRITE_SYNC, 0, 1024);

などとする感じです。デバイスが不揮発メモリではない場合はUnsupportedOprationExceptionが投げられます。

また、BufferPoolMXBeanに永続MappedByteBufferの統計情報がmapped - 'non-volatile memory'という名前で取れるようになるようです。

CompactNumberFormatの複数形対応

JEPになってない変更のひとつ。
ドイツ語とかイタリア語の場合に100万と200万でMillionかMillionenみたいな変形があって、それに対応したということらしい。

jshell> import java.text.*

jshell> var cnf = CompactNumberFormat.getCompactNumberInstance(Locale.GERMAN, NumberFormat.Style.LONG)
cnf ==> java.text.CompactNumberFormat@5088aaba

jshell> cnf.format(1_000_000)
$3 ==> "1 Million"

jshell> cnf.format(2_000_000)
$4 ==> "2 Millionen"

UnicodeのLanguage Plural Rulesに従って独自のルールを設定することができるコンストラクタも用意されています。

StrictMathへの便利メソッド追加

StrictMathに便利メソッドが追加されています。メソッド参照が使いやすくなることを狙ったんでしょうか。

  • incrementExact(int)
  • incrementExact(long)
  • decrementExact(int)
  • decrementExact(long)
  • negateExact(int)
  • negateExact(long)

インクリメント、デクリメント、符号反転ですが、オーバーフローしたときに
エラーがでます。

jshell> StrictMath.incrementExact(3)
$5 ==> 4

jshell> StrictMath.incrementExact(Integer.MAX_VALUE)
|  例外java.lang.ArithmeticException: integer overflow
|        at Math.incrementExact (Math.java:968)
|        at StrictMath.incrementExact (StrictMath.java:849)
|        at (#4:1)

jshell> StrictMath.negateExact(Integer.MAX_VALUE)
$6 ==> -2147483647

jshell> StrictMath.negateExact(Integer.MIN_VALUE)
|  例外java.lang.ArithmeticException: integer overflow
|        at Math.negateExact (Math.java:1044)
|        at StrictMath.negateExact (StrictMath.java:909)
|        at (#7:1)

通常の演算子ではオーバーフローがおきます。

jshell> Integer.MAX_VALUE+1
$8 ==> -2147483648

jshell> Integer.MIN_VALUE
$9 ==> -2147483648

jshell> -Integer.MIN_VALUE
$10 ==> -2147483648

ツール

ツールの変更としては、jpackageの追加が大きいですね。
343: Packaging Tool (Incubator)
367: Remove the Pack200 Tools and API

343: Packaging Tool (Incubator)

Javaアプリケーションのインストーラを作るツールが入りました。
関連APIも追加されていますが、Incubatorモジュールになっています。
Windowsではlight.exeとcandle.exeが必要なのでhttps://wixtoolset.org からダウンロードしてPATHに追加する必要があります。

試しに次のアプリケーションのインストーラを作ってみます。

import javax.swing.*;
public class App {
    public static void main(String[] args) {
        var f = new JFrame("My App");
        var t = new JTextArea();
        f.add(t);
        var b = new JButton("Hello");
        b.addActionListener(al -> t.append("Hello!\n"));
        f.add("North", b);
        f.setSize(500, 400);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }
}

アプリケーションはJarファイルになっている必要があります。

$ javac App.java
$ mkdir target
$ jar cf target/app.jar App.class
$ jpackage --name myapp --input target --main-jar app.jar --main-class App

そうするとWindowsの場合はmyapp.exeという名前で45MBのインストーラが作成されます。
モジュラーJARの場合は自動的に必要なモジュールのみのJavaランタイムがインストールされるようになりますが、ここではモジュール対応しないJarファイルを作ったので、jlinkを使って最低限のJavaランタイムを作ると小さなインストーラが作れるようになります。

$ jdeps App.class
java.base
java.desktop
$ jlink --add-modules java.base,java.desktop --output jre
$ jpackage --name myapp --input target --main-jar app.jar --main-class App --runtime-image jre

そうすると27MBまでインストーラのサイズが削減されました。
インストール後のアプリケーションサイズも124MBから74MBに減ります。
Windowsの場合は--win-menuをつけてWindowsメニューにアプリが追加されるようにするほうがいいでしょう。

367: Remove the Pack200 Tools and API

Pack200はJarファイルを効率的に圧縮するツールでしたが、主なユースケースであるAppletが使われなくなり、役目を終わったということで削除されます。
java.util.jar配下の関連APIも削除されます。

GC

GCに関する変更のJEPは5つありました。
363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector
364: ZGC on macOS
365: ZGC on Windows
366: Deprecate the ParallelScavenge + SerialOld GC Combination
345: NUMA-Aware Memory Allocation for G1

363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector

Concurrent Mark & Sweep GCがソースコードから削除されます。
Java 9のJEP 291でDeprecatedになっていましたが、メンテナンスを引き継ぐような人も現れなかったので削除するということです。
そうすることで、GCのメンテナンスコストが下がり、他のGCの開発が速くなることが期待されています。

364: ZGC on macOS / 365: ZGC on Windows

いままではLinuxしかサポートしてなかったZGCがmacOSとWindowsをサポートするようになりました。
当初は、ZGCは本番のLinuxサーバーでだけ使われて、macOSやWindowsは開発用途に使うだけだという想定でしたが、IDEをZGCで動かしたいなどの要望もあったことからmacOSやWindowsもサポートすることになったようです。
WindowsはWindows 10 ver 1803以降に対応しています。
ZGCは仮想アドレスを使って物理メモリを複数のアドレスでアクセスする仕組みを使っていますが、Windowsでは1803からページングファイルメモリがサポートされたということでWindowsでもZGCが動かせるようになったようです。

366: Deprecate the ParallelScavenge + SerialOld GC Combination

Young領域のGCをPrallelでOld領域のGCをSerialでという、ほとんど使われてない割にメンテナンスが大変な組み合わせが非推奨になりました。

345: NUMA-Aware Memory Allocation for G1

最近は各コアからのメモリアクセスが均等というわけではないNUMA(Non-Uniform Memory Access)アーキテクチャが広まっています。
パラレルGCではNUMAに対応していましたが、G1は対応していませんでした。
このJEPによってG1もNUMAに対応しています。ただし対応OSはLinuxのみです。

JDK

JDK自体に関する変更というのは、リリース形態やビルド方針などに関するものです。
Java 14ではSolarisやSPARCへの対応がDeprecatedになりました。
362: Deprecate the Solaris and SPARC Ports

362: Deprecate the Solaris and SPARC Ports

Solaris/SPARCやSolaris/x64、Linux/SPARCへの対応を外していくというものです。
CMSの削除と同様に、もし、いっぱい需要があるということを示せれば、取り下げられます。
また、これはオープンソースとしてのOpenJDKの開発の話で、Oracle JDKなど商用ディストリビューションでの対応継続は、それぞれに続く可能性があります。Solaris/SPARCを導入してるところだとJavaのライセンス代もそれほど問題にならなそうだし。

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

Java Integerの比較(==)が正しく動作しない

現象

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); // 「2 10000000 10000000」が入ってくる
        int n = sc.nextInt();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < n; i++) list.add(sc.nextInt());

        for (int i = 0; i < n-1; i++) {
            if (list.get(i) == list.get(i+1)) {
                System.out.println("等しい");
                return;
            }
        }
        System.out.println("等しくない");
    }
}

list.get(i)には10000000、list.get(i+1)にもおなじ10000000のはずなのに結果が「等しくない」が出力されてしまいました。なぜ??

プリミティブ型は==、オブジェクト型はequals

Integerはオブジェクト型のため、==で比較する場合、同一のインスタンスかどうかを確認することになるため、「等しくない」になってしまうようです。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); // 「2 10000000 10000000」が入ってくる
        int n = sc.nextInt();
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < n; i++) list.add(sc.nextInt());

        for (int i = 0; i < n-1; i++) {
            if (list.get(i).equals(list.get(i+1))) {
                System.out.println("等しい");
                return;
            }
        }
        System.out.println("等しくない");
    }
}

// 結果
// 等しい

結論、数を比較する際は、プリミティブ型は==、オブジェクト型はequalsを使いように注意しないとでしたー。

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

<p>manda syakira</p>

mandasyakira

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

<p>fejtjdtdejtejzdd</>

hdikdbkj

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

配列 vs ArrayList vs HashMapの拡張for文レース

訂正

比較演算子間違ってました(修正済み)

つかったもの

・Java9
・eclipce

動機

何が最適かは置いといて拡張for文で回したらどれが一番早いのかなと。

結果

ForEaerVs.java
public class ForEaerVs {
    public static void main(String[] argS) {

        Integer mm;

        // 配列
        Integer[] arry = new Integer[1000000000];

        for (Integer i = 0 ; i < 1000000000; i++) {
            Integer arryInt = i;
            arry[i] = arryInt;
        }
        long staetTimeArry = System.nanoTime();
            for(Integer iA : arry) {
                mm = iA;
            }
        long endTimeArry = System.nanoTime();
        System.out.println("配列速度:" + (endTimeArry -staetTimeArry));

        // List
        List<Integer> list = new ArrayList<Integer>();
        for (Integer j = 0 ; j < 1000000000; j++) {
            Integer arryInt = j;
            list.add(arryInt);
        }
        long staetTimeList = System.nanoTime();
            for (Integer iL: list) {
                mm = iL;
            }
        long endTimeList = System.nanoTime();
        System.out.println("List速度:" + (endTimeList -staetTimeList));


        // HushMap
        HashMap<Integer,Integer> hash = new HashMap<Integer,Integer>();
        for (Integer k = 0 ; k < 1000000000; k++) {
            Integer arryInt = k;
            hash.put(k, arryInt);
        }
        long staetTimeHash = System.nanoTime();
            for (Integer iH : hash.values()) {
                mm = iH;
            }
        long endTimeHash = System.nanoTime();
        System.out.println("Hash速度:" + (endTimeHash -staetTimeHash));

    }
}
ForEaerVsResult.java
配列速度  4020600
List速度  11350400
Hash速度  18974400

作成時間もいれた結果は以下

ForEaerVsPlusCreateResult.java
配列速度 35613700
List速度 98473300
Hash速度 121742700

感想

それぞれ使いどころは違うとはいえ、配列ってこんなに遅いのか。
配列先輩の凄さを崇めよ

追加

LinkedListでもやってみた

ForEaerVsResult.java
Array速度 4001200
List速度  9521900
Hash速度  19611200
Linked速度:15211300

作成時間もいれた結果は以下

ForEaerVsPlusCreateResult.java
Array速度 32542100
List速度  80343600
Hash速度  101256200
Linked速度:140576800
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【WireMock】Javaでスタブ・モックサーバを立ててE2Eテストを行いたい

連携先システムが検証系では使えないけどE2Eテストがしたい

世はマイクロサービス時代なので、HTTPリクエスト・レスポンスのモック・スタブの需要が高まっているみたいですね。
私の参加しているプロジェクトはマイクロサービスなんて関係ないのですが、
表題のとおりの目的で、Javaで動くAPIサーバを探していたところ教えていただきました。

WireMock

HTTPベースのAPIのシミュレータ、mockサーバー。
http://wiremock.org/
https://github.com/tomakehurst/wiremock

ほかのAPIServerとの比較は下記サイトで確認できます
Comparison of API simulation tools

ThoughtWorksのTECHNOLOGY RADARに2018年にTRIALで選ばれてました

https://www.thoughtworks.com/radar/tools/wiremock
ちなみにTRIALはちゃんと管理するのであればやる価値はあるという指標らしいです。

利用方法

ざっと見た感じ以下の2通りがあります

ソースコード内で利用する

MavenやGradleで導入します。こちらはJUNITと組み合わせて利用している例が多そうです
Download and Installation

StandAloneサーバとして導入する

公式サイトからJarファイルをダウンロードして起動します
Running as a Standalone Process

今回はE2Eテストのシミュレータとして動いてほしいので、StandAloneサーバとして利用します。
こちらの形式だと、できることが減りそうですかね?
やりたいことが実現できるか調査していきます。

参考サイト

WireMockサーバーの導入手順

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