20200328のJavaに関する記事は5件です。

javaでAtCoder Beginner Contest 160を解く

AtCoder Beginner Contest 160お疲れ様でした!
公式ページ

今回の自分の書いたコードはこちら
結果はA-EまでACできました。久しぶりの5問ACでテンション上がります^^

以下簡単に解説します。

問題A

引数にStringで6文字のものが渡されてくるので、
String.charAt()
で指定文字のcharacterを取り出して、比較すればOKです。

問題B

金額が与えられて、それを適切に500円硬貨と5円硬貨に両替する問題です。
答えの出力が「嬉しさ」になっていて、500円硬貨をもっていると1000の嬉しさが手に入るので、与えられた金額を2倍しないように注意。
また、5円硬貨についても5の嬉しさが入るからといって、単に余った金額を足せばよいってもんではないので注意です。

long happy = (x / 500) * 1000;
x = x % 500;
happy = happy + (x / 5) * 5;

みたいな感じでACできました。

問題C

池の周りに家が建っていて、とある地点からの距離が与えられます。
全ての家をまわるのに最小の距離を求める問題です。

今回の家は池の周りに建っているので、実際に通る経路としては視力検査のアイツのような形になるはずです。
円を少し切り取ったような形。

なので、家と家の間が一番遠い距離を求めることができれば、池の外周からその距離を引くことで最小距離が算出できます。

基本的には前の家の距離を覚えておいて、入力のたびに計算でOKです。
最初の家と最後の家も隣り合っているので、忘れずに比較しましょう。

問題D

N個の頂点を持つ無向グラフで、距離がkのものはいくつあるかを出力する問題です。
無向グラフとか難しい言葉でてきてびびりますが、今回のグラフは単純です。

せいぜい直接向かうか、X-Yの間の道を通るかの2通りしかありません。

TLE起こしそうだなぁ・・・と思っても、以下を見て安心します。

3 ≤ N ≤ 2 × 10^3

コードで表現すると以下のようになります。
二重forループで、直接行くのとX-Yの間通るのとどちらが近いかを計算して、答えの配列に追加しています。

for (int i = 1; i <= n; i++) {
    for (int j = i; j <= n; j++) {
        int direct = j - i;
        int shortCut = dist(i, x) + 1 + dist(j, y);
        int dist = Math.min(direct, shortCut);
        answer[dist]++;
    }
}

distっていうメソッド以下のような感じです。

public static int dist(int p, int q) {
    return Math.max(q - p, p - q);
}

問題E

X個の赤いリンゴとY個の緑色のリンゴを食べます。
赤いリンゴのおいしさと緑色のリンゴのおいしさを与えられて、おいしさが最大になるように食べます。
無色のリンゴもあり、こいつは着色できます。

自分は以下のようなアルゴリズムで解きました。

  • 赤いリンゴと緑色のリンゴをおいしい順に並べる
  • 赤いリンゴをおいしい順にX個、緑色のリンゴをおいしい順にY個取得して配列APPLEに加える
  • 配列APPLEに無色のリンゴを全て加える
  • 配列APPLEをおいしい順に並べる
  • 配列APPLEからX+Y個所得して足し算していく

で、無事ACできました。
無色のリンゴも配列APPLEに足す発想ができたのが勝因ですかね、、よかったぁ。

個人的にはE問題の中では難易度は低めかな・・・?

問題F

5分考えて、無理だなってなったので残りの20分はぼーっとしていました笑


感想

久しぶりの水パフォ(1411)出せまして、
レーティングは936→1003と初めての1000突破です!!:blush:

E問題がちょっと簡単めなのもありましたが、なんとか解けてよかった・・・
A-EのACにはこだわりをもって安定させていきたい。これできてればそのうち水ネームなれるはず!:raised_hands:

最後まで読んでくださり、ありがとうございます!

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

【XML】気象庁から地震情報を取得する①

気象庁XMLを取得して解析までの道のり

① XMLのダウンロード(今回)
② XMLファイルの解析(次回)

気象庁からXMLファイルを取得する

ダウンロードを実行するクラスを作成する。

XMLDownloader.java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class EarthquakeXMLDownloader implements Runnable {

    // 気象庁のリンク(http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml)
    String link;

    // ダウンロードする場所
    File out;

    public EarthquakeXMLDownloader (String link, File out) {
        this.link = link;
        this.out = out;
    }

    @Override
    public void run() {

        try {
            URL url = new URL(link);
            HttpURLConnection http = (HttpURLConnection) url.openConnection();
            double fileSize = (double)http.getContentLengthLong();
            BufferedInputStream in = new BufferedInputStream(http.getInputStream());
            FileOutputStream fos = new FileOutputStream(this.out);
            BufferedOutputStream bout = new BufferedOutputStream(fos, 1024);
            byte[] buffer = new byte[1024];
            double downloaded = 0.00;
            int read = 0;
            double percentDownloaded = 0.00;

            while((read = in.read(buffer, 0, 1024))>= 0) {
                bout.write(buffer, 0, read);
                downloaded += read;
                percentDownloaded = (downloaded*100)/fileSize;
                String percent = String.format("%.4f", percentDownloaded);
                System.out.println("Downloaded " + percent + "of a file.");
            }

            bout.close();
            in.close();
            System.out.println("Download complete.");
        }catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

↑のファイルを実行する。

Main.java
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Main {

    public static void main(String[] args) {

        // アウトプットするファイルパスを作成
        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm");
        String fileName = "earthquake_" + now.format(dateTimeFormatter);
        String link = "http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml";

        File out = new File("C:\\pleiades\\workspace\\SAX\\resource\\" + fileName + ".xml");
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

        // 実行する。
        new Thread(new EarthquakeXMLDownloader(link,out)).start();

    }

}

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

java 標準関数型インターフェース

関数型インターフェースとは、抽象メソッドを1つだけもつインターフェースで、ラムダ式やメソッド参照の代入先として使われるやつです
標準関数型インターフェースについてまとめてみました

標準関数型インターフェース

標準関数型インターフェースとして、典型的なメソッドの型がjava.util.functionパッケージで定義されている

基本的な標準関数型インターフェース

インターフェース メソッド 説明
Function <T, R> R apply (T t) T型の引数を受け取って、R型の結果を返す
Consumer <T> void accept (T t) T型の引数を受け取って、結果は返さない
Predicate <T> boolean test(T t) T型の引数を受け取って、boolean値の結果を返す
Supplier <T> T get() 引数無し、T型の結果を返す

サンプルコード

Sample.java
        // Function<T, R>
        // 例:引数の文字列長を返す
        Function<String, Integer> function = String::length;
        System.out.println(function.apply("12345")); // 5

        // Consumer<T>
        // 例:引数の文字列長を出力
        Consumer<String> consumer = str -> System.out.println(str.length());
        consumer.accept("12345"); // 5

        // Predicate<T>
        // 例:引数の文字列に"foo"が含まれているか判定
        Predicate<String> predicate = str -> Objects.nonNull(str) && str.contains("foo");
        System.out.println(predicate.test("foo bar")); // true

        // Supplier<T>
        // 例:"Sample"を返す
        Supplier<String> supplier = () -> "Sample";
        System.out.println(supplier.get()); // Sample

関数の合成

標準関数型インターフェースを合成して新しい関数型インターフェースを作成できる
composeandThenを使って合成する(composeandThenの違いは合成の順序)
Predicate系はandornegateを使って合成できる

サンプルコード

Sample.java
        // Functionの関数合成
        Function<String, Integer> function1 = String::length;
        Function<Integer, Integer> function2 = i -> i * 100;
        // composeを使う場合
        Function<String, Integer> newFunction1 = function2.compose(function1);
        // andThenを使う場合
        Function<String, Integer> newFunction2 = function1.andThen(function2);
        System.out.println(newFunction1.apply("12345")); // 500
        System.out.println(newFunction2.apply("12345")); // 500

        // Predicateの関数合成
        Predicate<Integer> predicate1 = i -> i % 2 == 0; // 偶数かどうか判定
        Predicate<Integer> predicate2 = i -> i >= 100; // 100以上どうか判定
        // 偶数かつ100以上かを判定
        Predicate<Integer> newPredicate1 = predicate1.and(predicate2);
        // 奇数かつ100未満かを判定(negate()は論理否定)
        Predicate<Integer> newPredicate2 = predicate1.negate().and(predicate2.negate());
        System.out.println(newPredicate1.test(100)); // true
        System.out.println(newPredicate2.test(99)); // true
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリケーション間でRequestIdを送りあってみる

やりたいこと

サービスが複数またがってている場合、運用時にサービス間を跨いだログを見たくなる時があるので、その方法をかく。
順番は
1. Spring BootでRequestIdを生成して、ログファイルに出しておく
2. 他アプリケーション(今回はGin)に対して、リクエストを投げる
3. Gin側でRequestIdを受け取る

ソース
Spring Boot
Gin

環境

  • Java
  • Spring Boot
  • Go
  • Gin

Spring Boot側にRequestIdの設定を入れる

ログファイルにRequestIdが表示されるようにする

MDCを使用して、リクエスト毎にUUIDを生成して、ログに仕込めるようにしておく

SampleFilter.java
import org.slf4j.MDC;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;

public class SampleFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String requestIdKey = "X-REQUEST-ID";
        String uuid = UUID.randomUUID().toString();

        // どこからでもリクエストIDが取得できるように設定しておく
        request.setAttribute(requestIdKey, uuid);

        try {
            // ログに出力できるように設定
            MDC.put(requestIdKey, uuid);
            filterChain.doFilter(request, response);

        } finally {
            MDC.remove(requestIdKey);
        }
    }
}

Filterを使えるようにBeanに登録する

/api配下にフィルターがかかるように設定する

SampleConfiguration.java
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SampleConfiguration {

    @Bean
    public FilterRegistrationBean<SampleFilter> hogeFilter() {
        FilterRegistrationBean<SampleFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new SampleFilter());
        bean.addUrlPatterns("/api/*");
        bean.setOrder(Integer.MIN_VALUE);
        return bean;
    }
}

Logbackの設定

SampleFilter.javaで定義したX-REQUEST-ID<pattern>にも設定する

logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />

    <springProperty name="loggingFilePath" source="logging.file.path" />

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${loggingFilePath}/spring.log</file>
        <encoder>
            <pattern>[%-5level] [%X{X-REQUEST-ID}] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%logger{36}] - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

今回はINFO以上のレベルしか出さないようにする

application.properties
logging.file.path=logs/
logging.level.org.springframework=INFO

リクエストを投げてログを確認

http://localhost:8080/api/にGETのエンドポイントを作っておく

SampleController.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api")
public class SampleController {

    private static final Logger logger = LoggerFactory.getLogger(SampleController.class);

    @GetMapping
    public String get() {
        logger.info("info!");
        return "get";
    }
}

リクエストの結果のログファイル

spring.log
[INFO ] [2389c607-f5ed-4789-95a3-efd78be1e8d9] [2020-XX-XX 23:26:25.536] [c.e.log.logdemo.SampleController] - info!

2389c607-f5ed-4789-95a3-efd78be1e8d9が生成したUUID
ちゃんと出てるっぽいね

RestTemplateで他サービスにリクエストする時にRequestIdを付与する

RestTemplateを使えるようにBeanに登録しておく
(特にここで定義する必要はなかったですが楽だったので)

LogDemoApplication.java
@SpringBootApplication
public class LogDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDemoApplication.class, args);
    }

    @Bean
    public RestTemplate setRestTemplate(){
        return new RestTemplate();
    }
}

他アプリケーションに投げるためのエンドポイントを用意しておく

SampleController.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@RestController
@RequestMapping("api")
public class SampleController {

    private static final Logger logger = LoggerFactory.getLogger(SampleController.class);

    // 追記
    RestTemplate restTemplate;

    // 追記
    public SampleController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @GetMapping
    public String get() {
        logger.info("info!");
        return "get";
    }

    // 追記
    @GetMapping("to-other-app")
    public String toOtherApp() {

        String requestId = (String) RequestContextHolder
                .getRequestAttributes()
                .getAttribute("X-REQUEST-ID", RequestAttributes.SCOPE_REQUEST);

        logger.info("他のアプリケーションにリクエスト投げます!");

        HttpHeaders headers = new HttpHeaders();
        headers.set("X-REQUEST-ID", requestId);
        HttpEntity<String> entity = new HttpEntity<>("headers", headers);
        ResponseEntity<OtherAppResponse> response = restTemplate.exchange("http://localhost/api", HttpMethod.GET, entity, OtherAppResponse.class);

        return response.getBody().getValue();
    }
}

これでSpring Boot側の設定は完了です!

Gin側のアプリケーションを作成する

別で書いたの記事のものを流用します

docker-compose up -dを叩くだけなので、簡単
http://localhost:80/apiにアクセスすると、 {"key": "value"}が返ってくるだけのAPIがあります

お互いのログを確認

Spring Bootはhttp://localhost:8080/
Giはhttp://localhost:80/
で起動します

Spring Boot側でリクエスト

http://localhost:8080/api/to-other-appにアクセス

spring.log
[INFO ] [549ef61a-44c9-4fe4-9c0f-3d923976d32f] [2020-XX-XX 00:19:00.274] [c.e.log.logdemo.SampleController] - 他のアプリケーションにリクエスト投げます!

ログが見えました
リクエストIDは 549ef61a-44c9-4fe4-9c0f-3d923976d32f

Gin側のログファイルを確認

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /api                      --> main.main.func1 (4 handlers)
[GIN-debug] Listening and serving HTTP on :3000
[GIN] 2020/XX/XX - 15:16:44 | 200 |       1.005ms |      172.19.0.3 | GET      "/api"
{"time":"2020-XX-XXT15:19:00.3482677Z","level":"-","prefix":"-","file":"main.go","line":"30","message":"RequestId=549ef61a-44c9-4fe4-9c0f-3d923976d32f"}

送られてきたリクエストIDは549ef61a-44c9-4fe4-9c0f-3d923976d32f
どうやら一致したようですね(日付はぼかしてます)

まとめ

さらっとやりましたが、Spring BootでリクエストIDをどこでも使えるようにするだとか、Ginでアプリケーション作るだとかに時間がかかりました。Spring Boot側のRequestContextHolderとか、「これでええんかな...」感はありますが、他にやり方が浮かばなかったので、もし知っていたら教えていただけるととても嬉しいです。Gin側でSpring Bootに返すのはHeader設定するだけで簡単っぽいので割愛しました。なんでGinなのか、ギョームではSpring Boot + NodeJSで実現してますが、最近Goに興味が湧いたので(Go楽しい)。
分かり辛いところ、もっと効率のいいやり方、アドバイスあればぜひコメントください

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

新人研修でやった三角形の面積を求める実装を今やってみる

プログラミングを始めて2年が経った

新卒で入社した会社のJava研修で初めてプログラミングをしてからもうすぐ2年が経ちます。最近は情報系の学生さんとも話す機会が増え、すごい人がたくさんいるんだなーと実感することが多いです。
自分が新人研修でやっていたことなんて大したことなかったなーと振り返りつつ疑問が…

あの時俺がやっていたのはオブジェクト指向?

研修の課題の一つに三角形の面積を求めなさいというものがありました。
趣旨としては手続き型的に書くのではなく、クラスを使って実装しようというものでした。
そこで、私が考えた答えは⬇️こんな感じだったと思います。

class Triangle {
  int bottom;
  int height;

  public Triangle(int arg1, int arg2) {
    this.bottom = arg1;
    this.height = arg2;
  }

  public int calc() {
    return this.bottom * this.height / 2;
  }
}

確か模範解答もこんな感んじだった気がします。
たぶん申し分ない。(申し分ある)





ちょっと待てよ:thinking: このクラスが表現している三角形ってこんな感じか?
スクリーンショット 2020-03-27 16.52.11.png

ましてやこんな感じか?
スクリーンショット 2020-03-27 16.48.18.png

うーん、、三角形ではない、、、、






けどいいんです!!!その時の我々が底辺と高さの組み合わせを三角形と呼びますと定義していればきっといいんです!たぶん

だがしかし、その時のテキストにはしっかり三角形の図が載っていたのできっと作成者側も△こういうのを考えているはず。
とういうことで、

あの時の課題をオブジェクト指向で再挑戦してみる

オブジェクト指向はかじってる最中です。不足、誤り等あればご指摘ください。

三角形を定義する

課題として与えられる情報は底辺高さだけなので、この2つを辺として三角形は直角三角形であるとします。(させて下さい)

実装してみる

public class RightTriangle {
    private Set<Side> sideSet;

    public RightTriangle(Side bottom, Side height) {
        assert !bottom.isHypotenuse();
        assert !height.isHypotenuse();

        sideSet = Set.of(bottom, height, calcHypotenuse(bottom, height));
    }

    private Side calcHypotenuse(Side bottom, Side height) {
        return Side.createHypotenuse(Math.hypot(bottom.length(), height.length()));
    }

    public double area() {
        return sideSet.stream()
          .filter(s -> !s.isHypotenuse())
          .map(Side::length)
          .reduce(1.0, (s1, s2) -> s1 * s2) / 2;
    }
}

public class Side {
    private boolean isHypotenuse;
    private double value;

    private Side(double value, boolean isHypotenuse) {
        assert value > 0;
        this.value = value;
        this.isHypotenuse = isHypotenuse;
    }

    public static Side createSide(double value) {
        return new Side(value, false);
    }

    public static Side createHypotenuse(double value) {
        return new Side(value, true);
    }

    public boolean isHypotenuse() { return this.isHypotenuse; }

    public double length() { return this.value; }
}

三角形クラスを初期化時に2辺から斜辺を求めるようにしてみた。これでイメージする三角形になると思う。
辺は値オブジェクトっぽく表現して、斜辺の状態を持たせてみた。
三角形クラスに面積を問い合わせると、斜辺じゃない2辺から計算して返してくれるようにした。

辺の長さはLengthクラスとして切り出してもよかったかもしれない。
三角形クラス、辺クラスの初期化処理はイケてないかもしれない。
そもそもいろいろとイケてないかもしれない。
これが今の精一杯…また2年後挑戦しよう:rice_ball:

ご指摘あればお願いします:bow_tone1:オブジェクト指向を理解したい

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