20190405のJavaに関する記事は6件です。

CodilityのBinaryGapを競プロ未経験が解いてみた

BinaryGapとは

簡単に言うと、2進数の1と1に囲まれた0の数で最大の値を求める問題です。
例えば1000010100010であれば、1と1の間の0の個数は左から4, 1, 3となるので、ここでは4が答えとなります。

正直、自信はありませんが100%のスコア判定をもらうことができました。Pythonのスペシャリストではない割には簡潔に書けたのではないかと思います。

1. Python コード

def solution(N):
    return len(max(str(bin(N)).replace("0b", "").split("1")[:-1]))

Step. 1

str(bin(N))で与えられた10進数を2進数に変換します。その際に文字列型へ変換も行います。

Step. 2

.replace("0b", "")二進数を文字列に変換すると最初にリテラルの0bがくっつきます、これをreplace(,)で削除します。

Step. 3

.split("1")1で文字列を分解し、0だけの配列を作成します。

Step. 4

[:-1]でスライスし、最後の要素を削除します。これは、1000101000のように最後の0は1で囲まれてるとは言えないためです。

Step. 5

str(bin(N)).replace("0b", "").split("1")[:-1]の時点で[0, 00000, 00, 00, 0]のような配列が出来上がります。これをmax( )で文字数(0の数)が一番多い要素を抜き出します。この例だと00000です。

Step. 6

5で00000となったので、len()で0の個数を数えます。この値がBinaryGapの答えとなります。

Java

Javaのバージョンも書いて見ました。Javaはあまり慣れていないので、もっといい書き方があるかもしれません。基本的には上記のPythonと同じアルゴリズムです。
違う点としては与えられる引数Nが割り切れる数か判定している所とPythonではmax()で求められた配列内の最大文字数要素を求めるのを自作している点です。
割り切れるかどうか判定した理由としては1000100101のような文字列を1で分割した際にPythonでは["", "000", "", "00", "", "0", ""]のような配列が帰ってくるので最後の要素を削除することで、必ず1で囲まれた0になることが保証されますが、Javaの場合は[, "000", , "00", , "0"]のような配列になってしまい1001のような場合に挙動が変わってしまうからです。

class Solution {
    public int solution(int N) {
        String[] zeros = Integer.toBinaryString(N).split("1");
        int index = zeros.length;
        int max = 0;

        /* 割り切れる数かどうかの判定 */
        if (N % 2 == 0) {
            index = index - 1;
        }

        /* 最大値の判定 */
        for (int i = 0; i < index; i++) {
            if (zeros[i].length() > max) {
                max = zeros[i].length();
            }
        }

        return max;
    }
}

まとめ

今回は、アルゴリズムプログラミングに初めてトライしました。今後はCodilityの他のLessonやJava(2019/4/6追記)などでもトライしていこうと思います.

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

lombokをGradleでインストールできない。

事象

Gradleに下記の2行追加して、Gradle実行したがエラーが出る。

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile('com.jayway.jsonpath:json-path')
    compileOnly 'org.projectlombok:lombok:1.18.6' <--追加
    annotationProcessor 'org.projectlombok:lombok:1.18.6' <--追加
}

エラー

Warning:<i><b>root project 'complete': Unable to resolve additional project configuration.</b>
Details: org.gradle.api.artifacts.ResolveException: Could not resolve all dependencies for configuration ':runtimeClasspath'.
Caused by: org.gradle.internal.resolve.ArtifactResolveException: Could not download spring-boot-starter-web.jar (org.springframework.boot:spring-boot-starter-web:2.1.3.RELEASE): No cached version available for offline mode</i>

対策

どの対策が聞いたかわかりませんが、上から順にやって最後に成功しました。

  1. Enable annotation processingにチェックを入れる。
    Intellij IDEA -> Preferences -> Compiler -> Annotation Processors

  2. Enable annotation processingにチェックを入れる。
    File -> Other Settings -> Default Settings -> Compiler -> Annotation Processors

  3. Lombokプラグインをインストール
    Intellij IDEA -> Preferences -> Plugins ->Browse Repositories-> Search for "Lombok"-> install plugin -> Apply and restart IDEA

  4. Work offlineにチェックを入れる。
    Intellij IDEA -> Preferences -> Build, Execution, Deployment -> Build Tools -> Gradle

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

因子水準の展開

因子水準を展開するサンプル

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TEMP {
    public static void main(String[] args){
        new TEMP();
    }

    TEMP() {
        TestCase1 testCase1 = new TestCase1();

        List<String> testCasesString = testCase1.getTestCasesStringList();
        for(String testCaseString : testCasesString) {
            System.out.println(testCaseString);
        }

        List<Map<String, String>> testCases = testCase1.getTestCasesMapList();
        for(Map<String, String> testcase : testCases){
            System.out.print(testcase.get("甲"));
            System.out.print(testcase.get("乙"));
            System.out.println(testcase.get("丙"));
        }
    }

    class TestCase1 extends AbsFactorLevelTypeTestCase{

        void setFactorAndLevel() {
            factors.add(new Factor("甲").setLevels("1", "2"));
            factors.add(new Factor("乙").setLevels("a", "b", "c"));
            factors.add(new Factor("丙").setLevels("A", "B", "C"));
            exclude("2,b,C");
        }
    }

    /**
     * @author maruta_r
     * For test-cases using factor and level.
     */
    abstract class AbsFactorLevelTypeTestCase {
        protected List<Factor> factors = new ArrayList<Factor>();
        private List<String> testCases;

        AbsFactorLevelTypeTestCase(){
            setFactorAndLevel();
        }

        /**
         * Specify factors and levels at subclass.
         * 
         * [EXAMPLE]
         * void setFactorAndLevel() {
         *   factors.add(new Factor("甲").setLevels("1", "2"));
         *   factors.add(new Factor("乙").setLevels("a", "b", "c"));
         *   factors.add(new Factor("丙").setLevels("A", "B", "C"));
         * }
         * 
         */
        abstract void setFactorAndLevel();

        /**
         * Combination to be excluded from tests.
         * Specify the name of each levels separated by comma.
         * 
         * [EXAMPLE]
         * exclude("1,a,B");
         * exclude("2,c,A");
         */
        protected void exclude(String combination) {
            if(this.testCases == null) expandAll();
            testCases.remove(combination);
        }

        /**
         * @return All combination of each levels separated by comma. Header included.
         */
        public List<String> getTestCasesStringList() {
            if(this.testCases == null) expandAll(); 
            return this.testCases;
        }

        /**
         * @return Test-cases converted Map List. Factor name is key.
         */
        public List<Map<String, String>> getTestCasesMapList() {
            List<Map<String, String>> testCasesMapList = new ArrayList<Map<String, String>>();
            List<String> copy = new ArrayList<String>(getTestCasesStringList());

            String[] keys = null;
            for(String testCase : copy) {
                Map<String, String> map = new HashMap<String, String>();

                //setting header as a keys
                String[] values = testCase.split(",");
                if(keys == null) {
                    keys = values;
                    continue;
                }

                for(int i = 0; i < values.length; i++) {
                    map.put(keys[i], values[i]);
                }

                testCasesMapList.add(map);
            }
            return testCasesMapList;
        }

        private void expandAll() {
            List<String> result = new ArrayList<String>();
            result.add("");
            String header = "";

            for(Factor factor : factors) {
                if(header.length() > 0) header += ",";
                header += factor.getFoctorName();

                List<String> newResult = new ArrayList<String>();

                for(int i = 0; i < result.size(); i++) {
                    List<String> levels = factor.getLevels();

                    for(String level : levels) {
                        String prevStr = result.get(i);
                        if(prevStr.length() > 0) prevStr += ",";
                        newResult.add(prevStr + level);
                    }
                }
                result = newResult;
            }
            result.add(0, header);
            this.testCases =  result;
        }
    }

    class Factor {
        private final String factorName;
        private List<String> levels = new ArrayList<String>();


        public Factor(String factorName, String... args) {
            this.factorName = factorName;
            setLevels(args);
        }

        public Factor setLevels(String... args) {
            for(int i = 0; i < args.length; i++){
                levels.add(args[i]);
            }
            return this;
        }

        public List<String> getLevels() {
             return this.levels;
        }

        public String getFoctorName() {
            return this.factorName;
        }
    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MicronautでApache Kafkaにアクセスする

Micronaut Kafka

MicronautにApache Kafkaのサポートがあるというので、試してみることにしました。

Kafka Support

Micronautを使って、Apache KafkaのProducerおよびConsumerを作ることができるようです。

Micronaut Kafka

環境

今回使ったMicronautのバージョン。

$ mn -V
| Micronaut Version: 1.0.4
| JVM Version: 1.8.0_191

Micronaut 1.0.4では、Apache Kafka 2.0.1に対応しているようです。

Upgrade to Kafka 2.0.1

Micronaut Kafka

これに合わせて、今回はApache Kafka 2.0.1をダウンロード。

$ wget http://ftp.tsukuba.wide.ad.jp/software/apache/kafka/2.0.1/kafka_2.12-2.0.1.tgz
$ tar xf kafka_2.12-2.0.1.tgz
$ cd kafka_2.12-2.0.1

Apache KafkaのQuick Startに習い、Apache ZooKeeperとApache Kafka Brokerを起動します。

Quick Start / Start the server

## Apache Zookeeper
$ bin/zookeeper-server-start.sh config/zookeeper.properties

## Apache Kafka(Broker)
$ bin/kafka-server-start.sh config/server.properties

Topicは、my-topicという名前で作成しました。

$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic my-topic
Created topic "my-topic".

アプリケーションを作る

それでは、アプリケーションを作ってみます。ProducerとConsumer(ここではListenerとしていますが)用のプロジェクトを作成。

## Producer
$ mn create-app --features kafka --build maven hello-kafka-producer

## Listener(Consumer)
$ mn create-app --features kafka --build maven hello-kafka-listener

以降、この2つのプロジェクトに対してソースコードを追加、編集していきます。

Producerを作る

では、Apache Kafka Brokerにメッセージを送信するProducerを書いてみます。

$ cd hello-kafka-producer

mainクラスは、自動生成されたまま使います。

src/main/java/hello/kafka/producer/Application.java

package hello.kafka.producer;

import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Micronaut.run(Application.class);
    }
}

Producerは、@KafkaClientアノテーションを付けたインターフェースを作成するようです。

Defining @KafkaClient Methods

こんな感じで作成。インターフェースに対する実装クラスの作成は不要です。

src/main/java/hello/kafka/producer/MessageClient.java

package hello.kafka.producer;

import io.micronaut.configuration.kafka.annotation.KafkaClient;
import io.micronaut.configuration.kafka.annotation.KafkaKey;
import io.micronaut.configuration.kafka.annotation.Topic;
import io.reactivex.Single;

@KafkaClient(acks = KafkaClient.Acknowledge.ALL)
public interface MessageClient {
    @Topic("my-topic")
    Single<String> send(@KafkaKey String key, Single<String> message);
}

設定は、@KafkaClientアノテーションおよび設定ファイルで行います。

@KafkaClientアノテーションでは、ACKの設定だけ行いました。

@KafkaClient(acks = KafkaClient.Acknowledge.ALL)
public interface MessageClient {

設定ファイル、こちら。

src/main/resources/application.yml

---
micronaut:
    application:
        name: hello-kafka-producer

---
kafka:
    bootstrap:
        servers: localhost:9092
    producers:
        default:
            retries: 5

kafka.bootstrap.serversでは、Apache Kafka Brokerに対する接続設定を行います。

Producerの振る舞いについては、kafka.producers.[client-id]で行います。

idを指定することで、@KafkaClient単位に設定を行うことができます。

Per @KafkaClient Producer Properties

今回は特にidを指定していないので、defaultという名前になっています。

メッセージの送信は、@Topicアノテーションにトピック名を指定したメソッドを使って行います。

    @Topic("my-topic")
    Single<String> send(@KafkaKey String key, Single<String> message);

キーを指定する場合は、@KafkaKeyを使用します。キーを使わない場合は、指定不要です(キーの引数自体を省略できます)。

また、RxJavaなど、Reactive Streamsまわりの型も使えるので、今回はこちらを利用しました。

Reactive and Non-Blocking Method Definitions

最後に、@KafkaClientを使う@Controller

src/main/java/hello/kafka/producer/MessageController.java

package hello.kafka.producer;

import javax.inject.Inject;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.reactivex.Single;

@Controller("/message")
public class MessageController {
    @Inject
    MessageClient messageClient;

    @Post(value = "/{key}", consumes = MediaType.TEXT_PLAIN, produces = MediaType.TEXT_PLAIN)
    public Single<String> message(String key, @Body Single<String> value) {
        return messageClient.send(key, value).map(v -> String.format("message [%s] sended", v));
    }
}

ビルド。

$ ./mvnw package

これで、Producer側は完了。

Listener(Consumer)

続いて、Consumer側。

$ cd hello-kafka-listener

Kafka Consumers Using @KafkaListener

Consumerは、@KafkaListenerアノテーションを使って作成します。こちらは、class定義になります。

src/main/java/hello/kafka/listener/MessageListener.java

package hello.kafka.listener;

import java.util.List;

import io.micronaut.configuration.kafka.Acknowledgement;
import io.micronaut.configuration.kafka.annotation.KafkaKey;
import io.micronaut.configuration.kafka.annotation.KafkaListener;
import io.micronaut.configuration.kafka.annotation.OffsetReset;
import io.micronaut.configuration.kafka.annotation.OffsetStrategy;
import io.micronaut.configuration.kafka.annotation.Topic;
import io.reactivex.Single;

@KafkaListener(groupId = "message-group", offsetReset = OffsetReset.EARLIEST, offsetStrategy = OffsetStrategy.DISABLED)
public class MessageListener {
    @Topic("my-topic")
    public void receive(@KafkaKey String key, Single<String> message, Acknowledgement acknowledgement) {
        message.subscribe(m -> {
            System.out.printf("Received key / message = %s / %s%n", key, m);
            acknowledgement.ack();
        });
    }
}

@KafkaListenerアノテーションでConsumerの設定を行い、メッセージを受け取るメソッドには@Topicアノテーションを指定します。

@KafkaListener(groupId = "message-group", offsetReset = OffsetReset.EARLIEST, offsetStrategy = OffsetStrategy.DISABLED)
public class MessageListener {
    @Topic("my-topic")
    public void receive(@KafkaKey String key, Single<String> message, Acknowledgement acknowledgement) {

使う型は、Reactiveなものを。

Receiving and returning Reactive Types

また、ACKを使うようにもしています。

        message.subscribe(m -> {
            System.out.printf("Received key / message = %s / %s%n", key, m);
            acknowledgement.ack();
        });

今回は設定はBrokerへの接続先のみとしました。あと、同一ホストでProducerも立ち上げることにするので、micronaut.server.port8080以外で設定。

src/main/resources/application.yml

---
micronaut:
    application:
        name: hello-kafka-listener
    server:
        port: 9080

---
kafka:
    bootstrap:
        servers: localhost:9092

こちらもビルド。

$ ./mvnw package

これで、Consumer側も準備完了です。

動かしてみる

では、動作確認してみましょう。

## Producerを起動
$ java -jar target/hello-kafka-producer-0.1.jar


## Consumerを起動
$ java -jar target/hello-kafka-listener-0.1.jar

Producerに適当にメッセージを放り込んでみます。

$ curl -H 'Content-Type: text/plain' localhost:8080/message/key1 -d 'value1'
message [value1] sended

$ curl -H 'Content-Type: text/plain' localhost:8080/message/key2 -d 'value2'
message [value2] sended

$ curl -H 'Content-Type: text/plain' localhost:8080/message/key3 -d 'value3'
message [value3] sended

Consumer側には、こんな感じで受信したメッセージが表示されます。

Received key / message = key1 / value1
Received key / message = key2 / value2
Received key / message = key3 / value3

動かせたようですね。

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

クラスタ構成のDBへ接続ためのJDBC URL

spring-bootでクラスタ構成のDBに接続するためのJDBC URLです。

Oracle RAC

String DB_URL= "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=" +
                     "(LOAD_BALANCE=OFF)(FAILOVER=ON)" +
                     "(ADDRESS=(PROTOCOL=TCP)(HOST=host1)(PORT=port1))" +
                     "(ADDRESS=(PROTOCOL=TCP)(HOST=host2)(PORT=port2)))" +
                     "(CONNECT_DATA=(SERVICE_NAME=service_name)))"

上記LOAD_BALANCE=ON、FAILOVER=ONの設定を組み合わせることによって「負荷分散」と「接続フェイルオーバー」の2つの機能が有効になる。

application.properties指定の場合、以下のように記載する。

spring.datasource.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=OFF)(FAILOVER=ON)(ADDRESS=(PROTOCOL=TCP)(HOST=host1)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=host2)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=testDB)))
spring.datasource.username=user
spring.datasource.password=password

AWS Aurora DBクラスタ

MariaDB JDBC ドライバーの自動フェイルオーバー機能を使用し、フェイルオーバーの状況下でマスターとレプリカを高速に切り替えられる。

JDBC URLを指定する場合、Aurora DBのクラスタエンドポイントを直接指定するか、マスターとレプリカのエンドポイントを両方指定するかの2つ方法があります。

クラスタエンドポイント指定

String DB_URL = "jdbc:mariadb:aurora//auroradbcluster.auroradbtest.com::3306/testDB";

application.properties指定の場合、以下のように記載する。

spring.datasource.url=jdbc:mariadb:aurora//auroradbcluster.auroradbtest.com::3306/testDB
spring.datasource.username=user
spring.datasource.password=password

マスターとレプリカのエンドポイントを両方指定

String DB_URL = "jdbc:mariadb:aurora//rds.auroradbtest.com:3306,rds-ro.auroradbtest.com:3306/testDB";

application.properties指定の場合、以下のように記載する。

spring.datasource.url=jdbc:mariadb:aurora//rds.auroradbtest.com:3306,rds-ro.auroradbtest.com:3306/testDB
spring.datasource.username=user
spring.datasource.password=password
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PagerViewで左右どちらにフリックしたか判定する方法

    /** Touch前の座標*/
    private boolean mIsPagerViewTouchDown = false;

    pager.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {

            float touchX = event.getX();

            switch(event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mPreviousTouchPointX = touchX;
                    break;
                case MotionEvent.ACTION_UP:
                    float dx = touchX - mPreviousTouchPointX;

                    // TouchDown時のタッチ座標とTouchUp時の座標を比較しどちらにフリックしたか判定
                    if ((Math.abs(dx) > 1)) {
                        if (dx > 0) {
                            Log.d(MainActivity.class.getSimpleName(), "右へフリック"+dx);
                        } else {
                            Log.d(MainActivity.class.getSimpleName(), "左へフリック"+dx);
                        }
                    }
                    break;
                default:
                    break;
            }

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