質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.95%

javaで効率のよい書き方

解決済

回答 6

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,848

score 48

いつもお世話になっております。

今回は効率化(?)について質問させていただきます。

やりたいこと

String型の配列に入っている”2016/07/20”という文字列を使って2つのmapを作りたいんです。

例)

String[] list =
{
"2014/12/01",
"2015/01/01",
//~~~~~中略~~~~~~
"2016/05/01",
}

//上記のものを


//下記の形に動的に書き換えたいのですが...
private Map<Integer,String> years;
private Map<String,List<String>> yearMonths;

years.put("0", "2014");
years.put("1", "2015");
years.put("2", "2016");

yearMonths.put("2015", Arrays.asList("12"));
yearMonths.put("2016", Arrays.asList("1","2",/*中略*/"11","12"));
yearMonths.put("2017", Arrays.asList("1","2","3","4","5"));

自分で作ってみたはいいもののコレは実用化できるものなのか?と思い質問させていただきました。
ちなみに今はyearMonthsの中身が連続していますが後々虫食いになる可能性があるので動的に生成させています。

下記が自作で作ったものです。
※mDが上記のlistの代わりのものです。

//private Map<Integer,String> years;                   ...①
//private Map<String,List<String>> yearMonths;          ...②

//②のList<String>の部分に入れ込む配列を生成
ArrayList<String> monthlist = new ArrayList<String>(); // ...③

 for (int i = 0; i < mD.size(); i++){

              if(i == 0){ /**初回*/

                  //最小年の①を作成
                  years.put(i,mD.get(i).getMonth().substring(0, 4));

                  //③に月を追加
                  monthlist.add(mD.get(i).getMonth().substring(5, 7));

              }else{      /**二回目以降*/


                  //1つ前の配列と現在の配列で年の部分が同値でないかの判定
                  if(mD.get(i).getMonth().substring(0, 4).equals(years.get(years.size()-1)) )
                  {
                     /**同値の場合*/

                     //③に月を追加
                     monthlist.add(mD.get(i).getMonth().substring(5, 7));

                 }else{

                    /**同値でない場合*/

                     //今まで月を追加してきた③のリストを使い②を作成する
                     yearMonths.put(mD.get(i - 1).getMonth().substring(0, 4), monthlist);
                     //挿入した③のリストはいらないので初期化
                      monthlist = new ArrayList<String>();

                      //次年度の①を作成する
                     years.put(years.size(),mD.get(i).getMonth().substring(0, 4));

                     //③に月を追加
                     monthlist.add(mD.get(i).getMonth().substring(5, 7));
                 }

                  if(i == mD.size()-1){
                      //最後に今まで月を追加してきた③のリストを使い②を作成する
                      yearMonths.put(mD.get(i - 1).getMonth().substring(0, 4), monthlist);
            //挿入した③のリストはいらないので初期化
                      monthlist = new ArrayList<String>();
                  }
          }
 }


いかがでしょうか。

ご教授願います。

追記

効率について追記させていただきます。

なるべく負荷のかからない形
もしくは
実行時間が短い形
の2点を想定しています。

javaのバージョンは8です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • coco_bauer

    2016/07/21 15:13

    「効率」の定義は? コードが短い(文字数が少ない)、実行時間が短い(実行が早い)など、視点が異なる様々な効率がありうるかと思うのですが。

    キャンセル

  • settyan

    2016/07/21 15:20

    そうですよね...
    何の効率を書かずになんて...
    効率について追記させていただきました。
    ご指摘ありがとうございました。

    キャンセル

  • yuba

    2016/07/21 15:21

    Javaのバージョンは8ですか?

    キャンセル

  • settyan

    2016/07/21 15:27 編集

    ご指摘ありがとうございます。
    java8です。

    キャンセル

回答 6

+3

ストリームの部分を、他の言語でのタプルを使うイメージで書いてみました。
ただ、SimpleEntryを使うのはたぶんあまり一般的じゃないですね。

yearsの方は、ストリームにするとかなり見苦しくなりそうなので単純なループにしました。

効率が良いかどうかはなんとも言えません。

// import java.util.*;
// import java.util.AbstractMap.SimpleEntry;
// import java.util.stream.*;

String[] list = { "2014/12/01", "2015/01/01", "2015/02/01", "2015/03/01", "2015/04/01", "2015/05/01",
        "2015/06/01", "2016/05/01", };

Map<Integer, String> years = new HashMap<>();
Map<String, List<String>> yearMonths = Stream.of(list).map(x -> {
    String[] a = x.split("/");
    return new SimpleEntry<String, String>(a[0], a[1]);
}).collect(Collectors.groupingBy(SimpleEntry::getKey,
                                 Collectors.mapping(SimpleEntry::getValue,
                                                    Collectors.toList())));

List<String> yearList = new ArrayList<>(yearMonths.keySet());
Collections.sort(yearList);
for (int i = 0, n = yearList.size(); i < n; i++) {
    years.put(i, yearList.get(i));
}

System.out.println(years);
// => {0=2014, 1=2015, 2=2016}
System.out.println(yearMonths);
// => {2016=[05], 2015=[01, 02, 03, 04, 05, 06], 2014=[12]}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 09:10

    ご解答ありがとうございました!
    試行錯誤してみます!

    キャンセル

+1

Map<String,List<String>> yearMonths =new Map<>();
for(String date : mD){
   String[] dateArray = date.split("¥/");
   if(!yearMonths.contains(dateArray[0]){
   yearMonths.put(dateArray[0],new ArrayList<String>());
}
yearMonths.get(dateArray[0]).add(dateArray[1]);
}


あとはyearMonthsのkeyを並び替えるとyearsが取得できます。

動作確認して無いですけど、多分こんな感じかな。速さとかはわからないですが。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 09:11

    ご解答ありがとうございました!
    試行錯誤してみます!

    キャンセル

+1

final Map<String, List<String>> yearMonths = Stream.of(list)
                .map(row -> row.split("/"))
                .collect(Collectors.groupingBy(row -> row[0]))
                .entrySet()
                .stream()
                .collect(Collectors.toMap(
                        g -> g.getKey(),
                        g -> g.getValue().stream()
                                .map(row -> row[1])
                                .collect(Collectors.toList())));


Java8だとこういう書き方をするんですが、うーん、これはこれでわかりにくいやつですね⋯


追記。Java7以前の機能だけを使った書き方だとこうなります。

Map<String, List<String>> yearMonths = new HashMap<>();
        for (String line: list) {
            String[] date = line.split("/");
            String year = date[0], month = date[1];
            List<String> monthList = yearMonths.get(year);
            if (monthList == null) {
                monthList = new ArrayList<>();
                yearMonths.put(year, monthList);
            }
            monthList.add(month);
        }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 09:11

    java8だとそんな書き方があるんですね...
    ご解答ありがとうございました!
    試行錯誤してみます!

    キャンセル

+1

手元にJava8の環境がないため、Java7で動作確認してあります。

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

public class Hoge {

    private String[] list = {
            "2014/12/01",
            "2015/01/01",
            "2014/01/20",
            "2016/01/01",
            "2016/02/01",
            "2016/03/01",
            "2016/04/01",
            "2016/05/01",
            "2016/06/01",
            "2016/07/01",
            "2016/08/01",
            "2016/09/01",
            "2016/10/01",
            "2016/11/01",
            "2016/12/01",
            "2016/12/02",
    };

    private Map<Integer, String> years = new HashMap<>();
    private Map<String, List<String>> yearMonths = new HashMap<>();

    public void doSomething() {

        SortedSet<String> tmpYears = new TreeSet<>();
        Map<String, SortedSet<String>> tmpYearMonths = new HashMap<>();

        for (String line : list) {
            String[] splitted = line.split("\\/");

            tmpYears.add(splitted[0]);

            if (tmpYearMonths.containsKey(splitted[0])) {
                SortedSet<String> months = tmpYearMonths.get(splitted[0]);
                months.add(splitted[1]);
            } else {
                SortedSet<String> months = new TreeSet<>();
                months.add(splitted[1]);
                tmpYearMonths.put(splitted[0], months);
            }
        }

        int i = 0;
        for (String line : tmpYears) {
            years.put(i++, line);
        }

        for (Map.Entry<String, SortedSet<String>> entry : tmpYearMonths.entrySet()) {
            yearMonths.put(entry.getKey(), Arrays.asList(entry.getValue().toArray(new String[] {})));
        }

        System.out.println(years);
        System.out.println(yearMonths);
    }
}

実行結果

{0=2014, 1=2015, 2=2016}
{2014=[01, 12], 2015=[01], 2016=[01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12]}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 09:10

    ご解答ありがとうございました!
    試行錯誤してみます!

    キャンセル

+1

年の順番付きのリストをなぜMapでやる必要があるのかわからなかったため、LinkedHashMapに一任しました。一応年のListへの変換もしておきました。
Mapへの追加は、computeIfAbsentを使えばキーが入ってるかなどの判定がいらずに1行で済みます。

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

public class Q41805 {

    public static void main(String[] args) {
        List<String> years = new ArrayList<>();
        Map<String, List<String>> yearMonths = new LinkedHashMap<>();
        String[] list = {
                "2014/12/01",
                // 中略(連続した年月)
                "2016/05/01",
        };
        for (String date : list) {
            String[] line = date.split("/");
            yearMonths.computeIfAbsent(line[0], k -> new ArrayList<>()).add(line[1]);
        }
        years.addAll(yearMonths.keySet());
        System.out.println(yearMonths);
        System.out.println(years);
    }

}

実行結果

{2014=[12], 2015=[01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12], 2016=[01, 01, 02, 03, 04, 05]}
[2014, 2015, 2016]

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/22 09:09

    LinkedHashMapなるものがあるんですね...
    ご解答ありがとうございました!
    試行錯誤してみます!

    キャンセル

check解決した方法

-1

皆様ご解答ありがとうございました。
様々な書き方を頂いたのですべて試してみようと思います。
僕自身これが言いとまだ決まったわけではないので

ベストアンサーを決めず、自己解決にさせていただきます。
ありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.95%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る