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

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

ただいまの
回答率

87.34%

java CSVデータを読み込み、HashMap<String, <String, Integer>>に要素を追加したい。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 5,363

score 18

CSVデータを一行ずつ読み込み一行を","で区切って配列に代入し、配列の[2](問題番号を表す)、[4](スコアを表す)、その問題番号[2]でそのスコア[4]の人数をHashMap<String, <String, Integer>>に追加していきたいのですが、

HashMap<String, HashMap<String, Integer>> map = new HashMap<>(); //mapの宣言
HashMap<String, Integer> scores = new HashMap<>();
File file = new File(args[0]); //引数のファイルの実体を作成
        BufferedReader in = new BufferedReader(new FileReader(file)); 
String line;
while((line = in.readLine()) != null){ //一行ずつファイルを読みこむ
            String[] array = line.split(","); //1行をコンマで区切る
            if(!map.containsKey(array[2]) || !map.get(array[2]).containsKey(array[4])){
                scores.put(array[4], 1);
                map.put(array[2], scores);
                System.out.println("問題番号:"+ array[2] + "スコア" + array[4] + "人数" + 1);
            }
            else{
                Integer number_p = map.get(array[2]).get(array[4]);
                number_p += 1;
                map.get(array[2]).put(array[4], number_p);
                System.out.println("問題番号"+array[2] + "スコア"+array[4] + "人数"+number_p);
            }
        }


これで実行したところ、問題番号が変わっても1つ前の問題番号のスコアが反映されて人数がおかしくなります。
どのように書けば、HashMap<問題番号, <スコア, 人数>>を作ることができるのでしょうか。
教えていただきたいです。

↓追記です

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.ArrayList;
public class ScoreAnalyzer2{
    public static void main(String args[]) throws IOException{ //mainメソッド
        ScoreAnalyzer2 app = new ScoreAnalyzer2();
        app.run(args);
    }
    void run(String args[]) throws IOException{ //runメソッド
        HashMap<String, HashMap<String, Integer>> map = new HashMap<>(); //mapの宣言
        HashMap<String, Integer> map2 = new HashMap<>(); //スコアと0

        ArrayList<String> list = new ArrayList<>();
        File file = new File(args[0]); //引数のファイルの実体を作成
        BufferedReader in = new BufferedReader(new FileReader(file)); 
        String line;
        while((line = in.readLine()) != null){ //一行ずつファイルを読みこむ
            String[] array = line.split(","); //1行をコンマで区切る
            if(!map.containsKey(array[2]) || !map.get(array[2]).containsKey(array[4])){
                HashMap<String, Integer> scores = new HashMap<>();
                scores.put(array[4], 1);
                map.put(array[2], scores);
                System.out.println("問題番号:"+ array[2] + "スコア" + array[4] + "人数" + 1);
            }
            else{
                Integer number_p = map.get(array[2]).get(array[4]);
                number_p += 1;
                map.get(array[2]).put(array[4], number_p);
                System.out.println("問題番号"+array[2] + "スコア"+array[4] + "人数"+number_p);
            }
        }
    }


最終的にはそれぞれの問題番号のスコアごとに割合も算出したいです。
2019/4/11    reading    1    24    10    9:30    9:31
2019/4/11    reading    1    102    10    9:30    9:40
2019/5/16    reading    5    96    8    9:03    9:09
2019/5/16    reading    5    117    10    9:03    9:07
2019/5/16    reading    5    44    10    9:03    9:09
2019/5/9    reading    4    37    8    9:02    9:09
2019/5/9    reading    4    56    6    9:02    9:07
2019/5/9    reading    4    114    8    9:02    9:10
2019/5/9    reading    4    58    8    9:03    9:10
2019/4/25    reading    3    59    8    9:00    9:12
2019/4/25    reading    3    144    6    9:03    9:11
2019/4/25    reading    3    107    8    9:07    9:13
2019/4/25    reading    3    74    2    9:11    9:14
2019/4/18    reading    2    73    2    9:00    9:13
2019/4/18    reading    2    105    8    9:00    9:10
2019/4/18    reading    2    96    6    9:00    9:07

データは一部抜粋です。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+2

scoresオブジェクトが共有されていることが問題です。
簡単な例だと、例えば次のようなコードで再現できます。

List<List<Integer>> listOfList = new ArrayList<>();
List<Integer> list = new ArrayList<>();

for(int i = 0; i < 3; ++i) {
    list.add(i);
    listOfList.add(list);
}
System.out.println(listOfList);  // => [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

これを避ける為には、毎度新たにオブジェクトを作るようにすれば良いです。

List<List<Integer>> listOfList = new ArrayList<>();

for(int i = 0; i < 3; ++i) {
    List<Integer> list = new ArrayList<>();

    list.add(i);
    listOfList.add(list);
}
System.out.println(listOfList);  // => [[0], [1], [2]]

組んでみた

面白そうだったので、適宜ググりつつ書いてみました。

import java.io.IOException;

import java.nio.file.Files;
import java.nio.file.Paths;

import java.util.HashMap;
import java.util.Map;


class Main {
    public static void main(String[] args) throws IOException {
        Map<Integer, Map<Integer, Integer>> map = new HashMap<>();


        var path = Paths.get("./data.csv");
        try(var in = Files.newBufferedReader(path)) {

            while(true) {
                var line = in.readLine();
                if(line == null) break;

                Integer question, score;
                {
                    var fields = line.split(",");
                    assert fields.length == 5: "ill-formed CSV";

                    question = Integer.valueOf(fields[2]);
                    score    = Integer.valueOf(fields[4]);
                }

                var inner = map.computeIfAbsent(
                    question, HashMap<Integer, Integer>::new
                );
                inner.merge(score, 1, (v1, v2) -> v1 + v2);
            }
        }

        for(var key: map.keySet()) {
            System.out.printf("%d, %s\n", key, map.get(key));
        }
    }
}

もっとイケてる書き方をご存知の方は是非教えて下さい。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/07/29 10:15

    追記しました。

    キャンセル

  • 2019/07/29 15:27

    array[2]用のMapは既に存在するけれど、それにarray[4]用のエントリが無い場合についての考慮が抜けています。
    しかしこの辺りを抜けなく記述しようと思うと存外大変なので、次のように二つの独立した問題として取り扱うことをお勧めします。

    ・ array[2]用のMapは既に存在するか?無い場合は作る。
    ・ 内側のMapについて、array[4]用のエントリは存在するか?無い場合は作る。有る場合は値を更新する。

    キャンセル

  • 2019/07/31 00:19

    ありがとうございます。試してみます。

    キャンセル

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

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

関連した質問

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