20200324のJavaに関する記事は8件です。

JavaアプリをDockerイメージにするならjibを使うと便利だよ

メリットの多いDocker

コンテナ化技術の一つであるDockerですが、使ってみると数多くのメリットがあります。
実際のメリットについてはネットで検索すると色々出てくるので、この記事ではDockerそのものに関する解説はいたしません。
ただ個人的に強調しておきたいのは、本番環境などへ直接Javaのモジュールやアーカイブなどを配置するようなデプロイしている場合、Dockerイメージをリポジトリにpushするという作業に変えることでたくさんの恩恵を得ることができると思います。

Dockerの面倒さ

メリットの多いDockerですが面倒な面もあります。
Dockerfileを書かないといけない。書くためのコマンドやベストプラクティスも学習するとなるとそれなりに面倒です。
ローカル開発環境がWindowsの場合にDocker環境を構築するのも面倒です。

jibが解決

もしDockerコンテナ上で動くアプリケーションがJavaで開発されている場合、Googleが提供しているjibを利用することでDockerにまつわるいくつかの面倒さから解放されます。

jibを利用するにあたって必要なのは(gradleを使っている場合) build.gradle

plugins {
  id 'com.google.cloud.tools.jib' version '2.1.0'
}

という設定だけです。

そして仮に docker-image-to-push/1.0.0 というイメージ名でpushしたい場合は

jib.to.image = 'docker-image-to-push/1.0.0'

build.gradle に記述するだけです。
これで gradle jib と実行すればDockerイメージが作成されpushされます。
驚くべきはたったこれだけの設定でいいことと、ローカルにDocker環境が必要でないことです。
もしDocker Hubにアカウントを持っているなら

jib.to {
  auth {
    username 'account'
    password 'pass'
  }
  image 'account/repository:1.0.0'
}

のように build.gradle に設定することですぐにjibを試すことができます。
(account, pass, repositoryの部分は環境に合わせて変更してください)

上記の例でわかるようにほとんど設定のいらないjibですが、色々設定することも可能です。
まず設定しておいてほしいのは(JavaのソースファイルのエンコーディングがUTF-8の場合)

jib.container.environment = [JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8']

という設定です。
これがないとソースファイル中に日本語がある場合にエラーになってしまいます。
設定できる内容については configurationから確認できます。

以上、jibの紹介でした。

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

【Java】日付から差分を算出したい

開始日付、終了日付をもとに処理時間を算出したかった。

date.java
import java.text.SimpleDateFormat;
import java.util.Date;

public class date {

    public static void main(String[] args) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date start = null;
        Date end = null;

        // Date型に変換
        try {
            start = sdf.parse("2020/03/23 12:30:12");
            end = sdf.parse("2020/03/23 12:31:12");
        } catch (java.text.ParseException e) {
            e.printStackTrace();
        }

        long timeStart = start.getTime();
        long timeEnd = end.getTime();
        long processTime = timeEnd - timeStart;

        // ミリ秒
        System.out.println(processTime);
    }
}

実行結果:

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

spring-batchでメタデータとstepで異なるTransactionManagerを使う

spring-batchで2つ以上のデータソースを使う場合、step定義作成時にStepBuilderHelper#transactionManagerでトランザクションマネージャを指定する。

以下は設定例。

import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.batch.BatchDataSource;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@SpringBootApplication
@EnableBatchProcessing
public class Application {
    @Bean
    @BatchDataSource
    public DataSource springBatchDs() {
        // 1
        return DataSourceBuilder
                .create()
                .url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
                .username("sa")
                .password("")
                .build();
    }

    @Bean
    @Primary
    public DataSource primaryDs() {
        // 2
        return DataSourceBuilder
                .create()
                .url("jdbc:oracle:thin:system/oracle@localhost:11521/XEPDB1")
                .username("system")
                .password("oracle")
                .build();
    }


    @Bean
    public Job job(JobBuilderFactory jobs, Step s1) {
        return jobs.get("myJob")
                .incrementer(new RunIdIncrementer())
                .start(s1)
                .build();
    }

    @Bean("stepTransactionManager")
    public PlatformTransactionManager stepTransactionManager(DataSource primaryDs) {
        // 3
        return new DataSourceTransactionManager(primaryDs);
    }

    @Bean
    public Step step1(StepBuilderFactory steps
            ,DataSource dataSource
            // 4
            ,@Qualifier("stepTransactionManager") PlatformTransactionManager transactionManager) {
        ItemReader<Integer> reader = new ListItemReader<Integer>(
                IntStream.range(1, 1001).boxed().collect(Collectors.toList()));

        JdbcTemplate jdbc = new JdbcTemplate(dataSource);

        TaskletStep build = steps.get("step1")
                // 5
                .transactionManager(transactionManager)
                .<Integer, Integer>chunk(10)
                .reader(reader)
                .writer(list -> {
                    list.forEach(s -> {
                        jdbc.update("update aaa set user_id = user_id + 1");
                    });
                })
                .build();
        return build;
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
    }
}
  1. サンプルとして、メタデータの保存先はH2を使用する。終了時に消えるので、事実上メタデータは永続化しない。
  2. メインのデータソース。
  3. step処理用のトランザクションマネージャ。メインのデータソースを使用する。メインのデータソースに@Primaryがついてるので、このメソッドの引数にはそちらのデータソースが来る。
  4. @Qualifierで上で作成したトランザクションマネージャを得る。@Qualifierが無いと、spring-batchのSimpleBatchConfigurationが内部的に作成するtransactionManagerが入ってくる。
  5. step定義作成時に上で作成したトランザクションマネージャを指定する。これでメタデータ処理とstep処理とで異なるトランザクションマネージャが使用される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】 Broadcast Recieverでintentを受け取る

Broadcast Recieverとは?

  • BroadcastReceiver(ブロードキャストレシーバー)とは、ブロードキャストしたインテントを受け取る仕組みのこと
  • OSのAndroidシステム側でブロードキャストされるイベント(スクリーンのON/OFFなど)以外でも、自分で作成したアプリでも独自のインテントを生成してブロードキャストできる

intentとは?

  • intentとは、アプリケーションの中の1つ1つの機能のこと。たとえばアプリケーション同士や、アプリケーションとウィジェット、アプリケーションとシステムを橋渡しする仕組みのこと
  • BroadcastRecieverを使わずとも、intentだけでも利用が可能で、例えば電話をかけたりカメラを起動することもできる
  • BroadcastRecieverでは、例えばアプリをインストールした際などに発生するintentを検知することができます。例えば...
    • スクリーンのON/OFFを検知する
    • 電池の状態を検知する
    • アプリのインストールを検知する
    • 振動を検知する 等

使ってみよう

アクティビティからサービスを起動 → サービスでレシーバーを登録 という流れでやってみました

MyReceiver.java
public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction() != null) {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
               Log.d("registerReceiver: ", "ON");
            }
            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                Log.d("registerReceiver: ", "OFF");
            }
        }
    }
}
MyActivity.java
public class MyActivity extends Activity {

    Button startButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        startButton = (Button)findViewById(R.id.start_button);
        startButton.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                getActivity().startForegroundService(new Intent(getActivity(), MentalHealthService.class));
            } else {
                getActivity().startService(new Intent(getActivity(), MentalHealthService.class));
            }
        }
    }
}
MyService.java
public class MyService extends Service {

    private MyReceiver mReceiver;
    private IntentFilter mIntentFilter;

    @Override
    public void onCreate() {
        super.onCreate();
        // Receiver登録を実行
        registerScreenReceiver();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Receiverを解除 
        unregisterReceiver(mReceiver);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startInForeground();
        return START_STICKY;
    }

    // receiverを登録
    private void registerScreenReceiver() {
        mReceiver = new MyReceiver();
        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
        mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(mReceiver, mIntentFilter);
    }

    // サービスをアライブさせるために通知を生成
    private void startInForeground() {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle(getResources().getString(R.string.app_name))
                .setContentText("test notification")
                .setTicker(getResources().getString(R.string.app_name))
                .setContentIntent(pendingIntent);
        Notification notification = builder.build();

        if (Build.VERSION.SDK_INT >= 26) {
            NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID
                    , NOTIFICATION_CHANNEL_NAME
                    , NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription(NOTIFICATION_CHANNEL_DESC);

            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
        startForeground(NOTIFICATION_ID, notification);
    }
}

解説

  • MyReceiverのonReceiveにて、intent.getAction()が登録したintentFilterのアクションと合致したらログを出す様に処理
  • MyActivityにボタンを設置。ボタンを押すとサービスを起動するという単純な構造
  • MyService内でReceiverの登録を行なっている。startInForegroundはForegroundService用でとりあえず実装(あまり気にしなくていいです)
  • サービスが起動している間に、スクリーンをOn/Offにしたらその度にログが出る

備考

  • 最ライフログやヘルスケアのアプリをよく見るので、どんな風にデータを集計しているのか気になったところ、intentを使っているパターンが多い様に感じたので調査してみた
  • 今回紹介した以外にも、intentはものすごく沢山あるので、アプリによっては有用なものもある...かもしれない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AtCoder Beginner Contest 152 C問題】TLEする人の書き方と、printデバッグを入れる位置のコツ

printデバッグを入れる位置で混乱していませんか?

お久しぶりです。宇宙意志です。

今回はいつものように書き捨てのSeleniumではなく、ABC152-C問題を見ていきたいと思います。別にリングフィット購入の自動転売で大儲けしようとしたのにLambdaが動かなくてキレていた訳ではないです。

今回の趣旨ですが考え方とデバッグの位置を說明です、まだTLE(時間切れ)で解けていません。

問題の概要

1.N個の順列がバラバラに並んでいます
2.前からiを走査させます
3.走査中のiよりも後ろにある順列全て(ここではjと定義されてます)と比較して、j!>=i! が常に成立するならi1つに付き1カウント
4.条件が成立するiの合計カウントを求める

という問題です。

解法の考え方

1.まずこの問題を聞いた時に、2重for文を使って配列のiの位置を記憶させて、jを配列として左端からiの位置まで走査すれば良いな
というのはすぐに思いつくと思います。

2.順列の並べ替え
 ここもすぐに気付かなければなりません。この問題はタイムアウトを狙っています。ですから、
 順列は常に n >= m の時常に n! >= m! が成立するので、n>mの確認をすれば順列を計算する必要はないと分かります。
 これで問題は単なる大小比較になりました。ここで順列の計算を始めると大量にTLE(時間切れ)が出てしまいます。

 また、j用の配列を左端からiまでを走査する時、途中で条件不成立ならbreakさせよう。(TLEを避けるため)というのも思いつきます。

3.いつカウントを加えればいいの?
 これについて考えなければなりません。
 jの配列を全て比較し終わった時に条件不成立ならcount++したい。という事は、j用のfor分をぐるぐる回してバターになっている
 時はcountはいじれない。最後に加えたい。という事は、フラグ(良い名前をつけましょう)を使って保持して、for文が終わった時点で
 count++するかしないか判定すれば良い。

 
 ここまで分かれば解けたも同然です。
 解けたも同然というのは内心的効果意思であって、解けたという事実を示したものでありません。

以下、コメントを入れたソースを見ていきましょう。

import java.io.*;
import java.util.*;

public class Main {

    void submit() {

        int N = nextInt();
        int[] P = new int[N];

        for(int i=0; i<N; i++){
            P[i] = nextInt();
        }

上は入力値を入れてるだけですね。順列にする必要がないのは上記で説明した通りです。

        int total = 1;

        if(N==1){
            out.println(total);
            return;
        }

最初に与えられた総数が1の時は特別で、i,jと2重配列を作りようがないのです。
こういう所で無理してネストを増やしてがんばってはいけません。
ソースを見やすくします。最初に貰った数値が1つだけの時はreturnして終了させて、ネストを減らします。

        for(int i=1; i<N; i++){
            /*** big_flgとかいう分かりにくい名前はやめて、リーダブルコードを読みましょう!!! ***/
            boolean big_flg= true;

            /*** jを走査しています。i以下を比較する配列なので、初期位置は int j=0になります ***/
            for(int j=0; j<=i; j++){

                /*** 今回見て欲しいデバッグ部分1です。比較対象になる整数を全て出力しています。 ***/
                out.println("P[i]: "+ P[i]);
                out.println("P[j]: "+ P[j]);

                if(P[i]<=P[j]){
                    big_flg = true;
                }else{
                    big_flg = false;
                    /*** 計算量を減らすために、駄目だと分かり次第breakして2重ループを抜けて次のiに進みます。
               ちなみにcontinueも処理とネスト減らしの為によく使います ***/
                    break;
                }
            }

大体コメントに書いたのですが、for文の2重配列で比較しているだけです。で、問題は、「エラーが出た時どうすれば良いのかわからないよドラえもん!」という方です。

今回記載しているように、常にデバッグ値は急いでいても、「"名前:数値 」という形で出してあげましょう。

out.println("P[i]: "+ P[i]);
out.println("P[j]: "+ P[j]);

このように書く事で、いわゆるprintデバッグの精度を上げる事が出来ます。

            /*** 今回見て欲しいデバッグ部分2です。結局出てきた時のフラグはどっちなの? 成立? 不成立?を見ています ***/
            out.println("big_flg: "+ big_flg);                    

            if(big_flg){
                total++;
            }

        }
        out.println(total);
    }

で、のび太さんはまたデバッグする時に__どこでデバッグするの? どの位置でフラグの値を知りたいの?という事が重要です。

これに関しては一言でいうと、if文の前に入れましょう。大体if文に入る前に間違ってます。

printデバッグはどこに入れれば良いのか

二重for文で実装時に間違えるケースはそんなに多くなくて、

1.forの境界値で間違えてる
2.if文の条件が間違えてる
3.if文に入るフラグが間違えてる

の3つです。1.2は余程複雑な条件でなければすぐに気付きます。そもそも複雑な条件は作ってはいけません。
だから3です。C問題が解けない方は、3のデバッグの仕方を鍛えましょう、と言っています。

以下はトップコーダーのパクリなのでおまじない。気にしません。わたしのソースにSystem.out.println()ではなく、
out.println()で出力しているのはこのおまじないによります。

void preCalc() {

}

void stress() {

}

void test() {

}

Main() throws IOException {
    br = new BufferedReader(new InputStreamReader(System.in));
    out = new PrintWriter(System.out);
    preCalc();
    submit();
    //stress();
    //test();
    out.close();
}

static final Random rng = new Random();

static int rand(int l, int r) {
    return l + rng.nextInt(r - l + 1);
}

public static void main(String[] args) throws IOException {
    new Main();
}

BufferedReader br;
PrintWriter out;
StringTokenizer st;

String nextToken() {
    while (st == null || !st.hasMoreTokens()) {
        try {
            st = new StringTokenizer(br.readLine());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    return st.nextToken();
}

String nextString() {
    try {
        return br.readLine();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

int nextInt() {
    return Integer.parseInt(nextToken());
}

long nextLong() {
    return Long.parseLong(nextToken());
}

double nextDouble() {
    return Double.parseDouble(nextToken());
}

}
```

まとめたソースは以下。

ちなみにこれでもTLEになりますた? ボスケテ

import java.io.*;
import java.util.*;

public class Main {

    void submit() {

        int N = nextInt();
        int[] P = new int[N];

        for(int i=0; i<N; i++){
            P[i] = nextInt();
        }

        int total = 1;

        if(N==1){
            out.println(total);
            return;
        }

        for(int i=1; i<N; i++){
            boolean big_flg= true;
            for(int j=0; j<=i; j++){
                if(P[i]<=P[j]){
                    //out.println("P[i]: "+ P[i]);
                    //out.println("P[j]: "+ P[j]);
                    big_flg = true;
                }else{
                    big_flg = false;
                    break;
                }
            }
            //out.println("big_flg: "+ big_flg);                    
            if(big_flg){
                total++;
            }
        }
        out.println(total);
    }

    void preCalc() {

    }

    void stress() {

    }

    void test() {

    }

    Main() throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(System.out);
        preCalc();
        submit();
        //stress();
        //test();
        out.close();
    }

    static final Random rng = new Random();

    static int rand(int l, int r) {
        return l + rng.nextInt(r - l + 1);
    }

    public static void main(String[] args) throws IOException {
        new Main();
    }

    BufferedReader br;
    PrintWriter out;
    StringTokenizer st;

    String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return st.nextToken();
    }

    String nextString() {
        try {
            return br.readLine();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    int nextInt() {
        return Integer.parseInt(nextToken());
    }

    long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }
}

という訳で、今回は以上です。

最期に

java8で回答された優秀な方の解法がたんまりありますので、table右側の詳細で見てみて下さい。
https://atcoder.jp/contests/abc152/submissions?f.Task=abc152_c&f.Language=3016&f.Status=AC&f.User=

今回は以上になります。
・デバッグの位置に気を遣おうね
・名前は表示しようね、
という2点でした。

それでは、また会いましょう。

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

【AtCoder Beginner Contest 152 C問題】TLEについてと、printデバッグ位置のコツ

printデバッグを入れる位置で混乱していませんか?

お久しぶりです。宇宙意志です。

今回はいつものように書き捨てのSeleniumではなく、ABC152-C問題を見ていきたいと思います。別にリングフィット購入の自動転売で大儲けしようとしたのにLambdaが動かなくてキレていた訳ではないです。

今回の趣旨ですが考え方とデバッグの位置を說明です、まだTLE(時間切れ)で解けていません。

問題の概要

1.N個の順列がバラバラに並んでいます
2.前からiを走査させます
3.走査中のiよりも手前にある順列全て(ここではjと定義されてます)と比較して、j!>=i! j>=iが常に成立するならi 1つに付き1カウント
4.条件が成立するiの合計カウントを求める

という問題です。

解法の考え方

1.まずこの問題を聞いた時に、2重for文を使って配列のiの位置を記憶させて、
 jを配列として左端からiの位置まで走査すれば良いな
、というのはすぐに思いつくと思います。

2.順列の並べ替え
 ここもすぐに気付かなければなりません。この問題はタイムアウトを狙っています。ですから、
 順列は常に n >= m の時常に n! >= m! が成立するので、n>mの確認をすれば順列を計算する必要はないと分かります。
 これで問題は単なる大小比較になりました。ここで順列の計算を始めると大量にTLE(時間切れ)が出てしまいます。

 @mui_nyan さまからコメントでご指摘が合ったためyoutubeの解説動画で確認しました。順列という言葉に
 特に意味はなく、普通の順番のランダムの数値という事のようです。申し訳ございませんでした。
 (2020/03/24 15:56 修正)

 また、j用の配列を左端からiまでを走査する時、途中で条件不成立ならbreakさせよう。(TLEを避けるため)
 というのも思いつきます。

3.いつカウントを加えればいいの?
 これについて考えなければなりません。
 jの配列を全て比較し終わった時に条件不成立ならcount++したい。という事は、
 j用のfor分をぐるぐる回してバターになっている時はcountはいじれない。
 最後に加えたい。という事は、フラグ(良い名前をつけましょう)を使って保持して、
 for文が終わった時点でcount++するかしないか判定すれば良い。

 
 ここまで分かれば解けたも同然です。
 解けたも同然というのは内心的効果意思であって、解けたという事実を示したものでありません。(民法)

以下、コメントを入れたソースを見ていきましょう。

import java.io.*;
import java.util.*;

public class Main {

    void submit() {

        int N = nextInt();
        int[] P = new int[N];

        for(int i=0; i<N; i++){
            P[i] = nextInt();
        }

上は入力値を入れてるだけですね。順列にする必要がないのは上記で説明した通りです。

        int total = 1;

        if(N==1){
            out.println(total);
            return;
        }

最初に与えられた総数が1の時は特別で、i,jと2重配列を作りようがないのです。
こういう所で無理してネストを増やしてがんばってはいけません。
ソースを見やすくします。最初に貰った数値が1つだけの時はreturnして終了させて、ネストを減らします。

※以下のソースコードは解法の追記で書き直しています

        for(int i=1; i<N; i++){
            /*** big_flgとかいう分かりにくい名前はやめて、リーダブルコードを読みましょう!!! ***/
            boolean big_flg= true;

            /*** jを走査しています。i以下を比較する配列なので、初期位置は int j=0になります ***/
            for(int j=0; j<=i; j++){

                /*** 今回見て欲しいデバッグ部分1です。比較対象になる整数を全て出力しています。 ***/
                out.println("P[i]: "+ P[i]);
                out.println("P[j]: "+ P[j]);

                if(P[i]<=P[j]){
                    big_flg = true;
                }else{
                    big_flg = false;
                    /*** 計算量を減らすために、駄目だと分かり次第breakして2重ループを抜けて次のiに進みます。
               ちなみにcontinueも処理とネスト減らしの為によく使います ***/
                    break;
                }
            }

大体コメントに書いたのですが、for文の2重配列で比較しているだけです。で、問題は、「エラーが出た時どうすれば良いのかわからないよドラえもん!」という方です。

今回記載しているように、常にデバッグ値は急いでいても、「"名前:数値 」という形で出してあげましょう。

out.println("P[i]: "+ P[i]);
out.println("P[j]: "+ P[j]);

このように書く事で、いわゆるprintデバッグの精度を上げる事が出来ます。

            /*** 今回見て欲しいデバッグ部分2です。結局出てきた時のフラグはどっちなの? 成立? 不成立?を見ています ***/
            out.println("big_flg: "+ big_flg);                    

            if(big_flg){
                total++;
            }

        }
        out.println(total);
    }

で、のび太さんはまたデバッグする時にどこでデバッグするの? どの位置でフラグの値を知りたいの?という事が重要です。
これに関しては一言でいうと、if文の前に入れましょう。大体if文に入る前に間違ってます。

printデバッグはどこに入れれば良いのか

二重for文で実装時に間違えるケースはそんなに多くなくて、

1.forの境界値で間違えてる
2.if文の条件が間違えてる
3.if文に入るフラグが間違えてる

の3つです。1.2は余程複雑な条件でなければすぐに気付きます。
そもそも複雑な条件は作ってはいけません。
だから3です。C問題が解けない方は、3のデバッグの仕方を鍛えましょう、と言っています。

以下はトップコーダーのパクリなのでおまじない。気にしません。わたしのソースにSystem.out.println()ではなく、
out.println()で出力しているのはこのおまじないによります。

    void preCalc() {

    }

    void stress() {

    }

    void test() {

    }

    Main() throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(System.out);
        preCalc();
        submit();
        //stress();
        //test();
        out.close();
    }

    static final Random rng = new Random();

    static int rand(int l, int r) {
        return l + rng.nextInt(r - l + 1);
    }

    public static void main(String[] args) throws IOException {
        new Main();
    }

    BufferedReader br;
    PrintWriter out;
    StringTokenizer st;

    String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return st.nextToken();
    }

    String nextString() {
        try {
            return br.readLine();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    int nextInt() {
        return Integer.parseInt(nextToken());
    }

    long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }
}

まとめたソースは以下。

ちなみにこれでもTLEになりますた? ボスケテ

abc152_c.java
import java.io.*;
import java.util.*;

public class Main {

    void submit() {

        int N = nextInt();
        int[] P = new int[N];

        for(int i=0; i<N; i++){
            P[i] = nextInt();
        }

        int total = 1;

        if(N==1){
            out.println(total);
            return;
        }

        for(int i=1; i<N; i++){
            boolean big_flg= true;
            for(int j=0; j<=i; j++){
                //out.println("P[i]: "+ P[i]);
                //out.println("P[j]: "+ P[j]);
                if(P[i]<=P[j]){
                    big_flg = true;
                }else{
                    big_flg = false;
                    break;
                }
            }
            //out.println("big_flg: "+ big_flg);                    
            if(big_flg){
                total++;
            }
        }
        out.println(total);
    }

    void preCalc() {

    }

    void stress() {

    }

    void test() {

    }

    Main() throws IOException {
        br = new BufferedReader(new InputStreamReader(System.in));
        out = new PrintWriter(System.out);
        preCalc();
        submit();
        //stress();
        //test();
        out.close();
    }

    static final Random rng = new Random();

    static int rand(int l, int r) {
        return l + rng.nextInt(r - l + 1);
    }

    public static void main(String[] args) throws IOException {
        new Main();
    }

    BufferedReader br;
    PrintWriter out;
    StringTokenizer st;

    String nextToken() {
        while (st == null || !st.hasMoreTokens()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return st.nextToken();
    }

    String nextString() {
        try {
            return br.readLine();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    int nextInt() {
        return Integer.parseInt(nextToken());
    }

    long nextLong() {
        return Long.parseLong(nextToken());
    }

    double nextDouble() {
        return Double.parseDouble(nextToken());
    }
}

という訳で、今回は以上です。

最期に

java8で回答された優秀な方の解法がたんまりありますので、table右側の詳細で見てみて下さい。
https://atcoder.jp/contests/abc152/submissions?f.Task=abc152_c&f.Language=3016&f.Status=AC&f.User=

今回は以上になります。
・デバッグの位置に気を遣おうね
・名前は表示しようね

という2点でした。

それでは、また会いましょう。

解法の追記(2020/03/24 15:57)

数時間ぶりにお久しぶりです。宇宙意志です。
@swordone さまより (i番目までの最小値)>(i+1番目の値)の個数を数えれば済む というアドバイスを頂きましたので、
解説動画も見て確認しました。上記の手法だとTLEしないという事です。という事で実装しました。

min を保持して、そこから走査すれば計算量が減らせるという事です。以下のようになります。

        int min=P[0];
        for(int i=1; i<N; i++){
            if(min >= P[i]){
                min = P[i];
                count++;
            }
        }
        out.println(count);
    }

大分スッキリしましたね! これがパッと思いつけば良いんですが、本番では工夫せずに素直に実装してTLEを出してしまうのが筆者の実力という事でしょうか……ぴえん?

という事で、コメント頂いたお二人の方、ありがとうございました!

追々記(2020/03/24 20:30)

java用にソースのシンタックスハイライト(今知った)を修正して頂きました! @altさま、ありがとうございます!?

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

java bronze silver合格

大分前ですがOracle認定のJava bronzeとsilverのSE8合格しました。
bronzeもsilverも対策は一緒なのでひとまとめに。

勉強時間の目安(bronze)

  1. プログラミング未経験:50h
  2. 何らかのプログラミング言語を習得している:30h
  3. java経験者:10h

いきなりsilverを受験する場合は+10h程度

参考資料

黒本白本

その他

受験するのにピアソンVUEとoracleの登録が必要になるので先に登録しておくことをオススメします。
結構面倒でした。
bronzeは自宅で受験可能です。

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

GCPでJava版Minecraftサーバを建てた話(ついでにホワイトリストも設定)

はじめに

前回建てた ubuntu のサーバにそのまま Java 版 Minecraft サーバを建てたときのメモです。
Java のチューニングについては書かないので別途調べてください┏(<:)

AlwaysFree(一生無料)の「f1-micro」だとメモリサイズが小さすぎるので、事前に「n1-standard-1」に拡張しておきました。

1. Java 版 Minecraft サーバの構築

1.1. Java のインストール

サーバに SSH で接続して、root ユーザにスイッチしておきます。

su

root ユーザでログインしたら mkdir コマンドでサーバをインストールするディレクトリを作ります。
作ったら cd コマンドで移動します。

mkdir /mcjava
cd /mcjava

Minecraft サーバをインストールしようと思いましたが、Java が入ってなさそうなので先に Java を入れましょう。

apt install default-jre

ディスク容量がどうのこうのと聞かれたら y (yes)と打っといてください。
以上で Java のインストールは完了です。

1.2. Minecraft サーバのインストール

続いて Minecraft サーバのインストールです。ダウンロード用 URL は事前に調べておきましょう。
[MINECRAFT]https://www.minecraft.net/ja-jp/download/server/

javadl.png

リンクをコピーしたらダウンロードしましょう。

wget https://launcher.mojang.com/v1/objects/bb2b6b1aefcd70dfd1892149ac3a215f6c636b07/server.jar

ダウンロードしたらサーバを1回起動します。エラーで終了するはずです。
root@mcapsv01_ _mcjava - Google Chrome 2020_03_24 1_06_51_2.png
サーバをダウンロードしたディレクトリに eula.txt というテキストファイルができているはずなので、開いて中身を編集します。
EULA(エンドユーザーライセンス条項)に同意するか否かというファイルなので、同意する場合は eula=false の箇所を eula=true に書き換えましょう。
root@mcapsv01_ _mcjava - Google Chrome 2020_03_24 1_06_27_2.png

eula=true

これでインストール完了です。
忘れてました。Java 版の Minecraft サーバはデフォルトで tcp/25565 ポートを使っているので GCP のファイアウォールルール設定でポートを開けておきましょう。
port2.png

1.3. Minecraft サーバの起動

作ったディレクトリに移動してサーバ起動のコマンドを入力します。サーバを起動するための専用ユーザを作っておいても良いですが、今回はそのまま root ユーザで起動します。
ダウンロードしてきたファイルの名前が「server.jar」だった場合は以下のコマンドで起動します。

cd /mcjava
java -Xmx1024M -Xms1024M -jar server.jar nogui

以上で起動します。

余談ですが、Java 版 Minecraft サーバは Java の仮想マシンJVM 上で、事前に設定したメモリを上限として起動します。そのため、ubuntu サーバの負荷だけを見ていても Minecraft が余裕で動いてるのかどうかは分かりません。
人が増えてきてメモリが足りてないなと思ったらサーバ起動コマンドの -Xmx や -Xms の数字を増やしてみましょう。チューニングについてはここでは説明しません。

1.4. Minecraftサーバへの接続

統合版と一緒なので説明は省略。

1.5. Minecraftサーバの停止

統合版と一緒なので説明は省略。

1.6. ホワイトリストの設定

ホワイトリストも一緒に設定しておこうと思ってやってみたんだけども、統合版と少し仕様が違っていたのでメモっておきます。
統合版では存在しないプレイヤー名も自由にホワイトリストに登録できましたが、Java 版ではそれができないようです。以下の通り、JohnikiJoestar2 は登録できませんでした。
root@mcapsv01_ _mcjava - Google Chrome 2020_03_24 1_24_05_2.png
ということで、whitelist.json を直接編集することにしました。
root@mcapsv01_ _mcjava - Google Chrome 2020_03_24 2_02_04_2.png
ここでも更に罠が。統合版のホワイトリストはユーザの名前だけ分かれば whitelist.json の登録はできますが、Java 版では uuid が必須項目のようです。統合版のつもりでやってたので、ここで 10 分くらいハマりました。
uuid はサーバ側のログを見れば分かるのですが、これからログインしてくる新規ユーザのログは無いです。
ですが、Mojang が uuid を調べる Mojang API を提供しています。

[Mojang API] https://api.mojang.com/users/profiles/minecraft/JohnikiJoestar

{"id":"1f843a4609ae4715a3b062a522193fa0","name":"JohnikiJoestar"}

これでジョニキ・ジョースターの uuid も分かったのでホワイトリストに登録しましょう。
whitelist.json の形式は xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx なので、id を少し手直ししてファイルを編集します。

[
  {
    "uuid": "1f843a46-09ae-4715-a3b0-62a522193fa0",
    "name": "JohnikiJoestar"
  }
]

ファイルを直接編集した場合は、whitelist をリロードしておきましょう。

whitelist reload

以上です。ね?簡単だったでしょ?(ハマったけどな)
start2.png

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