20190727のJavaに関する記事は10件です。

ストリームって何ぞ

お仕事で新人研修ぶりでコードを書いています。
ストリームってすっごい基礎的なことだと思うんですが、
わかんなかったもんはしょうがないので調べていきます。

ストリームって日本語にしたら「流れ」です。
ストリームを説明する文章にもよく「データの流れ」なる言葉が出て来る気がします。
でも、「流れ」って言われるとピンとこない。

定義

プログラミングの分野では、データの入出力全般を扱う抽象的なオブジェクトやデータ型を意味する場合が多い。データが出入りする何らかの対象(メモリ領域やファイル、ネットワークなど)をプログラム中で扱えるように抽象化したもので、接続や切断、書き込みや読み込みなどを簡易な操作で行うことができる。

出典
e-words(http://e-words.jp/w/%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0.html)

「データが出入りする領域をプログラムで操作できるように扱えるようにしたもの」ぐらいの意味でしょうか。

ではJavaでストリームを扱うにはどうしたらいいのでしょう?

Javaの入出力機能はすべて、文字やバイトシーケンスの流れを表す「ストリーム」という概念をもとに設計されている。Javaにおいては、ストリームはデータの読み書きを行う標準化された機構であり、Javaに含まれている可変のデータソースを表すオブジェクトはすべて、ストリームとしてデータを読み書きするメソッドを提供している。

出典
builder(https://builder.japan.zdnet.com/java/20363416/)

あ、Javaで入出力処理はすべてストリームで行われると…

ストリーム指向の入出力を扱うクラスは、多くの場合java.ioパッケージに含まれている。このパッケージの中心となるのはInputStreamクラス、OutputStreamクラスの2つの抽象クラスだ。他のストリーム指向のクラスは、すべてこれらのクラスを拡張して定義されている。

出典
同上

なるほど。出典もとのサイトにも例示されてますが、
FileReaderクラスもInputStreamクラスを継承してるわけですね。
で、それを使ったらストリームを使ってることになると。

java.io.FileReader fileReader = new java.io.FileReader("/home/me/myfile.txt");

int aChar = 0;
while ((aChar = fileReader.read()) >= 0){
    System.out.println((char)aChar);
}

じゃあ、ストリームを使うのと使わないのでどんな違いがあるの?
という疑問も浮かびますが、それについてはまた後日。。。

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

Javaの標準入力のScannerで詰まった話

Javaの標準入力で詰まってしまった

題名の通りJavaの標準入力を取得する際に詰まったので
Scannerの違い

今回登場するScannerのメソッドは
- next();
- nextInt();
- nextLine();
の3つ。

next();

これは標準入力を空白まで読み取ってくれる。

標準入力: a 123 →取得: a

nextInt();

これも標準入力を空白まで読み取ってくれる。
返り値はint

標準入力: 1 abc →取得: 1

nextLine();

これが上2つとは違って2つの注意点。
- 改行(\n)まで読み込む
- 空白も読み取る
返り値はString

例1

標準入力

1
aa

取得

1

これは一行で書くなら 1\naa なので \n の前の 1 まで読んで読み取り開始位置は aa の前

例2

標準入力

1 abc
aa

取得

1 abc

これは一行で書くなら 1 abc\naa なので \n の前の 1 abc まで読んで読み取り開始位置は aa の前

例3

標準入力

1 abc

取得

Exception in thread "main" java.util.NoSuchElementException: No line found

これは一行で書くなら 1 abc\n なので \n の前の 1 abc まで読んで読み取り開始位置は、、、次の行がないのでエラー

上記を踏まえると

標準入力

a
2 3
b
4
c

として全ての数字/文字を取りたければ

1. next(); or nextLine(); →a
2. nextInt(); →2
3. nextInt(); →3
4. next(); →b
5. nextInt(); →4
6. next(); or nextLine();→c

となる。

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

Java Servlet と JSP で Hello World (Maven + Jetty で手軽に Web サーバ起動)

概要

  • Java Servlet と JSP をローカル環境で動かす
  • サーブレットコンテナには Jetty を使用する
  • Maven のプラグインで Jetty を起動する

環境

  • macOS Mojave
  • OpenJDK 11.0.2
  • Apache Maven 3.6.1
  • Jetty 9.4.19.v20190610 (Servlet API 3.1, JSP 2.3)

ソースコード

ソースコード一覧

  • pom.xml: Maven 用ビルド設定ファイル
  • MyServlet.java: Java サーブレットのソースコード
  • index.jsp: JSP のソースコード
  • web.xml: Web アプリケーションの設定ファイル
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── MyServlet.java
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

pom.xml: Maven 用ビルド設定ファイル

  • Java Servlet API 3.1 を使用するため dependency に javax.servlet-api を追加する。
  • Maven から手軽に Jetty を起動して動作確認できるよう jetty-maven-plugin を追加する。
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>my-servlet</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>my-servlet</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <jetty.version>9.4.19.v20190610</jetty.version>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- https://www.eclipse.org/jetty/documentation/current/jetty-maven-plugin.html -->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>${jetty.version}</version>
      </plugin>
    </plugins>
  </build>

</project>

MyServlet.java: Java サーブレットのソースコード

package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/html; charset=utf-8");
    PrintWriter out = resp.getWriter();
    out.println("<html><body>Hello, Servlet World!</body></html>");
    out.flush();
    out.close();
  }
}

index.jsp: JSP のソースコード

<html><body>Hello JSP World!</body></html>

web.xml: Web アプリケーションの設定ファイル

  • MyServlet.java のサーブレットに /my-servlet/* でアクセスできるようマッピングしている。
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>My Servlet Web Application</display-name>

  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>com.example.MyServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/my-servlet/*</url-pattern>
  </servlet-mapping>
</web-app>

Jetty を起動して Web アプリケーションにアクセスする

mvn jetty:run で Jetty を起動できる。

$ mvn jetty:run

http://localhost:8080/my-servlet/ にアクセスすると MyServlet.java で出力したコンテンツが返ってくる。

$ curl http://localhost:8080/my-servlet/
<html><body>Hello, Servlet World!</body></html>

http://localhost:8080/ にアクセスすると index.jsp に書いたコンテンツが返ってくる。

$ curl http://localhost:8080/
<html><body>Hello JSP World!</body></html>

参考資料

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

【コンピューターサイエンス入門第2回:機械学習やってみる】 k平均法をJavaで実装してみよう~データ同士の距離~

はじめに

こんにちは、炭山水です。

前回の続きです。
【コンピューターサイエンス入門第1回:機械学習やってみる】 k平均法をJavaで実装してみよう~座標の概念について~

前回は平面や空間上にデータを配置する座標の概念と、それをJavaで表現するところまでやってみました。

今回はデータ同士の距離の概念についてお話しします。

環境

  • IntelliJ IDEA
  • Java12 + SpringBoot 2.1.6
  • JUnit5

この環境の準備についてはこちらの記事で書きました。
IntelliJ+Gradle+SpringBoot+JUnit5(jupiter)で新規開発を始めるときの備忘録

改めて考えると距離ってなんだ

第0回でもお伝えした通り、要するにクラスタリングでやりたいことって近いデータをまとめるということなわけですが、じゃあデータ同士が近いってなんだということを定義してあげる必要があります。

例えば図のような場合

image.png

人間の目で見るとまあ、一目瞭然なわけですが、計算するのはコンピュータですので、近い/遠いを数値で表現してあげるわけですね。

そこでコンピュータでは、距離(distance)という値を用いることで、

  • 距離の値が大きければ遠い
  • 距離の値が小さければ近い

という風に数値で比較することができるようになります1

ユークリッド距離を使おう

実は距離の計算方法はそれだけで研究が成り立つほどたくさんあるのですが、今回は計算しやすく仕組みが視覚的にわかりやすいユークリッド距離というものを使います。

御大層な名前をしていますが、要するに2点間を定規で測ったときにわかる普通の距離です。が、コンピュータにデータごとに定規を使わせるわけにもいかないので、計算で算出します。

image.png

図をご覧ください。三平方の定理を覚えていますでしょうか。要はアレです。
[1,4]という座標に配置された点と[5,1]という座標に配置された点は、横方向(各座標一つ目の要素)と、縦方向(各座標二つ目の要素)の差の2乗の、合計の、平方根をとると5という距離が出ます。

ちなみに3次元でももっと高次元でも同じ方法で計算できるのでいくらでも拡張できます。

[3,1,2]と[1,0,6]という3次元における距離は、
image.png
となります。次元が増えると足す数をその分増やせばOKです。

実装してみよう

package net.tan3sugarless.clusteringsample.lib.data;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.Value;
import net.tan3sugarless.clusteringsample.exception.DimensionNotUnifiedException;
import net.tan3sugarless.clusteringsample.exception.NullCoordinateException;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * ユークリッド距離空間上の座標集合
 */
@Getter
@ToString
@EqualsAndHashCode
@Value
public class EuclideanSpace {

    private final List<List<Double>> points;
    private final int dimension;

    /**
     * n次元座標のリストと次元をセットする
     *
     * @param points : n次元座標のリスト
     * @throws DimensionNotUnifiedException : 座標の次元が統一されていないリストをセットした
     * @throws NullCoordinateException      : 座標の数値にnullが含まれていた
     * @throws NullPointerException         : nullデータ、もしくはnull要素を含むデータを渡した
     */
    public EuclideanSpace(List<List<Double>> points) {
        if (points.stream().mapToInt(List::size).distinct().count() > 1) {
            throw new DimensionNotUnifiedException();
        }
        if (points.stream().anyMatch(point -> point.stream().anyMatch(Objects::isNull))) {
            throw new NullCoordinateException();
        }

        this.points = points;
        this.dimension = points.stream().findFirst().map(List::size).orElse(0);
    }

    /**
     * 任意の座標からの、このインスタンスに格納された各座標の距離を算出する
     *
     * @param target 各座標からの距離を出したい基準点の座標
     * @return targetからのユークリッド距離を表すリスト
     * @throws DimensionNotUnifiedException : targetとインスタンスの次元が異なる
     * @throws NullCoordinateException      : targetにnullを含む
     */
    public List<Double> distanceFromTarget(List<Double> target) {
        if (target.size() != dimension) {
            throw new DimensionNotUnifiedException();
        }

        if (target.stream().anyMatch(Objects::isNull)) {
            throw new NullCoordinateException();
        }

        return points.stream().map(point -> {
            double squareOfDistance = 0.0;
            for (int i = 0; i < target.size(); i++) {
                squareOfDistance += Math.pow(point.get(i) - target.get(i), 2);
            }

            return Math.sqrt(squareOfDistance);
        }).collect(Collectors.toList());
    }
}

前回と同じクラスに作っていきます。

まず、次元のチェックをしやすくするためにコンストラクタとフィールドをちょっと改修しました。

    private final int dimension;

intフィールドに次元を格納できるようにしたのと、

        this.dimension = points.stream().findFirst().map(List::size).orElse(0);

取得したデータが何次元が計算するロジックの追加ですね。

で、実際に距離を算出するメソッドがこちら。今後の実装で「とある点と全部の座標に対する距離」を算出したいのでこのようなかたちになってます。

    /**
     * 任意の座標からの、このインスタンスに格納された各座標の距離を算出する
     *
     * @param target 各座標からの距離を出したい基準点の座標
     * @return targetからのユークリッド距離を表すリスト
     * @throws DimensionNotUnifiedException : targetとインスタンスの次元が異なる
     * @throws NullCoordinateException      : targetにnullを含む
     */
    public List<Double> distanceFromTarget(List<Double> target) {
        if (target.size() != dimension) {
            throw new DimensionNotUnifiedException();
        }

        if (target.stream().anyMatch(Objects::isNull)) {
            throw new NullCoordinateException();
        }

        return points.stream().map(point -> {
            double squareOfDistance = 0.0;
            for (int i = 0; i < target.size(); i++) {
                squareOfDistance += Math.pow(point.get(i) - target.get(i), 2);
            }

            return Math.sqrt(squareOfDistance);
        }).collect(Collectors.toList());
    }

いきなりMath::powとかMath::sqrt出てきてますが、powは累乗、sqrtは平方根をとるメソッドです。MathはWebサービスとか業務アプリケーションであんまりなじみのないクラスですが算術計算でよく使うので覚えていてくださいね。

図示するとこういうことがしたい感じです。

image.png

そしてテスト

package net.tan3sugarless.clusteringsample.lib.data;

import net.tan3sugarless.clusteringsample.exception.DimensionNotUnifiedException;
import net.tan3sugarless.clusteringsample.exception.NullCoordinateException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

class EuclideanSpaceTest {

    //全体 null,空,要素1つ,要素複数
    //各要素 null含む,空含む,すべて空(0次元),1次元,n次元
    //各要素内の座標 null含む,0含む,null含まない
    //次元チェック すべて同じ次元,異なる次元
    static Stream<Arguments> testConstructorProvider() {
        //@formatter:off
        return Stream.of(
                Arguments.of(null, new NullPointerException(), 0),
                Arguments.of(Collections.emptyList(), null, 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, -2.1)), null, 2),
                Arguments.of(Arrays.asList(Arrays.asList(1.2, 0.1), Arrays.asList(0.0, 1.5)), null, 2),
                Arguments.of(Arrays.asList(null, Arrays.asList(0, 1.5), Arrays.asList(-0.9, 0.1)), new NullPointerException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(-0.9, 0.1), Arrays.asList(0.0, 1.5), Collections.emptyList()), new DimensionNotUnifiedException(), 0),
                Arguments.of(Arrays.asList(Collections.emptyList(), Collections.emptyList(), Collections.emptyList()), null, 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5), Arrays.asList(0.0), Arrays.asList(-2.2)), null, 1),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, 2.2, -1.9), Arrays.asList(0.0, 0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), null, 3),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, null, -1.9), Arrays.asList(0.0, 0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), new NullCoordinateException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(1.5, 2.1, -1.9), Arrays.asList(0.0, 0.0), Arrays.asList(0.9, 5.0, 2.2)), new DimensionNotUnifiedException(), 0),
                Arguments.of(Arrays.asList(Arrays.asList(2.1, -1.9), Arrays.asList(0, 0, 0), Arrays.asList(0.9, 5.0, 2.2)), new DimensionNotUnifiedException(), 0)
        );
        //@formatter:on
    }

    @ParameterizedTest
    @MethodSource("testConstructorProvider")
    @DisplayName("コンストラクタのテスト")
    void testConstructor(List<List<Double>> points, RuntimeException e, int dimension) {
        if (e == null) {
            Assertions.assertDoesNotThrow(() -> new EuclideanSpace(points));

            EuclideanSpace actual = new EuclideanSpace(points);
            Assertions.assertEquals(dimension, actual.getDimension());
        } else {
            Assertions.assertThrows(e.getClass(), () -> new EuclideanSpace(points));
        }
    }

    // points : 0件/1件/2件, 0次元/1次元/2次元, 0/正/負
    // target : null/空/1次元/2次元, null含む/含まない, 0/正/負/同一座標
    static Stream<Arguments> testDistanceFromTargetProvider() {
        return Stream.of(
                //@formatter:off
                Arguments.of(Collections.emptyList(), Collections.emptyList(), null, Collections.emptyList()),
                Arguments.of(Collections.emptyList(), Arrays.asList(0.1), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Collections.emptyList()), Collections.emptyList(), null, Arrays.asList(0.0)),
                Arguments.of(Arrays.asList(Collections.emptyList()), Arrays.asList(0.1), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(3.0)), Arrays.asList(1.0), null, Arrays.asList(2.0)),
                Arguments.of(Arrays.asList(Arrays.asList(3.0)), Arrays.asList(1.0, 2.0), new DimensionNotUnifiedException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(-1.0, 0.0)), Arrays.asList(2.0, -4.0), null, Arrays.asList(5.0)),
                Arguments.of(Arrays.asList(Arrays.asList(-1.0, 0.0)), Arrays.asList(null, -4.0), new NullCoordinateException(), Collections.emptyList()),
                Arguments.of(Arrays.asList(Arrays.asList(-3.0, 0.0), Arrays.asList(0.0, -4.0)), Arrays.asList(0.0, -4.0), null, Arrays.asList(5.0, 0.0))
                //@formatter:on
        );
    }

    @ParameterizedTest
    @MethodSource("testDistanceFromTargetProvider")
    @DisplayName("距離算出のテスト")
    void testDistanceFromTarget(List<List<Double>> points, List<Double> target, RuntimeException e, List<Double> distances) {
        EuclideanSpace space = new EuclideanSpace(points);

        if (e == null) {
            Assertions.assertEquals(distances, space.distanceFromTarget(target));
        } else {
            Assertions.assertThrows(e.getClass(), () -> space.distanceFromTarget(target));
        }
    }
}

今回実装したバージョンはGitHubのこちらのバージョンで公開しています。
https://github.com/tan3nonsugar/clusteringsample/releases/tag/v0.0.2

ここまで読んでいただいてありがとうございました。次回は「データの重心」という考え方についてお話ししようと思います。では~ノシ


  1. 厳密な話をすれば、非類似度/類似度の説明を先にしたり、距離の数学的な定義を満たす満たさないの話をするべきなのでしょうが、感覚をつかむことを優先してここでは割愛します。 

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

【Java】標準入力&標準出力をJUnitでテストしたい

きっかけ

最近競技プログラミングの勉強をはじめた。ローカルで動作確認をする際にいちいち入力するのがめんどくさい。JUnitでやりたい。

環境

  • Windows10
  • IntelliJ IDEA Community Edition 2019.1.1
  • Java 1.8.0_211

参考にした記事

Qiita - JUnitで標準入力をつっこんで流す
kencobaの日記 - 標準入出力を横取りする
にょきにょきブログ - System.out を置き換えよう

概要

SystemクラスのsetOutsetInで出力先と入力先を標準出力、入力から変えることができる。
OutにはPrintStream、InにはInputStreamを継承したクラスを定義して設定すればよい。

実装

入力クラスと出力クラスを作成する。

入力クラス - StandardInputStream

StandardInputStream.java
import java.io.InputStream;

public class StandardInputStream extends InputStream {
    private StringBuilder sb = new StringBuilder();
    private String lf = System.getProperty("line.separator");

    /**
     * 文字列を入力する。改行は自動的に行う
     * @param str 入力文字列
     */
    public void inputln(String str){
        sb.append(str).append(lf);
    }

    @Override
    public int read() {
        if (sb.length() == 0) return -1;
        int result = sb.charAt(0);
        sb.deleteCharAt(0);
        return result;
    }
}

出力クラス - StandardOutputStream

StandardOutputStream.java
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.StringReader;

public class StandardOutputStream extends PrintStream {
    private BufferedReader br = new BufferedReader(new StringReader(""));

    public StandardOutputStream() {
        super(new ByteArrayOutputStream());
    }

    /**
     * 1行分の文字列を読み込む
     * @return 改行を含まない文字。終端の場合はnull
     */
    public String readLine() {
        String line = "";
        try {
            if ((line = br.readLine()) != null) return line;
            br = new BufferedReader(new StringReader(out.toString()));
            ((ByteArrayOutputStream) out).reset();
            return br.readLine();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

テスト対象コード

テスト対象コードはatcoderさんから拝借。
PracticeA - Welcome to AtCoder

問題
整数 a,b,cと、文字列 s が与えられます。 a+b+c の計算結果と、文字列 s を並べて表示しなさい。

入力

a
b c
s
WelcomeToAtCoder.java
import java.util.Scanner;

public class WelcomeToAtCoder {
    public static void main(String[] args) {
        try (Scanner sc = new Scanner(System.in)) {
            int r = sc.nextInt();
            r += sc.nextInt();
            r += sc.nextInt();
            String s = sc.next();
            System.out.println(r + " " + s);
        }
    }
}

テストコード

WelcomeToAtCoderTest.java
import WelcomeToAtCoder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import util.io.StandardInputStream;
import util.io.StandardOutputStream;

import static org.hamcrest.CoreMatchers.is;

public class WelcomeToAtCoderTest {

    private StandardInputStream in = new StandardInputStream();
    private StandardOutputStream out = new StandardOutputStream();

    @Before
    public void before() {
        System.setIn(in);
        System.setOut(out);
    }

    @After
    public void after() {
        System.setIn(null);
        System.setOut(null);
    }

    @Test
    public void main01() {
        in.inputln("1");
        in.inputln("2 3");
        in.inputln("hoge");
        WelcomeToAtCoder.main(null);
        Assert.assertThat(out.readLine(), is("6 hoge"));
    }
}

事前処理で入出力を切り替えてテストする。

おわりに

テストケースを書くのは簡単なので実装が楽になった気がする。
これに限らずテストはちゃんと書こうと思った。
TDD勉強したいなあ。
JUnit久しぶりにさわった

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

変数名の付け方 違和感7選

はじめに

仕事柄、他人の書いたソース(主に発注先から引き取ったソース)を読む機会が多々あるが、その中でも読みづらさの原因の一つである「変数名の付け方」について、違和感を感じる頻度の高かったものをリストアップしてみた。

もちろん自分の書いたソースについても、あとから自分が見ずらくならないよう、自分自身も気を付けたいと思う。

以下、パターンを列挙する。

1. やたら省略されている

× res -> 〇result
△ prop -> 〇property
× pat -> 〇pattern
× enz -> 〇enzyme

昔のマシンリソースが限られていた時代ならいざ知らず、現在では、可読性が大幅に下がるため、省略するメリットはほとんどない。
"prop"は、そこまで違和感はないが、一貫性という観点から"property"とした方が、より「違和感」を感じない。
"enz" にいたっては、めったに出くわさないような(おそらく)重要な単語を省略されても...と思う。

2. 微妙な綴りミス

× propaty -> 〇property
× canceld -> 〇canceled
× spacified -> 〇specified

"propaty" は最初から違和感を感じていたものの、あまり見かけるミスでもないため、綴りが間違えていることに気が付くのに時間がかかり、気づいた時には胸のつかえが取れすっきりした。"canceld"も、過去形にしたいのはわかるが、正しく書いてほしい。
最近の開発ツールは綴りミスも指摘してくれるものが多いため、それらを活用すればよい。

3. 具体性がなく目的が分かりづらい

× count -> 〇progressCount
× max -> 〇progressCountMax

小さなループや関数の中等、スコープが狭い場合はありだと思うが、大きなクラスのインスタンス変数等で使うのであれば、もう少し具体的に書かないと、当該箇所を読んでいるときに「何の変数だっけ」となってしまう。

これはプリミティブ型だけではなく、クラス型変数にもいえる。
例えばCalculationResult クラスのクラス型変数の名前としては、

× result -> 〇calculationResult

とのように具体的にすべきである。前者では、何のresultか即座にイメージできない。

4. 意味が逆

isValidというboolean型の変数名を付けたはいいが、妥当でない場合に"true"、妥当な場合に"false"が入るようになっていた。
論外としか言いようがない。こうなると確信犯的なものさえ感じるのだが...

boolean isValid = false;

if(!inputFile.exists()){
    isValid = true;
}

if(isValid){
  printUsage();
}

5. 型がイメージできない

自分はデータがどう処理されていくかをイメージしながらソースを読むことが多いため、変数名から目的と型が一目で分かることが望ましいと考えている。データ処理でよく使うものは、プリミティブ型以外では、配列、List、Map等がある。Listには〇〇List(〇〇はListに格納されるるものを表す名前)、Mapには〇〇△△Map(〇〇はMapのキーを表す名前、△△はMapの値を表す名前)などつけられていると分かりやすく感じる。

Map型に対して、以下の例を挙げる。

× names -> 〇 nameValidMap

後者は、少なくとも名前と何かの対応が格納されたマップということが分かるが、前者では全くわからない。リストと勘違いし、読み進むうちにあれ?っと思い、定義したた箇所に戻って確認というループを繰り返すことになる。

6. 単数、複数がイメージできない

前項と近いが、データの処理の流れを追うにあたり、その変数が"単数"のデータなのか"複数"のデータなのかの理解が重要である。
したがって単数データであれば単数形にし、配列やリストなど複数のデータを格納可能な変数は複数形にした方がとても分かりやすい。

7. 慣用的な名前をわざわざ変更している

Javaならおなじみのメイン関数である。こう書いてある。

public static void main(String[] option){
    
}

違和感を感じないだろうか。
そう、main関数の引数の名前は、"argv"と相場が決まっているにも関わらず、わざわざ別の名前に変更している。

main関数の引数には、プログラムの”引数”が格納されるのであり、"オプション"が格納されるわけではない(結果として格納されるとしても)

このoptionという変数はどこで定義されているんだろうと結構悩んでしまった。
慣用的に使う名前を変更するのであれば、よほどの積極的な理由が必要ではないだろうか。

おわりに

今格闘してるソースは他にも「空行の入れ方」、「変数のスコープの取り方」、「コメントの書き方」、「処理の順番」などの違和感が大量にあるのだが、別の機会にまとめたい。

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

地理院タイルからCS立体図のタイルを作ってみる

概要

最近、仕事の絡みで長野県林業総合センターで考案された、微地形が強調された地図「CS立体図」の存在を知りました。
CS立体図は傾斜量図(Slope)と曲率図(Curvature)を合成して作成される図で、凹凸など、地形判読を容易にする立体図法です。
QGIsプラグインなど、既に様々なツールが提供されていますが、勉強を兼ねて、地理院タイルからCS立体図タイルを作成してみました。

aso02.jpg
aso01.jpg

作成方法

CS立体図作成に必要な傾斜量図は地理院タイルで提供されているので、DEMタイルから曲率図を作成すれば、CS立体図を作成できると考えました。
このため、作成手順は以下のとおりとしています。
 1.地理院タイルより、傾斜量図タイルを取得。
 2.地理院タイルより、DEMタイルを取得
 3.DEMより曲率を計算し、曲率図タイルを作成
 4.傾斜量図タイルと曲率図タイルのピクセルを乗算し、CS立体図タイル生成

ソースコード

ソースコードはGithubにアップしています。-> https://github.com/termat/CSMapTile
詳細はそちらをご覧ください。
なお、CS立体図タイルの生成はTileDBクラスで行っており、主な処理内容は以下のとおりです。

1.CS立体図タイル生成

TileDB.java(抜粋)
    private static String slope="https://cyberjapandata.gsi.go.jp/xyz/slopemap";
    private static String dem14="https://cyberjapandata.gsi.go.jp/xyz/dem_png";
    private static String dem15a="https://cyberjapandata.gsi.go.jp/xyz/dem5a_png";
    private static String dem15b="https://cyberjapandata.gsi.go.jp/xyz/dem5b_png";
    private static final int P8=(int)Math.pow(2,8);
    private static final int P16=(int)Math.pow(2,16);
    private static final int P23=(int)Math.pow(2,23);
    private static final int P24=(int)Math.pow(2,24);
    private static final double U=0.01;
    private static double GEO_R=6378137;
    private static int[] col=new int[]{255,232,197};

    /*
     * CSタイル画像取得
     */
    public BufferedImage getCSImage(int zoom,int tileX,int tileY)throws IOException{
        String param="/"+zoom+"/"+tileX+"/"+tileY;
        BufferedImage s_img=ImageIO.read(new URL(slope+param+".png"));
        s_img=getColorTransSlope(s_img);
        BufferedImage d_img=createDemTile(zoom,tileX,tileY);
        BufferedImage c_img=getCurve(d_img,zoom);
        BufferedImage img=mul(c_img,s_img);
        return img;
    }

    /*
     * 傾斜量図をオレンジ系色に変換
     */
    private static BufferedImage getColorTransSlope(BufferedImage img){
        int w=img.getWidth();
        int h=img.getHeight();
        BufferedImage ret=new BufferedImage(w,h,img.getType());
        for(int i=0;i<w;i++){
            for(int j=0;j<h;j++){
                float cv=(float)(img.getRGB(i, j)&0x0000ff)/255f;
                ret.setRGB(i, j, new Color((int)(col[0]*cv),(int)(col[1]*cv),(int)(col[2]*cv)).getRGB());
            }
        }
        return ret;
    }

    /*
     * 258×258サイズのDemタイルを生成
     */
    private static BufferedImage createDemTile(int zoom,int tileX,int tileY)throws IOException{
        BufferedImage bc=getDemImage(zoom,tileX,tileY);
        BufferedImage ret=new BufferedImage(bc.getWidth()+2,bc.getHeight()+2,bc.getType());
        Graphics2D g=ret.createGraphics();
        g.drawImage(bc, 1, 1, comp);
        try{
            BufferedImage bu=getDemImage(zoom,tileX,tileY-1);
            g.drawImage(bu, 1, -255, comp);
        }catch(IOException e){}
        try{
            BufferedImage bd=getDemImage(zoom,tileX,tileY+1);
            g.drawImage(bd, 1, 257, comp);
        }catch(IOException e){}
        try{
            BufferedImage bl=getDemImage(zoom,tileX-1,tileY);
            g.drawImage(bl, -255, 1, comp);
        }catch(IOException e){}

        try{
            BufferedImage br=getDemImage(zoom,tileX+1,tileY);
            g.drawImage(br, 257, 1, comp);
        }catch(IOException e){}
        g.dispose();
        return ret;
    }

    /*
     * 地理院からDemタイルを取得
     */
    private static BufferedImage getDemImage(int zoom,int tileX,int tileY)throws IOException{
        String param="/"+zoom+"/"+tileX+"/"+tileY;
        BufferedImage d_img=null;
        if(zoom<=14){
            d_img=ImageIO.read(new URL(dem14+param+".png"));
        }else{
            try{
                d_img=ImageIO.read(new URL(dem15a+param+".png"));
            }catch(IOException e){
                d_img=ImageIO.read(new URL(dem15b+param+".png"));
            }
        }
        return d_img;
    }

    /*
     * 画素乗算
     */
    private static BufferedImage mul(BufferedImage im1,BufferedImage im2){
        int w=im1.getWidth();
        int h=im1.getHeight();
        BufferedImage ret=new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
        for(int i=0;i<w;i++){
            for(int j=0;j<h;j++){
                int rgb1=im1.getRGB(i, j);
                int rgb2=im2.getRGB(i, j);
                ret.setRGB(i, j, mulRGB(rgb1,rgb2));
            }
        }
        return ret;
    }

    /*
     * RGB乗算
     */
    private static int mulRGB(int rgb1,int rgb2){
        float r1=(float)(rgb1 >> 16 & 0xff)/255f;
        float g1=(float)(rgb1&0x00ff00 >> 8 & 0xff)/255f;
        float b1=(float)(rgb1&0x0000ff & 0xff)/255f;
        float r2=(float)(rgb2 >> 16 & 0xff)/255f;
        float g2=(float)(rgb2&0x00ff00 >> 8 & 0xff)/255f;
        float b2=(float)(rgb2&0x0000ff & 0xff)/255f;
        return new Color(r1*r2,g1*g2,b1*b2).getRGB();
    }

    /*
     * 曲率画像取得
     */
    public static BufferedImage getCurve(BufferedImage dem,int zoom){
        BufferedImage ret=new BufferedImage(dem.getWidth(),dem.getHeight(),BufferedImage.TYPE_INT_RGB);
        double[][] dd=new double[dem.getWidth()][dem.getHeight()];
        for(int i=0;i<dd.length;i++){
            for(int j=0;j<dd[i].length;j++){
                dd[i][j]=getZ(dem.getRGB(i, j));
            }
        }
        double ll=getL(zoom);
        for(int i=1;i<dd.length-1;i++){
            for(int j=1;j<dd[i].length-1;j++){
                double[][] p=new double[][]{
                        {dd[i-1][j-1],dd[i-1][j],dd[i-1][j+1]},
                        {dd[i][j-1],dd[i][j],dd[i][j+1]},
                        {dd[i+1][j-1],dd[i+1][j],dd[i+1][j+1]}};
                double cu=getCurveVal(p,ll);
                Color col=grad1.getColor(range.getNormalValue(cu));
                ret.setRGB(i, j, col.getRGB());
            }
        }
        return ret.getSubimage(1, 1, 256, 256);
    }

    /*
     * 標高取得
     */
    private static double getZ(int intColor){
        Color c=new Color(intColor);
        int r=c.getRed();
        int g=c.getGreen();
        int b=c.getBlue();
        int x=r*P16+g*P8+b;
        if(x<P23){
            return U*(double)x;
        }else if(x>P23){
            return 0;
        }else{
            return U*(double)(x-P24);
        }
    }

    /*
     * ズームレベルの1pixel長さ(m)
     */
    private static double getL(int zoom){
        return 2*GEO_R*Math.PI/256/Math.pow(2, zoom);
    }

    /*
     * 曲率計算
     */
    private static double getCurveVal(double[][] p,double ll){
        double ex1=p[1][0]+p[1][2]-2*p[1][1];
        double ex2=p[0][1]+p[2][1]-2*p[1][1];
        return (ex1+ex2)/(ll*ll)*100;
    }

■国土地理院 傾斜量タイル
slope.png
■国土地理院 DEMタイル
dem.png
■DEMタイルから計算した曲率図タイル
curve.png
■生成したCS立体図タイル
csmap.png

2.地すべり地形タイル生成

せっかくなので、国立研究開発法人 防災科学技術研究所が公開している地すべり地形分布図WMSサービスから地すべり地形情報を取得してタイル化し、CM立体図タイルと重ね合わせることにしました。
地すべり地形情報の取得とタイル化はJShisWMSクラスで処理しています。

JShisWMS.java
import java.awt.image.BufferedImage;
import java.net.URL;

import javax.imageio.ImageIO;

public class JShisWMS {
    private static final String baseURL="http://www.j-shis.bosai.go.jp/map/wms/landslide?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&";
    private static final double L=(180.0/Math.PI)*Math.asin(Math.tanh(Math.PI));

/*
 * http://www.j-shis.bosai.go.jp/wms-landslide
 *
 * 4301 Tokyo
 * 4326 WGS84
 * 4612 JGS2000
 *
 * 輪郭構造(滑落崖と側方崖)  L-V3-S100
 * 輪郭構造(移動体の輪郭・境界)(移動体アーク)    L-V3-S200
 * 輪郭構造(移動体の輪郭・境界)(移動体ポリゴン) L-V3-S300
 * 内部構造 L-V3-S400
 * 移動方向等移動体の主移動方向   L-V3-S500
 * 移動体の重心   L-V3-CENTER
 * 上記全ての地すべり地形    L-V3-ALL
 *
 */

    public static BufferedImage getJShis(int zoom,int tileX,int tileY){
        try{
            String url=getURL(zoom,tileX,tileY);
            BufferedImage img=ImageIO.read(new URL(url));
            return img;
        }catch(Exception e){
            e.printStackTrace();
            return null;
        }
    }

    private static String getURL(int zoom,int tileX,int tileY){
        String ret=baseURL+getBBOX(zoom,tileX,tileY)+"&CRS=EPSG:4612"+"&WIDTH=256&HEIGHT=256&LAYERS=L-V3-ALL&FORMAT=image/png&TRANSPARENT=TRUE";
        return ret;
    }

    public static String getBBOX(int zoom,int tileX,int tileY){
        String ret="BBOX=";
        float[][] b=getTileBounds(zoom,tileX,tileY);
        ret=ret+Float.toString(b[1][2])+","+Float.toString(b[0][1])+","+Float.toString(b[0][2])+","+Float.toString(b[1][1]);
        return ret;
    }

    private static float[][] getTileBounds(int zoom,long tileX,long tileY){
        float[] ll1=pixelToLonLatCoord(zoom,tileX*256,tileY*256);
        float[] ll2=pixelToLonLatCoord(zoom,tileX*256+255,tileY*256+255);
        return new float[][]{ll1,ll2};
    }

    private static float[] pixelToLonLatCoord(int zoom,long x,long y){
        double lon=180.0*(x/Math.pow(2, zoom+7)-1);
        double tmp0=((-Math.PI/Math.pow(2, zoom+7))*y);
        double tmp1=atanh(Math.sin(L*Math.PI/180.0));
        double lat=(180.0/Math.PI)*Math.asin(Math.tanh(tmp0+tmp1));
        return new float[]{(float)zoom,(float)lon,(float)lat};
    }

    private static double atanh(double v){
        return 0.5*Math.log((1.0+v)/(1.0-v));
    }
}

■CS立体図タイル
cs2.png
■JSHIS地すべり地形タイル
jsi.png
■CS地すべり地形タイル
compo.png

3.その他

今回はローカル利用が目的なので、Githubにのせたコードでは、生成タイルをSqliteに格納するようにしています。
また、生成タイル確認用にjxmapviewerを利用したタイルブラウザのコードを含めています。

参考資料

CS立体図タイルの作成に当たっては、以下の資料を参考にしました。

最後に

 CS立体図の作成方法について調べている時に@wayama_ryousuke氏のエントリ「pix2pixおかわり!CS立体図から地すべり地形分布図を作成してみた」を知りました。すごくおもしろそうなので、解像度的に無理があるかもですが、今回生成したCS立体図タイルと地すべり地形タイルでpix2pixを試してみたいです。

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

JavaとC++は学ぶな!!初心者が学ぶべき言語【さらに厳選】

注意事項

この記事には以下の要素が含まれます。苦手な方はご注意くださいね。

  • Pythonへの熱いdisり
  • C言語なんてなかった
  • JDK問題について深く知らないので怖がっている
  • いわゆる関数型言語に言及しないスタイル(F#とかScalaとか?)

はじめに

JavaとC++は学ぶな!!初心者が学ぶべき言語【厳選】

という記事が出ているのですが、C++erとしては遺憾の意を示さざるを得ません。そのうえでこれからプログラミングを始める人におすすめする言語を紹介します。え、C++?初心者にはおすすめできません。

初心者におすすめする言語が満たすべき要件

  • 人口がそこそこいる
  • Windowsでも容易に開発環境が構築できる
  • 動作結果が目で見てわかる
  • コンパイルしなくていい
  • 言語規格が少なくとも5年以内に更新されている
  • 言語規格が後方互換性を備えていること
  • 日本語資料が豊富にあること
  • 非力なPCでも開発できる

人口がそこそこいる

プログラミング言語を作っている人もたくさんいるので無限にプログラミング言語は存在し得るわけですが、さすがにある程度ユーザー数がいない言語を薦めるわけにはいきません。

Windowsでも容易に開発環境が構築できる

例えばPythonはなぜかWindowsだと動かないという事態によく遭遇します(私だけ・・・?)。初心者にエラー文を読んで原因を切り分けさせるのは無理です。

他の言語でもWindowsで完璧に動くぜ!って言うのは以外と少ないです。Rubyはだいぶ頑張っていると思いますがそれでも特に音声やグラフィックが絡むとcannot load such fileと言われがちです(なぜかmsys2 mingw64 rubyでruby2dが使えない、チャットで問い合わせ中)。

動作結果が目で見てわかる

初心者はコンソールなんてまともに扱えませんし、"Hello world!"っていう文字を見せられても感動できません。

なんか図形が動くとかそういう視覚に容易に訴えられるのが望ましいです。

C++はGUIを作るのが極めて難しいのでこういうのは無理です(2D graphics標準化は頓挫したしな)。

言語標準ライブラリに2D描画ライブラリがあるか、十分メンテされてユーザーが居る2Dライブラリがあることが望ましいですね。

コンパイルしなくていい

コンパイルはなれている人でもやっぱり面倒だと思います。もちろんいろいろ自動化することは出来ますが、自動化を組むのが今度は面倒です。

ちなみに面倒くさがれない人は多分プログラミングにあまり向いていません。

言語規格が少なくとも5年以内に更新されている

プログラミング言語も切磋琢磨し日々様々な改善がなされているべきです。ある程度保守的なC++ですら3年おきに更新しているんですから5年経っても更新されない言語は死んでます。え?C言語?2017年末にC11と全く同じ内容をリネームして出してたけどあれはないでしょ。

言語規格が後方互換性を備えていること

初心者が頑張ってググって出てきた情報どおりに書いて動かそうとしたら動かないとなるとモチベーションが吹き飛びます。初心者じゃなくても心が折れます。セキュリティ上の懸念などからdeprecatedになる機能が出るのは仕方ないとしても、Pythonみたいに巨大なbreaking changeを噛ましてくる言語はおすすめできません。

日本語資料が豊富にあること

ただでさえわからないプログラミング言語を学ぶのに、自分の母国語じゃない資料で勉強するとか脳みそがパンクしてしまいます。

非力なPCでも開発できる

プログラミングするのに15万超えるようなPC買えと言われても多くの人は困ってしまいます。Unityとかは結構メモリー持っていくのでつらそうです。一般に出回っているPCはメモリーが4GBのものが多いです。

おすすめしたい言語

これらを満たす言語を残念ながら私の狭い知識では一つしか上げることが出来ませんでした。

JavaScript

ブラウザ上で動くのがやっぱり大きいです。Canvas APIを叩くことで図形が描画できます。実行環境をブラウザにすることで、ChromeかFirefoxを入れてもらえば(運が良ければすでに入っている)ほぼセットアップが終わります。
そのままのJavaScriptを使ったブロックくずしゲーム - ゲーム開発 | MDN
というStep-by-StepでCanvas APIを使う資料がなんとMDNにありますし実に良いです。

言語の人口も計算不能なレベルで大きく、開発も盛んです。

おすすめしたかった言語

Ruby

Ruby自体はかなりいい言語で、日本語資料も開発者が日本人ですからたくさんあります。グラフィックではRuby2dが結構良さそうなライブラリなんですよね、動けば。うごけー。

おすすめできない言語の例

C++

C++erが言うんだから間違いない(確信)。Visual Studioはコンソールアプリケーションを作る分には手順を簡単にしてくれますが、外部ライブラリを使うには不向きです。外部ライブラリをまともに使うにはCMakeという別の言語の助けが必要になります。ものすごく難しいってわけではないですが、他の言語より難しいのは確かです。

C++自体もC++17以降を使う分には極端に難しいということはないと思うのですが(C++er基準)、何分古い情報が多いのとまともな入門書がないのが辛い気がします。

グラフィックライブラリ自体はopennframeworksとかDXライブラリとかがあります。

C#

Unityはゲーム業界でもっとも使われているライブラリです。これはC#で書くことが出来ますが、Unityで使えるC#のバージョンがものすごく古いという問題がありました。ようやく新しいC#への追従の動きが一昨年くらいからあるわけですが果たしてC#の進化に追いつけるのか・・・?

C#自体はC#7.0とかC#8.0で書く分には悪くないと思いますが古い情報に出くわしやすい印象です。

Java

広く使われている言語で進化を続けています。が!Oracle JDK vs OpenJDK問題を経て(Oracle) JDK8で止まる勢とOpenJDKに移る勢で分裂している気がします。おなじくJVMで動く言語ScalaのドキュメントにはJDK8をいれろって書いてあります。C++erとしては言語そのものよりJVMレベルのゴタゴタが気になってしまいます。

ちなみにOpenJDKは自分でPATH通さないといけない問題はchocolatey使えやって思ってます。

2Dグラフィックは言語標準ライブラリにある気がします。

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

IntelliJ IDEA 2019.2の新機能プロファイラーを試してみた

IntelliJ IDEA 2019.2の新機能 | 株式会社サムライズム

今回の新機能はチョー豪華!

ぼくは、パフォーマンス改善をよくやるんですけど、IntelliJ IDEAにもプロファイルングツールがつくようになったので、使ってみました。
便利そうだったので、これからのメインウェポンはこれになりそうです。

前提: Ubuntu19.04, Scala(Javaでもいけました)

設定方法

↓のように+を押して一通りのプロファイラーを追加してOKを押す
Screenshot from 2019-07-27 10-01-05.png

そうすると実行するときのメニューになんか増えてるんで、とりあえずRun 'なんとか' with 'Profiler'を押してみる
Screenshot from 2019-07-27 10-34-46.png

↓のモーダルが出てくるので、
Screenshot from 2019-07-27 10-00-11.png
指示の通りに、

sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'
sudo sh -c 'echo 0 > /proc/sys/kernel/kptr_restrict'

を実行。
調べてみたら、この設定は、カーネルのパフォーマンスに関する情報の収集や解析に必要な設定らしいです。
Linux以外だと要らないか別の設定方法が出てくるかも?

↓関連記事
第52章 コンパイラーとツール - Red Hat Customer Portal
Linux* カーネルの解析を有効にする

プロファイルしてみた

Frame Graphタブ

うーんあまり使わないかも?
Screenshot from 2019-07-27 10-08-52.png

Call Tree

ツリーになってそれぞれ%で出るようになった。
VisualVMでプロファイルするとこんな感じのが出ますよね。
%の右の青い数字は折り畳まれてる件数みたいです。
Screenshot from 2019-07-27 10-11-19.png

Method List

サンプリングされた順でソートされるようになった。
選択したメソッドからBack TraceタブとMerged Calleesタブが選べる。
Back Traceタブはどこから呼ばれたのかをたどることができる。
Screenshot from 2019-07-27 10-52-35.png

Merged Calleesタブは、そのメソッド内で呼ばれたメソッドっぽい。
Screenshot from 2019-07-27 10-53-19.png

Java Flight Recorderも使ってみた

GCのログやら、弱参照の個数やらなんか色々出ますね。
Screenshot from 2019-07-27 10-25-58.png

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

reactorでblockingな処理を�マルチスレッドで実行する

CompletableFutureやExecutorServiceを使わず、reactorでもblockingな処理をマルチスレッドで行って結果をまとめるような処理をやりたい。具体的な実装方法はマニュアルに記載がある。
https://projectreactor.io/docs/core/release/reference/#faq.wrap-blocking
ただし、実際にこの方法で実装すると、Schedulers.elastic()がひとおもいにthreadを生成してしまうために、リソースを食いつぶしてしまう可能性がある。

    public static void main(String[] args) {
        final var webClient = WebClient.builder()
                                       .baseUrl("http://example.com")
                                       .build();
        Flux.fromStream(IntStream.range(1, 100).boxed())
            .flatMap(i -> Mono.fromCallable(() -> webClient.get()
                                                           .retrieve()
                                                           .bodyToMono(String.class)
                                                           .block()) // 説明のためあえてblockしてる
                              .subscribeOn(Schedulers.elastic()))
            .blockLast();
    }

実行ログ

・
・
02:24:57.014 [elastic-77] DEBUG o.s.w.r.f.client.ExchangeFunctions - [11ca9c18] HTTP GET http://example.com
02:24:57.017 [elastic-15] DEBUG o.s.w.r.f.client.ExchangeFunctions - [3ee270fc] HTTP GET http://example.com
02:24:57.017 [elastic-61] DEBUG o.s.w.r.f.client.ExchangeFunctions - [272cc048] HTTP GET http://example.com
02:24:57.015 [elastic-45] DEBUG o.s.w.r.f.client.ExchangeFunctions - [12f4ca28] HTTP GET http://example.com
02:24:57.014 [elastic-17] DEBUG o.s.w.r.f.client.ExchangeFunctions - [3d44ed66] HTTP GET http://example.com
02:24:57.017 [elastic-57] DEBUG o.s.w.r.f.client.ExchangeFunctions - [6b5899a3] HTTP GET http://example.com
02:24:57.017 [elastic-92] DEBUG o.s.w.r.f.client.ExchangeFunctions - [7ec595f3] HTTP GET http://example.com
02:24:57.015 [elastic-94] DEBUG o.s.w.r.f.client.ExchangeFunctions - [70f26d87] HTTP GET http://example.com
・
・
    public static void main(String[] args) {
        final var webClient = WebClient.builder()
                                       .baseUrl("http://example.com")
                                       .build();

        Flux.fromStream(IntStream.range(1, 100).boxed())
            .flatMap(i -> Mono.fromCallable(() -> webClient.get()
                                                           .retrieve()
                                                           .bodyToMono(String.class)
                                                           .block()) // 説明のためあえてblockしてる
                              .subscribeOn(Schedulers.elastic())
                    , 10) // concurrencyに10を指定
            .blockLast();
    }

実行ログ

02:28:06.020 [elastic-4] DEBUG o.s.w.r.f.client.ExchangeFunctions - [1b6144e3] HTTP GET http://example.com
02:28:06.020 [elastic-9] DEBUG o.s.w.r.f.client.ExchangeFunctions - [64d61eb3] HTTP GET http://example.com
02:28:06.020 [elastic-5] DEBUG o.s.w.r.f.client.ExchangeFunctions - [1b00ce18] HTTP GET http://example.com
02:28:06.020 [elastic-2] DEBUG o.s.w.r.f.client.ExchangeFunctions - [590d0628] HTTP GET http://example.com
02:28:06.021 [elastic-3] DEBUG o.s.w.r.f.client.ExchangeFunctions - [504a226f] HTTP GET http://example.com
02:28:06.021 [elastic-6] DEBUG o.s.w.r.f.client.ExchangeFunctions - [3ace12f2] HTTP GET http://example.com
02:28:06.021 [elastic-10] DEBUG o.s.w.r.f.client.ExchangeFunctions - [4135ca0a] HTTP GET http://example.com
02:28:06.021 [elastic-8] DEBUG o.s.w.r.f.client.ExchangeFunctions - [badf622] HTTP GET http://example.com
02:28:06.020 [elastic-7] DEBUG o.s.w.r.f.client.ExchangeFunctions - [2dfed701] HTTP GET http://example.com
02:28:06.021 [elastic-11] DEBUG o.s.w.r.f.client.ExchangeFunctions - [753526d8] HTTP GET http://example.com
02:28:06.673 [reactor-http-nio-8] DEBUG o.s.w.r.f.client.ExchangeFunctions - [2dfed701] Response 200 OK
02:28:06.673 [reactor-http-nio-9] DEBUG o.s.w.r.f.client.ExchangeFunctions - [504a226f] Response 200 OK
02:28:06.687 [reactor-http-nio-7] DEBUG o.s.w.r.f.client.ExchangeFunctions - [590d0628] Response 200 OK
02:28:06.757 [reactor-http-nio-6] DEBUG o.s.w.r.f.client.ExchangeFunctions - [753526d8] Response 200 OK
・
・

elastic schedulerのthreadの数が10個に制限されている。

実際には、flatMap()を呼ぶ場合にでも、内部的にconcurrencyが指定されている。ただ、その値が大きいために制限がないようなログに見えていた。

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