20210505のJavaに関する記事は14件です。

Bing Image Searchで推しメン画像を自動取得する

Bing Search APIには何種類かのAPIがあります。今回は画像を検索するBing Image Searchを使って、推しメン画像の自動収集をしてみました。 実行結果はこんな感じです。フォルダ内が可愛い推しメン(乃木坂46の大園桃子さん)画像で溢れて幸せになれます。 Bing Search API v7 まずはBing Search v7を検索して選びます。 Bingリソースを作成します。 準備はこれだけです。キーが発行されるので1か2いずれかをコピーします。 ハマったこと 先にハマったことを書きたいと思います。最初、以下の記事を参考に進めていました。 ライブラリがあるんだな、と何も考えずに以下を追加しました。 <dependency> <groupId>com.microsoft.azure.cognitiveservices</groupId> <artifactId>azure-cognitiveservices-imagesearch</artifactId> <version>1.0.2</version> </dependency> 記事のコードを参考にキーを設定したのですが、以下のエラーで認証が通りませんでした。 Exception in thread "main" com.microsoft.azure.cognitiveservices.search.imagesearch.models.ErrorResponseException: Status code 401, {"error":{"code":"401","message":"Access denied due to invalid subscription key or wrong API endpoint. Make sure to provide a valid key for an active subscription and use a correct regional API endpoint for your resource."}} キーの指定方法は正しく、何が原因がさっぱりわかりませんでした。仕方なく認証の部分で使っているBingImageSearchManagerクラスのソースを確認したところ、ライブラリの中で使っているエンドポイントが以下になっていました。 https://api.cognitive.microsoft.com/bing/v7.0/ Bingリソースの画面上では以下のエンドポイントになっています。 https://api.bing.microsoft.com/ ドキュメントを見ると以下のように「Bing Search API は、Cognitive Services プラットフォームから Microsoft.com の下の新しい領域に移行されています。」と警告があるので、古い情報をみてしまっていたようです。 HttpClientでAPIを呼び出す 先ほどのライブラリを使うのは諦めて、HttpClientで呼び出すことにしました。 レスポンスのJSONは以下のような形で色々な情報が含まれています。 { "_type": "Images", "instrumentation": { "_type": "ResponseInstrumentation" }, "readLink": "images/search?q=大園桃子", "webSearchUrl": "https://www.bing.com/images/search?q=大園桃子&FORM=OIIARP", "queryContext": { "originalQuery": "大園桃子", "alterationDisplayQuery": "大園桃子", "alterationOverrideQuery": "+大園桃子", "alterationMethod": "AM_JustChangeIt", "alterationType": "CombinedAlterationsChained" }, "totalEstimatedMatches": 997, "nextOffset": 38, "currentOffset": 0, "value": [ { "webSearchUrl": "https://www.bing.com/images/search?view=detailv2&FORM=OIIRPO&q=%E5%A4%A7%E5%9C%92%E6%A1%83%E5%AD%90&id=C98F2558D4D0436AFB3E601D2AF41B57CA13EDEC&simid=608037150088111611", "name": "大園玲のかわいい性格の秘密や大園桃子との関係は?推しメン ...", "thumbnailUrl": "https://tse1.mm.bing.net/th?id=OIP.GazlCdtTHvJoKftuEEnDbgHaEu&pid=Api", "datePublished": "2020-05-16T22:19:00.0000000Z", "isFamilyFriendly": true, "contentUrl": "https://img.ananweb.jp/2019/12/29195005/2183-beauty1.jpg", "hostPageUrl": "https://idolevery.com/oozonorei-12180", "contentSize": "286430 B", "encodingFormat": "jpeg", "hostPageDisplayUrl": "https://idolevery.com/oozonorei-12180", "width": 1300, "height": 831, "hostPageDiscoveredDate": "2020-04-20T00:00:00.0000000Z", "thumbnail": { "width": 474, "height": 302 }, "imageInsightsToken": "ccid_GazlCdtT*cp_15E9A25A3E3827AB4EAE1B6B87C4DB2C*mid_C98F2558D4D0436AFB3E601D2AF41B57CA13EDEC*simid_608037150088111611*thid_OIP.GazlCdtTHvJoKftuEEnDbgHaEu", "insightsMetadata": { "pagesIncludingCount": 11, "availableSizesCount": 4 }, "imageId": "C98F2558D4D0436AFB3E601D2AF41B57CA13EDEC", "accentColor": "A1382A" }, { ..省略.. } ] } とりあえず、contentUrlだけ引っ張れれば良いので、GSONでマッピングするための簡単なクラスを2つ作りました。 package com.example; public class Value { private String contentUrl; public Value(String contentUrl) { this.contentUrl = contentUrl; } public String getContentUrl() { return contentUrl; } public void setContentUrl(String contentUrl) { this.contentUrl = contentUrl; } } package com.example; import java.util.List; public class Result { private List<Value> value; public Result() { } public Result(List<Value> value) { this.value = value; } public List<Value> getValue() { return value; } public void setValue(List<Value> value) { this.value = value; } } 実装は以下のとおりです。Bing Image Searchで取得したcontentUrlの画像ファイルをダウンロードします。 package com.example; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpClient.Version; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.file.Paths; import com.google.gson.Gson; public class App { public static void main( String[] args ) throws URISyntaxException, IOException, InterruptedException { HttpClient client = HttpClient.newBuilder() .version(Version.HTTP_1_1) .build(); HttpRequest req = HttpRequest.newBuilder(new URI("https://api.bing.microsoft.com/v7.0/images/search?q=" + URLEncoder.encode("大園桃子", "UTF-8"))) .header("Ocp-Apim-Subscription-Key", "xxxxxxxx") .GET() .build(); HttpResponse<String> ret = client.send(req, HttpResponse.BodyHandlers.ofString()); Gson gson = new Gson(); Result result = gson.fromJson(ret.body(), Result.class); result.getValue().stream().forEach(v -> { try { URL url = new URL(v.getContentUrl()); String fileName = Paths.get(url.getPath()).getFileName().toString(); ReadableByteChannel readableByteChannel = Channels.newChannel(url.openStream()); FileOutputStream fileOutputStream = new FileOutputStream(fileName); fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }); } } 実行すると最初に紹介したように画像が取得できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】オーバーロード

オーバーロードとは、一つのクラス内に引数の数や型の異なる同じメソッドを複数記述することです。 例 class Calc { int add(int a, int b){ return a + b; } int add(int a){ return a + 1; } double add(double a, double b){ return a + b; } } class Call { public static void main(String[] args){ Calc cal1 = new Calc(); Calc cal2 = new Calc(); Calc cal3 = new Calc(); System.out.println(cal1.add(1, 1)); System.out.println(cal2.add(2)); System.out.println(cal3.add(2.2, 3.3)); } } 実行結果 2 3 5.5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】異なるオブジェクトのフィールドの参照と代入

異なるオブジェクトのフィールドを参照したり、代入したりするにはピリオドを使って次のように記述する。 代入 x.a = 5; 参照 b = x.a + 2 x ... オブジェクト名 a ... フィールド名 (例) class Wdata { int month; int day; String sky; } class Wether { public static void main(String[] args) { Wdata today = new Wdata(); today.month = 10; today.day = 5; today.sky = "はれ"; System.out.println(today.month+"月"+today.day+"日"+today.sky); } } 実行結果 10月5日はれ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】オブジェクトの生成

クラスからオブジェクトを生成する。 [クラス名] [オブジェクト名]; [オブジェクト名] = new [クラス名](); 次のようにまとめて記述する方が一般的 [クラス名] [オブジェクト名] = new [クラス名](); (example) Class Student{ int[] point = new int[3]; String name; double ave(){ ... } } Class Score { public static void main(String[] args){ Student taro = new Student(); Student jiro = new Student(); ... } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Eclipse+Forgeでマイクラのmod開発~最小限の作業で空のmodを作る

デバッグ環境を整えるところまでは以前の記事と同じです。以前の記事ではMdkにもともと含まれているサンプルのmodを動かしただけなので、今回はオリジナルのmodを作ります。といっても、modに色々させようと思うと学習しないとダメなことが大量にあるので今回は空っぽのmodを最少限度の作業で作ってみましょ。 環境 OS: Windows10 Jdk: openjdk version "15.0.2" 2021-01-19 ※16.xでもいいかもだけどgradleやらなにやら色々変わってしまって心配だったので今回は15で。 IDE: Eclipse IDE for Java Developers (Version 4.19.0) Buildship(Eclipseのgradleプラグイン): 3.0 Minecraft: Java版 1.16.5 Forge: 1.16.5 modの大元になるクラスの作成。 まずはサンプルコードのsrc\main\java\com\example\examplemodを削除します。comディレクトリ事バッサリ削除してOKです。 空のクラスを一個作ります。クラス名、パッケージなどなどなんでもOK。サンプルコードを載せておきます。 EmptyMod.java package jp.munecraft.mod.test; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import net.minecraftforge.fml.common.Mod; // ★詳細後述 @Mod("emptymod") public class EmptyMod { // forgeがもともとlog4jを使っているので気楽にLoggerが使えます。 private static final Logger LOGGER = LogManager.getLogger(); public EmptyMod() { // 今回はログを吐くだけ。 LOGGER.info("Welcome to munecraft mod!!!"); } } 空っぽです。コンストラクタが呼ばれたらログを吐くだけ。ポイントは@Modアノテーションですね。このアノテーションを付けたクラスがmodのメインのクラスと認識されます。引数の文字列はmodIdと呼ばれるもので、後でも出てくるので間違えないように同じものを使ってください。 ちなみにmodIdに使える文字や長さには制限があり、forgeのソースによると Required to be lowercased in the english locale for compatibility. Will be truncated to 64 characters long. だそうです。試してみましたが、確かに大文字や_といった記号を使うと起動時にエラーになりました。 modの設定ファイルの作成 いじるファイルはsrc/main/resources/META-INF/mods.tomlだけです。中身を見てみよう。 mods.toml # This is an example mods.toml file. It contains the data relating to the loading mods. # There are several mandatory fields (#mandatory), and many more that are optional (#optional). # (中略) # A URL to refer people to when problems occur with this mod #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional # A list of mods - how many allowed here is determined by the individual mod loader [[mods]] #mandatory # The modid of the mod modId="emptymod" #mandatory ★ここ! # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it # ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata # see the associated build.gradle script for how to populate this completely automatically during a build version="${file.jarVersion}" #mandatory # A display name for the mod displayName="Empty mod by munecraft" #mandatory # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ #updateJSONURL="https://change.me.example.invalid/updates.json" #optional # (後略) 最低限いじらないとダメなのは[[mods]]セクションのmodIdだけです。ここを先ほど@Modアノテーションで指定したmodIdにそろえなくてはなりません。 動かしてみる 以前の記事でビルドスクリプトをインポート済みであれば、EclipseからrunClientが簡単にできるようになっているはず。で、実行。。 [17:06:16] [Render thread/DEBUG] [ne.mi.fm.ja.FMLModContainer/LOADING]: Creating FMLModContainer instance for jp.munecraft.mod.test.EmptyMod with classLoader cpw.mods.modlauncher.TransformingClassLoader@4b039c6d & cpw.mods.modlauncher.TransformingClassLoader@4b039c6d [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.fo.ForgeVersion/CORE]: Forge Version package package net.minecraftforge.versions.forge, Forge, version 36.1 from cpw.mods.modlauncher.TransformingClassLoader@4b039c6d [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.fo.ForgeVersion/CORE]: Found Forge version 36.1.0 [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.fo.ForgeVersion/CORE]: Found Forge spec 36.1 [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.fo.ForgeVersion/CORE]: Found Forge group net.minecraftforge [17:06:16] [modloading-worker-2/INFO] [jp.mu.mo.te.EmptyMod/]: Welcome to munecraft mod!!! [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.mc.MCPVersion/CORE]: Found MC version information 1.16.5 [17:06:16] [modloading-worker-1/DEBUG] [ne.mi.ve.mc.MCPVersion/CORE]: Found MCP version information 20210115.111550 [17:06:16] [modloading-worker-1/INFO] [ne.mi.co.ForgeMod/FORGEMOD]: Forge mod loading, version 36.1.0, for MC 1.16.5 with MCP 20210115.111550 無事にWelcome to munecraft mod!!!がコンソールに表示されました!ね?簡単でしょ?本当はこの記事でいじった以外にもいっぱい設定がありますが、それはまた後々。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot から Key Vault の値を Managed IDを使って読み取る

Spring Boot から Key Vault の値を Managed IDを使って読み取る 今回は、Managed ID(管理ID)についてです。Managed IDについては、以下にがっつりと解説がありますが、簡単に言ってしまえば、例えば、WebApps から SQL DB 、ストレージ、Key Vault など他コンポーネントのアクセスにパスワードなどのシークレットを必要としない仕組みとなります。 Azure リソースのマネージド ID | Microsoft Docs もちろん、WebAppsから、他のAzureリソースへ勝手にアクセスできるわけではありませんから、Azure 上でしかるべき手順を踏んで設定を行っておく必要があります。設定は、ポータル以外にも Azure CLI などのコマンドラインツールでも可能です。 WebApps + Key Vault での例 先だっての記事の続きというわけではありませんが、前提とする環境は、 Spring Boot + WebApps/Key Vault で試してみたいと思います。 WebAppsでの設定 ポータルで確認すると、WebApps には、 「ID」という管理項目があります。Managed ID を有効にするには、ここでこの機能をオンにします。 オンにするとなにが起こるのかというと、Azure AD 上に、このWebAppsに紐づいたサービスプリンシパルと呼ばれる資格情報がこっそり作成されます。WebApps 側の設定はこれだけです。この時に表示されるオブジェクトID (クライアントID)は、あとで必要ですのでメモっておきます。またサービスプリンシパルの名前はWebAppsの名前と同じ名前になります。後ほどアクセス権を設定するところで使います。 Key Vault での設定 Key Vaultには、キー、シークレット、証明書の3つの種類をストアできます。いわゆる構成設定値のような、キーバリュー型のものはシークレットに保存します。以下のクラスを用意したとき、 kvconfig.messsage を設定しないといけませんが、Key Vault では「ドット」を使用できなので、kvconfig-message で代用します。 @Configuration @ConfigurationProperties("kvconfig") public class KvConfig { private String message; public String getMessage() { return this.message; } public void setMessage(String message) { this.message = message; } } シークレットの作成を選んで、hirakegema という値を定義しておきます。 次にこの Key Vault へのアクセス許可を設定します。「アクセスポリシ-」を開きポリシーを追加します。キー、シークレット、証明書に対して、それぞれ CRUD + List のような権限設定ができます。ここでは、シークレットの読込み権限が必要ですので、読み取りと一覧を付与します(一覧へのアクセス権限がないと失敗するのでご注意)。権限の付与は最低にし無駄に編集権限とかを与えるのはやめましょう。 プリンシパルの選択は、WebAppsの名前を指定すると、同名のサービスプリンシパルが指定できるでしょう。 Spring Boot アプリの設定 依存に、azure-spring-boot-starter-keyvault-secrets を設定します。 <dependency> <groupId>com.azure.spring</groupId> <artifactId>azure-spring-boot-starter-keyvault-secrets</artifactId> </dependency> application.properties には、以下の情報が必要です。 テナントID Key Vault のURL クライアントID(Web Apps で Managed IDをオンにしたときに表示されるGUID) azure.keyvault.enabled=true azure.keyvault.client-id=ManagedIDのオブジェクトID azure.keyvault.tenant-id=Azure AD のテナントID azure.keyvault.uri=https://xxxxxxx.vault.azure.net/ コントローラークラスは、構成クラスをDIして表示するだけです。 @RestController public class KvController { private KvConfig config; public KvController(KvConfig config) { this.config = config; } @GetMapping("kv") public String getMessage() { return config.getMessage(); } } デプロイ デプロイしたエンドポイントに curl で APIを叩いてみましょう。 hirakegoma が表示されていれば正しく動作しています。これで Key Vault にアクセスするために、キーを管理する必要がなくなりました。 $ curl https://config-sample-1620013095057.azurewebsites.net/kv hirakegoma うまく動かない場合 うまく動かない場合は、だいたい設定に失敗して、Spring Boot 起動時に例外が発生していることが多いです。私も何度かやりました。そのような場合は、App Service から 「問題の診断と解決」というメニューを開いて、Application Log を参照すると、起動時の例外を見ることが出来るので、参考にしてください。 以下失敗例。 ローカル環境で試したい場合 .NET ですと、Visual Studio 自身が認証した情報をつかって Key Vault へアクセスしてくれるのですが、Javaのこのシナリオの場合は、Azure CLI の認証情報とかを勝手に使ってくれません。 このような場合は、手動でサービスプリンシパルを作成し、Key Vault のアクセス件を設定する必要があります。Azure CLI ですと以下のコマンドで作成できます。作成したサービスプリンシパルは、前述したKey Vault の設定でアクセス権を設定できます。 az ad sp create-for-rbac --name xxxxxxxxx コマンドを実行すると、アプリケーションID(クラインとID)と、パスワードが表示されるので、それぞれ、client-id と client-key に設定します。 azure.keyvault.enabled=true azure.keyvault.client-id=ManagedIDのオブジェクトID azure.keyvault.tenant-id=Azure AD のテナントID azure.keyvault.uri=https://xxxxxxx.vault.azure.net/ azure.keyvault.client-key=パスワード これでローカル実行出来ると思います。 まとめ 今回は、先にWebAppsにデプロイした例を説明しましたが、ローカル環境からやったほうが簡単だと思います。意外とくだらない設定ミスで時間を潰したりするので、例外メッセージはきちんと読みましょう(>自分)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】3個以上の複数の値からMAX(もしくはMIN)値を取得する際にどう書けばいいか

概要 例えば、配列にもListにも格納されてないintの値が3個以上あって、その中からMAXやMINを取得したい場合、どういうふうに書くのが良いでしょうか。Math.max関数を入れ子にしたり、一度Listに入れてStreamAPIのmax関数を使うとかもありだと思いますが、もう少し良い書き方ないかなと思い、今回メモ書きとして残しておきます。 対応案 Find the max of 3 numbers in Java with different data typesのstackoverflowの記事に色々対応方法書いてありますが、可変引数でメソッド化して、その後にList化してmaxを取るやり方が個人的には一番スマートかなと感じました。 可変引数についてはこちらを参照してください。 実装サンプル sample.java import java.util.Arrays; import java.util.Collections; public class MaxTest { public static void main(String[] args){ int test1 = 3; int test2 = 4; int test3 = 5; System.out.println(getMax(test1, test2, test3)); } private static int getMax(Integer... vals) { return Collections.max(Arrays.asList(vals)); } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Play Frameworkで作成したWebアプリをHerokuにデプロイするときに詰まった話。

経緯 Play Frameworkで作成したWebアプリを作成しました。 せっかく作成したのでHerokuにデプロイしてみようとしたのですが エラーでデプロイができませんでした。 ログを見ると"App crashed"と書かれており ログだけでは何が原因なのかがちょっとわからなかったです。 ちなみに言語はJavaです。 対応 ログに書かれている"App crashed"を頼りに 調べてみて下記のこと実施してみました。 1.不要なファイルの削除   ローカルではSQLiteを利用していたため   データベースのファイル(拡張子が「.sqlite3」の奴)や   クエリが記載されたファイルがありました。   HerokuではPostgreSQLを利用するため、いらないので削除しました。 2.PostgreSQLのJDBCドライバの追加   DBはPostgreSQLを利用するので   JDBCドライバ(拡張子が「jar」のファイル)を追加しました。 3.build.sbtの修正   「name:=」のところがアプリ名になっていなかったので    アプリ名になるよう修正。    また、PostgreSQLのドライバーライブラリを記載しました。 name:=""""アプリケーション名"""         ・         ・         ・ libraryDependencies+="org.postgresql"%"postgresql"%"jarファイルのバージョン" 4.Procfileの追加   Procfileがなかったので追加しました。   記載内容は以下になります。   ※${DATABASE_URL}は後で説明。    ${APPLICATION_SECRET}はシークレットキーの環境変数 web: target/universal/stage/bin/アプリケーション名 -Dhttp.port=${PORT} -DapplyEvolutions.default=true -Ddb.default.driver=org.postgresql.Driver -Ddb.default.url=${DATABASE_URL} -Dplay.cypto.secret=${APPLICATION_SECRET} 5.application.confの修正   PostgreSQLを利用するため、driver,url,user,passwordを追加。   シークレットキーを設定しないと怒られたので、それも設定。   また、現状だとアクセスが許可されているのはローカルのみなので   一旦は全許可とするように設定。   ※url,user,passwordの環境変数について    HerokuではDBの情報を環境変数に設定します。    デフォルトで環境変数「DATABASE_・・」にDBの情報を保持する。 db.default.driver=org.postgresql.Driver db.default.url=${?DATABASE_URL} db.default.user=${?DATABASE_USER} db.default.password=${?DATABASE_PASSWORD} play.http.secret.key="生成したシークレットキー" play.filters.hosts.allowd+="." 感想 上記のことを行い無事デプロイできました。 デプロイで詰まってから解決するまで2~3週間ほど時間が かかってしまい大変でした。 Ruby on Railsは簡単でしたので大丈夫かなーと思っていましたが 全然大丈夫ではありませんでした。 シークレットキーをapplication.confとProcfileに記載したりと ところどころあってるのかなーと思うところはありますが とりあえずデプロイできてよかったです。 あと、説明が間違ってたらごめんなさい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Effective Java(第3版) 項目7 使われなくなったオブジェクト参照を取り除く

使われなくなった参照を保持しないように注意し、メモリリークが発生しないようにする、という内容です。 記載内容 クラスが独自のメモリ管理を行う場合 コード例のスタックのように、クラスが独自のメモリ管理を行う場合はメモリリークに注意する必要が有る。 具体的には、使われなくなった参照にnullを設定することで、不要な参照を取り除けば良い。 ただし、このnullの設定は、不要にコードを乱雑にするため、常に行うべきではない。 (スコープの終了で解放される変数に対して、nullを設定するようなことはするべきではない) キャッシュ オブジェクト参照をキャッシュに入れると、そこにオブジェクト参照が存在することを忘れることが多く、 キャッシュした参照が不要になっても、キャッシュ内に残ったままにしてしまうことが多い。 一般的な対処法として、以下がある。 WeakHashMap(キーへの参照が無くなった場合に、キャッシュされた値が不要になる場合に有効) 古いキャッシュのバックグラウンド(ScheduledThreadPoolExecutor等)での除去 直接java.lang.refを使用する(高度なキャッシュ処理が必要な場合) リスナー、コールバック コールバックを登録可能だが、明示的な解除をAPIとして提供していない場合、コールバックが蓄積し、メモリリークとなる。 対処方法として、コールバックに対して、弱い参照だけを保持する方法がある。 考察 メモリリークを避けるのは当然ですが、油断するとメモリリークしてしまう、ということは良くあると思います。 特に、クラス内でコレクションによる参照の保持することで、意図せずメモリ管理を行っていることが有ります。 その場合、その参照が増え続けないか、明示的な除去が必要かは、よく考える必要が有ります。 ただ、プログラムの稼働時間が長くなると問題が出る場合もあり、注意が必要かと思います。 メモリリークが発生した場合、プロファイラでの調査等、手間がかかりますので、 実装時に注意しておくべきです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Eclipseの初期設定についての備忘録

C#、VisualStudioの村の民だったのですが 次回参画のプロジェクトが初めてのJava/Springのシステムなので 軽く予習もかねてEclipseでSpringのプロジェクト作って動かしてみました。 ということでEclipseの初期設定を備忘録。 まぁVisualStudio入れたときにいつもしてたやつのEclipse版って感じです。 この程度のことはデフォルトで設定していてほしいんですけどね。。。 STS(SpringToolSuite)のインストール ヘルプ>Eclipseマーケットプレースより、 ・Springと検索しインストール ダークテーマの設定 ウインドウ>設定>一般>外観より、 ・テーマ:ダークを選択 エディタの設定 設定>一般>エディター>テキスト・エディターより、 ・以下にチェック  ・行番号の表示  ・タブでスペースを挿入  ・空白文字を表示 UTF-8エンコーディングの設定 ウインドウ>設定>一般>ワークスペースより、 ・テキスト・ファイル・エンコードを「その他」:UTF-8 コンテンツ・アシストの設定 VisualStudioのインテリセンスみたくする方法。 ウインドウ>設定>Java>エディター>コンテンツ・アシストより、 ・自動有効化を使用可能 へチェック ・自動有効化遅延の値を80 ・Javaの自動有効化トリガーに.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 保存アクションの設定 保存時にコードの自動整形してくれる。 ウインドウ>設定>Java>エディター>保存・アクションより、 ・保存時に選択したアクションを実行 へチェック ・すべての行をフォーマット へチェック ・インポートの編成 へチェック 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Aerospikeことはじめ ~セカンドインデックスの生成と検索~

はじめに AerospikeはPrimaryKeyを指定してデータを引き出すKVSだが、 条件を指定して複数Record検索もできるようなのでやってみる。 環境 ソフトウェア バージョン OS Windows10 Docker Engine 20.10.5 Java 11 セカンダリインデックス 概要 AerospikeでPrimaryKeyではなく、Binの値で検索を行いたい場合は 対象のBinに対して、セカンダリインデックスを作成する必要がある。 公式より引用 セカンダリ インデックスは主キー以外のキーに存在し、一対多リレーションシップをモデル化できます。セカンダリ インデックスはビンバイビン (RDBMSカラムの場合と同様) で指定されます。これにより、インデックスの保存に必要なリソースの量を最小限に抑え、効率的な更新が可能になります。 どうやらDBでのデータの持ち方はRDBMSのいわゆるインデックスとほぼ同じっぽい。 RDBMSと異なり、このセカンダリインデックスが張られているBinに対してのみ条件指定検索が可能。 セカンダリインデックスが張られていないBinを条件に指定して検索するとエラーになる。 作成方法 セカンダリインデックスを作成する方法は、AQL(Aerospike Query Language)かJava等のクライアントライブラリのAPIを叩く方法がある。 今回はJavaクライアントライブラリのAPIを利用して作成してみる。 ついでにJavaでインデックスを利用した検索もする。 (AQLでやってみたら、インデックスを作成できても何故かそのインデックスが有効に働かなかった・・) 事前準備 Aerospikeコンテナを起動して、AQLで以下のデータを作成。 (順番がバラバラだが気にしない) テストデータ aql> SELECT * FROM test.MarioGames +-------------------+----------+---------+ | Name | GameHard | Release | +-------------------+----------+---------+ | "SuperMario64" | "N64" | 1996 | | "SuperMarioUSA" | "FC" | 1992 | | "SuperMarioBros2" | "FC" | 1986 | | "SuperMarioWorld" | "SFC" | 1990 | | "SuperMarioLand" | "GB" | 1989 | | "SuperMarioBros3" | "FC" | 1988 | | "SuperMarioLand2" | "GB" | 1992 | | "SuperMarioBros" | "FC" | 1985 | +-------------------+----------+---------+ 8 rows in set (0.094 secs) OK 実装 1. Aerospikeに接続 とりあえず単一ノードに接続。 AerospikeClient client = new AerospikeClient("127.0.0.1", 3000); 2. セカンダリインデックス生成 String Namespace = "test"; String Set = "MarioGames"; String IndexName = "MarioGamesIDX1"; String IndexTargetBin = "GameHard"; // ①インデックス生成 IndexTask task = client.createIndex( null, // Policy。nullでいいらしい Namespace, // Namespace Set, // Set IndexName, // Index名 IndexTargetBin, // Index対象Bin IndexType.STRING); // 型。文字列ならSTRING、数値型ならNUMERIC task.waitTillComplete(); 上記コードを実行すると、セカンダリインデックスが生成されるまで待機し、 生成されれば後続コードを実行する。 セカンダリインデックスが生成されたかどうかは、 AQLで以下のコマンドで確認できる。 実行結果 aql> SHOW INDEXES test +--------+------------+-----------+--------------+-------+------------------+------------+----------+ | ns | bin | indextype | set | state | indexname | path | type | +--------+------------+-----------+--------------+-------+------------------+------------+----------+ | "test" | "GameHard" | "NONE" | "MarioGames" | "RW" | "MarioGamesIDX1" | "GameHard" | "STRING" | +--------+------------+-----------+--------------+-------+------------------+------------+----------+ [127.0.0.1:3000] 1 row in set (0.001 secs) OK インデックス「MarioGamesIDX1」ができた? 3. クエリ生成 // ②Statementを作成 Statement stmt = new Statement(); stmt.setNamespace(Namespace); stmt.setSetName(Set); // ③抽出条件指定 stmt.setFilter(Filter.equal(IndexTargetBin, "FC")); 検索クエリとなるStatementを生成する。 ②でNamespace、Setの指定と ③で抽出条件を指定する。 この例の場合、GameHardがファミコン(FC)のものを指定している。 検索条件の指定の仕方として、StatementのsetFilterメソッドにFilterオブジェクトを渡す。 FilterクラスにFilterオブジェクトを生成するstaticヘルパーメソッドがいくつか用意されているので それを利用すればよい。 equalメソッドの他にもcontainsメソッドやrangeメソッドなどがある。 Filterクラス APIドキュメント 4. クエリ送信、結果取得 // ④クエリ送信 QueryPolicy qPolicy = new QueryPolicy(); RecordSet rs = client.query(qPolicy, stmt); // ⑤結果出力 try { while (rs.next()) { Key key = rs.getKey(); Record record = rs.getRecord(); // RecordからBinの値を取得 System.out.printf("Key=[%s] Name=[%s] GameHard=[%s] Release=[%d]\n", key, record.getString("Name"), record.getString("GameHard"), record.getInt("Release") ); } } finally { rs.close(); } ④で検索クエリを送信し、 ⑤で結果を受け取り、出力をしている。 QueryPolicyは特に指定するものが無ければnullでもいいようだ。 見てのとおり、JDBCでDBアクセスしているような感覚で実装できる。 上記のコードの実行結果は下記。 実行結果 Key=[test:MarioGames:null:478187846c7b97110c6e0588481e387cb77cc076] Name=[SuperMarioUSA] GameHard=[FC] Release=[1992] Key=[test:MarioGames:null:283523796855567793a088f4cc6d0b152516812d] Name=[SuperMarioBros2] GameHard=[FC] Release=[1986] Key=[test:MarioGames:null:50090e5fc99f5c11390d547d0b3c9504e3f0d49a] Name=[SuperMarioBros3] GameHard=[FC] Release=[1988] Key=[test:MarioGames:null:be0e183510a456a9ce6d3d920d7a2a74ef93c1e1] Name=[SuperMarioBros] GameHard=[FC] Release=[1985] ファミコンのマリオのゲームが取得できた? おわりに 今回はとりあえずここまで。 セカンダリインデックスが張られていないとBinの値を指定した絞り込み検索ができないので システムの検索仕様とDB仕様を固めたあとにセカンダリインデックス生成AQLクエリを作って DBで実行する流れになると思う(本来は) セカンダリインデックス生成中はクエリを受け付けないっぽい? ので本番などで気軽には実行できなさそう。(当然だが) セカンダリインデックスが張られているBinの値を更新した場合、 インデックスのデータも更新されるらしい。 ここら辺もRDBMSと似ている。 参考 セカンダリ インデックス https://docs.aerospike.com/docs/architecture/secondary-index.html# Java クライアントクエリ https://docs.aerospike.com/docs/client/java/usage/query/index.html APIリファレンス https://docs.aerospike.com/apidocs/java/ Aerospike で aql を使ってデータ操作を試してみる https://qiita.com/amotz/items/b8f52e9e09bce1ddea6b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Aerospikeことはじめ ~セカンダリインデックスの生成と検索~

はじめに AerospikeはPrimaryKeyを指定してデータを引き出すKVSだが、 条件を指定して複数Record検索もできるようなのでやってみる。 環境 ソフトウェア バージョン OS Windows10 Docker Engine 20.10.5 Java 11 Aerospike CommunityEdition-5.5.0.9 セカンダリインデックス 概要 AerospikeでPrimaryKeyではなく、Binの値で検索を行いたい場合は 対象のBinに対して、セカンダリインデックスを作成する必要がある。 公式より引用 セカンダリ インデックスは主キー以外のキーに存在し、一対多リレーションシップをモデル化できます。セカンダリ インデックスはビンバイビン (RDBMSカラムの場合と同様) で指定されます。これにより、インデックスの保存に必要なリソースの量を最小限に抑え、効率的な更新が可能になります。 どうやらDBでのデータの持ち方はRDBMSのいわゆるインデックスとほぼ同じっぽい。 RDBMSと異なり、このセカンダリインデックスが張られているBinに対してのみ条件指定検索が可能。 セカンダリインデックスが張られていないBinを条件に指定して検索するとエラーになる。 作成方法 セカンダリインデックスを作成する方法は、AQL(Aerospike Query Language)かJava等のクライアントライブラリのAPIを叩く方法がある。 今回はJavaクライアントライブラリのAPIを利用して作成してみる。 ついでにJavaでインデックスを利用した検索もする。 (AQLでやってみたら、インデックスを作成できても何故かそのインデックスが有効に働かなかった・・) 事前準備 Aerospikeコンテナを起動して、AQLで以下のデータを作成。 (順番がバラバラだが気にしない) テストデータ aql> SELECT * FROM test.MarioGames +-------------------+----------+---------+ | Name | GameHard | Release | +-------------------+----------+---------+ | "SuperMario64" | "N64" | 1996 | | "SuperMarioUSA" | "FC" | 1992 | | "SuperMarioBros2" | "FC" | 1986 | | "SuperMarioWorld" | "SFC" | 1990 | | "SuperMarioLand" | "GB" | 1989 | | "SuperMarioBros3" | "FC" | 1988 | | "SuperMarioLand2" | "GB" | 1992 | | "SuperMarioBros" | "FC" | 1985 | +-------------------+----------+---------+ 8 rows in set (0.094 secs) OK 実装(Java) 1. Aerospikeに接続 とりあえず単一ノードに接続。 AerospikeClient client = new AerospikeClient("127.0.0.1", 3000); 2. セカンダリインデックス生成 String Namespace = "test"; String Set = "MarioGames"; String IndexName = "MarioGamesIDX1"; String IndexTargetBin = "GameHard"; // ①インデックス生成 IndexTask task = client.createIndex( null, // Policy。nullでいいらしい Namespace, // Namespace Set, // Set IndexName, // Index名 IndexTargetBin, // Index対象Bin IndexType.STRING); // 型。文字列ならSTRING、数値型ならNUMERIC task.waitTillComplete(); 上記コードを実行すると、セカンダリインデックスが生成されるまで待機し、 生成されれば後続コードを実行する。 ちなみに既にDBに存在するインデックス名を指定するとエラーになる。 (ので、このコードは検索の度に毎回実行するようなものではない。) セカンダリインデックスが生成されたかどうかは、 AQLで以下のコマンドで確認できる。 実行結果 aql> SHOW INDEXES test +--------+------------+-----------+--------------+-------+------------------+------------+----------+ | ns | bin | indextype | set | state | indexname | path | type | +--------+------------+-----------+--------------+-------+------------------+------------+----------+ | "test" | "GameHard" | "NONE" | "MarioGames" | "RW" | "MarioGamesIDX1" | "GameHard" | "STRING" | +--------+------------+-----------+--------------+-------+------------------+------------+----------+ [127.0.0.1:3000] 1 row in set (0.001 secs) OK インデックス「MarioGamesIDX1」ができた? 3. クエリ生成 // ②Statementを作成 Statement stmt = new Statement(); stmt.setNamespace(Namespace); stmt.setSetName(Set); // ③抽出条件指定 stmt.setFilter(Filter.equal(IndexTargetBin, "FC")); 検索クエリとなるStatementを生成する。 ②でNamespace、Setの指定と ③で抽出条件を指定する。 この例の場合、GameHardがファミコン(FC)のものを指定している。 検索条件の指定の仕方として、StatementのsetFilterメソッドにFilterオブジェクトを渡す。 FilterクラスにFilterオブジェクトを生成するstaticヘルパーメソッドがいくつか用意されているので それを利用すればよい。 equalメソッドの他にもcontainsメソッドやrangeメソッドなどがある。 Filterクラス APIドキュメント 4. クエリ送信、結果取得 // ④クエリ送信 QueryPolicy qPolicy = new QueryPolicy(); RecordSet rs = client.query(qPolicy, stmt); // ⑤結果出力 try { while (rs.next()) { Key key = rs.getKey(); Record record = rs.getRecord(); // RecordからBinの値を取得 System.out.printf("Key=[%s] Name=[%s] GameHard=[%s] Release=[%d]\n", key, record.getString("Name"), record.getString("GameHard"), record.getInt("Release") ); } } finally { rs.close(); } ④で検索クエリを送信し、 ⑤で結果を受け取り、出力をしている。 QueryPolicyは特に指定するものが無ければnullでもいいようだ。 見てのとおり、JDBCでDBアクセスしているような感覚で実装できる。 上記のコードの実行結果は下記。 実行結果 Key=[test:MarioGames:null:478187846c7b97110c6e0588481e387cb77cc076] Name=[SuperMarioUSA] GameHard=[FC] Release=[1992] Key=[test:MarioGames:null:283523796855567793a088f4cc6d0b152516812d] Name=[SuperMarioBros2] GameHard=[FC] Release=[1986] Key=[test:MarioGames:null:50090e5fc99f5c11390d547d0b3c9504e3f0d49a] Name=[SuperMarioBros3] GameHard=[FC] Release=[1988] Key=[test:MarioGames:null:be0e183510a456a9ce6d3d920d7a2a74ef93c1e1] Name=[SuperMarioBros] GameHard=[FC] Release=[1985] ファミコンのマリオのゲームが取得できた? おわりに 今回はとりあえずここまで。 セカンダリインデックスが張られていないとBinの値を指定した絞り込み検索ができないので システムの検索仕様とDB仕様を固めたあとにセカンダリインデックス生成AQLクエリを作って DBで実行する流れになると思う(本来は) セカンダリインデックス生成中はクエリを受け付けないっぽい? ので本番などで気軽には実行できなさそう。(当然だが) セカンダリインデックスが張られているBinの値を更新した場合、 インデックスのデータも更新されるらしい。 ここら辺もRDBMSと似ている。 参考 セカンダリ インデックス https://docs.aerospike.com/docs/architecture/secondary-index.html# Java クライアントクエリ https://docs.aerospike.com/docs/client/java/usage/query/index.html APIリファレンス https://docs.aerospike.com/apidocs/java/ Aerospike で aql を使ってデータ操作を試してみる https://qiita.com/amotz/items/b8f52e9e09bce1ddea6b
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

借金返済プログラム

対象読者 ・プログラミング初心者の方 ・勉強法が分からない方 ・その他、記事に興味のある方 はじめに 昔自作した問題が出てきたのでご紹介します。 よくある感じの問題で特に面白みもないですが... プログラミングを勉強したての頃、簡単な問題を作っては、覚えた文法を使って解いてみるということをよくやっていました。覚えた文法だけではどうにもならない箇所は適宜調べるようにしていました。 カレンダーを見るに、どうやら今回の問題を作った当時はプログラミングを始めて10日目だったようなので文法も全く勉強し終えていませんでした。 ただ、どうしても最初は文法の勉強に終始してしまいがちですが、僕は文法だけ勉強しても自分でコードを書かないと腹に落ちない部分が大きかったです。 なので、個人的には勉強したての人もどんどんコードを書いていった方が良いんじゃないかなと思ってます。 プログラミングの勉強法 プログラミングってどう勉強するのが良いんでしょう。 僕自身まだプログラミングを学び始めて7ヶ月なので分からないことも多いですが、それでも初期に比べればかなりプログラミングというものの輪郭が見えてきたように感じます。 現時点では、「はじめに」にも少し書いたように「何でも良いから調べながら作る」というのが一番大切だと考えています。 未だに何か実装したいとなったら調べながら作ることがほとんどです。 というか、この先どれだけ経験を積んでいったとしても、何も調べずに実装できるようになる日は来ないと思っています。 プログラミングに関連する知識は膨大で、とても全てを覚えるのは不可能だからです。 なのでプログラミングには「分からないことを調べる力」が最も大切であると考えています。 初心が本を読んでいるだけでは中々「調べる力」は身に付きませんから、自分で実際に何か作るのが大切というわけです。 自分で作って詰まってみて、ネットや本で解決策を探して、、、と試行錯誤するしかありません。(これが嫌な人にはこの仕事は向いてないかもしれません。) 中々解決策が見つからずに時間を取られることもありますが、思わぬ副産物が得られることも多いです。解決策を探す中で、直接解決には繋がらなくともタメになる知識がストックされていきますから、イライラせず楽しくやりましょう。 では、結局全てを覚えることは不可能で、調べながら作るしかないのなら、なぜ文法や機能を体系立てて勉強するのでしょう。 その理由は、さっき言ったことと少し矛盾するようですが、「調べる力」を高めるためだと考えています。 何も作らず知識を詰め込む「だけ」ではいけませんが、知識を詰め込むこと自体は「調べる力」を補助的に高めてくれます。 いくら勉強しても細かい文法なんかは抜けていってしまいますが「どんなことが出来るか」ぐらいは覚えているものです。 時間の表示ができたなーとかマウスが乗ったタイミングで画像を動かせたなーとかそんな程度で構いません。 何が出来るかさえ覚えていれば、それをいざ使いたい時に調べる時間はほとんどかからないでしょう。 また、何が出来るかという知識のストックが多ければ多いほど、それらを組み合わせることで機能を実現する見通しが立ちやすいです。 調べれば作れることは分かっていますし、調べるのにもそれほど時間はかからないですから、スムーズに実装することが可能になるでしょう。 なので文法なんかは、こんなん忘れても調べればいいやってスタンスで流してしまって、その言語を使ってどんなことが出来るのがを抑えていくのがオススメです。 抽象的な話で全てを上手く説明するのは難しいですが、最終的には今勉強している知識を調べて利用したいということさえ念頭に置いておけば、どう勉強すればいいかは自ずと分かってくると思います。 月々の返済額を求めるプログラム この問題自体は今回お伝えしたかったメインの内容ではないのでサラッとやります。 問題. debt円の借金をmonthヶ月で返済したい. 毎月の返済額paymentを求めよ. ただし月利をrate(>0)とする. (debt,month,rateは入力された値を用いる) import java.io.*; public class PayBackDebt{ public static void main(String[] args) throws IOException { int debt, month; //借金額, 返済月数 double rate; //月利 BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); System.out.println("ご利用は計画的に!"); System.out.println("借入金を入力してください(単位:円)"); debt = Integer.parseInt(in.readLine()); System.out.println("月利を入力してください(単位:%) ただし0は不可"); rate = Double.parseDouble(in.readLine())/100; //変換して格納 System.out.println("完済までの期間を入力してください(単位:月)"); month = Integer.parseInt(in.readLine()); System.out.println("月々の返済額は" + calcPayment(debt, rate, month) + "円です"); } public static int calcPayment(int debt, double rate, int month){ int payment = (int)debt/month; //仮の返済月額を設定(借金額/返済月数) double debtRemain = debt; //借金残高(初期値は総借金額) //完済成功までループ(指定月数後に借金残高<=0) do{ debtRemain = debt; //借金残高をリセット payment += 1; //返済月額を+1円 //(残高×(1+月利)-返済月額)を月数分ループ for(int i = 0; i < month; i++){ debtRemain = debtRemain*(1 + rate) - payment; } }while(debtRemain > 0); return payment; } } 仮の返済額を「借金額/返済月数」(すなわち利子が無いとき)として設定して、そこから1円ずつ返済額を上げながら、指定された月数で完済できるかを調べています。 常識的な借金額や利率であれば実行速度に問題はありませんが、計算量を減らしたければ適当に返済額の上限を見つけて二分探索をしてもよいでしょう。 補足 漸化式を解けば簡単に一般解も求められます。 $$ 返済額=\frac{借入金×月利×(1+月利)^{返済月数}}{(1+月利)^{返済月数}-1} $$ 一般解を出してしまえば実行速度を気にする必要もないですね。 今回はプログラミングの練習なのでわざわざコードを書きましたが、求められるなら一般解を求めましょう。 最後まで読んでいただいてありがとうございました!どんどんコードを書いて力を付けていきましょう?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミングの勉強法

対象読者 ・プログラミング初心者の方 ・勉強法が分からない方 ・その他、記事に興味のある方 はじめに プログラミングの勉強法について話しつつ、昔自作した問題が出てきたのでサラッとご紹介したいと思います。 よくある感じの問題で特に面白みもないですが... プログラミングを勉強したての頃、簡単な問題を作っては、覚えた文法を使って解いてみるということをよくやっていました。覚えた文法だけではどうにもならない箇所は適宜調べるようにしていました。 カレンダーを見るに、どうやら今回の問題を作った当時はプログラミングを始めて10日目だったようなので文法も全く勉強し終えていませんでした。 ただ、どうしても最初は文法の勉強に終始してしまいがちですが、僕は文法だけ勉強しても自分でコードを書かないと腹に落ちない部分が大きかったです。 なので、個人的には勉強したての人もどんどんコードを書いていった方が良いんじゃないかなと思ってます。 プログラミングの勉強法 残念ながら(?)具体的に「この本をやるべし」みたいな話ではありません。 プログラミングってどう勉強するのが良いんでしょう。 僕自身まだプログラミングを学び始めて7ヶ月なので分からないことも多いですが、それでも初期に比べればかなりプログラミングというものの輪郭が見えてきたように感じます。 現時点では、「はじめに」にも少し書いたように「何でも良いから調べながら作る」というのが一番大切だと考えています。 未だに何か実装したいとなったら調べながら作ることがほとんどです。 というか、この先どれだけ経験を積んでいったとしても、何も調べずに実装できるようになる日は来ないと思っています。 プログラミングに関連する知識は膨大で、とても全てを覚えるのは不可能だからです。 なのでプログラミングで最も必要な能力は、「分からないことを調べる力」です。 初心者が本を読んでいるだけでは中々「調べる力」は身に付きませんから、自分で実際に何か作るのが大切というわけです。 自分で作って詰まってみて、ネットや本で解決策を探して、、、と試行錯誤するしかありません。(これがどうしても嫌な人にはこの仕事は向いてないかもしれません。) 中々解決策が見つからずに時間を取られることもありますが、思わぬ副産物が得られることも多いです。解決策を探す中で、直接解決には繋がらなくともタメになる知識がストックされていきますから、イライラせず楽しくやりましょう。 では、結局全てを覚えることは不可能で、調べながら作るしかないのなら、なぜ文法や機能を体系立てて勉強するのでしょう。 その理由は、さっき言ったことと少し矛盾するようですが、「調べる力」を高めるためだと考えています。 何も作らず知識を詰め込む「だけ」ではいけませんが、知識を詰め込むこと自体は「調べる力」を補助的に高めてくれます。(より正確には調べる手間を大幅にカットしてくれます。) いくら勉強しても細かい文法なんかは抜けていってしまいますが、「どんなことが出来るか」ぐらいは覚えているものです。 時間の表示ができたなーとかマウスが乗ったタイミングで画像を動かせたなーとかそんな程度で構いません。 一度腰を据えて学習した内容なら、何が出来るかさえ覚えていれば、それをいざ使いたい時に調べる時間はほとんどかからないでしょう。 また、何が出来るかという知識のストックが多ければ多いほど、それらを組み合わせることで機能を実現する見通しが立ちやすいです。 調べれば作れることは分かっていますし、調べるのにもそれほど時間はかからないですから、スムーズに実装することが可能になるでしょう。 なので文法なんかは、こんなん忘れても調べればいいやってスタンスで流してしまって、その言語を使ってどんなことが出来るのかを抑えていくのがオススメです。 最終的には今勉強している知識を調べて利用したいということさえ念頭に置いておけば、どう勉強すればいいかは何となく分かってくると思います。 なんだかふわっとした話になってしまいましたが、まとめると ・何でも良いから自分で作る。 ・嫌がらずにどんどん調べる。 ・知識も大事。知識を詰め込む時はそれを後から調べて使うことを意識する。 月々の返済額を求めるプログラム この問題自体は今回のメインの内容ではないのでサラッとやります。 自分で何かしら作りたいものを考えて実装する一例としてご参考になれば幸いです。 問題. debt円の借金をmonthヶ月で返済したい. 毎月の返済額paymentを求めよ. ただし月利をrate(>0)とする. (debt,month,rateは入力された値を用いる.) import java.io.*; public class PayBackDebt{ public static void main(String[] args) throws IOException { int debt, month; //借金額, 返済月数 double rate; //月利 BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); System.out.println("ご利用は計画的に!"); System.out.println("借入金を入力してください(単位:円)"); debt = Integer.parseInt(in.readLine()); System.out.println("月利を入力してください(単位:%) ただし0は不可"); rate = Double.parseDouble(in.readLine())/100; //変換して格納 System.out.println("完済までの期間を入力してください(単位:月)"); month = Integer.parseInt(in.readLine()); System.out.println("月々の返済額は" + calcPayment(debt, rate, month) + "円です"); } public static int calcPayment(int debt, double rate, int month){ int payment = (int)debt/month; //仮の返済月額を設定(借金額/返済月数) double debtRemain = debt; //借金残高(初期値は総借金額) //完済成功までループ(指定月数後に借金残高<=0) do{ debtRemain = debt; //借金残高をリセット payment += 1; //返済月額を+1円 //(残高×(1+月利)-返済月額)を月数分ループ for(int i = 0; i < month; i++){ debtRemain = debtRemain*(1 + rate) - payment; } }while(debtRemain > 0); return payment; } } 仮の返済額を「借金額/返済月数」(すなわち利子が無いとき)として設定して、そこから1円ずつ返済額を上げながら、指定された月数で完済できるかを調べています。 常識的な借金額や利率であれば実行速度に問題はありませんが、計算量を減らしたければ適当に返済額の上限を見つけて二分探索をしてもよいでしょう。 補足 漸化式を解けば簡単に一般解も求められます。 $$ 返済額=\frac{借入金×月利×(1+月利)^{返済月数}}{(1+月利)^{返済月数}-1} $$ 一般解を出してしまえば実行速度を気にする必要もないですね。 今回はプログラミングの練習なのでコードを書きましたが、求められるなら一般解を求めましょう。 最後まで読んでいただいてありがとうございました!どんどんコードを書いて調べて力を付けていきましょう?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む