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

Java HashMap 中身を覗いて見ました

Java の HashMap に、データを入れながら、中身を覗いて見ました。
Java Version : 1.8

InspectHashMap.java
package com;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class InspectHashMap {

    public static void main(String[] args) throws Exception {
        //Method loadFactor = HashMap.class.getDeclaredMethod("loadFactor");
        //loadFactor.setAccessible(true);
        Field table = HashMap.class.getDeclaredField("table");
        table.setAccessible(true);
        Field threshold = HashMap.class.getDeclaredField("threshold");
        threshold.setAccessible(true);
        Method capacity = HashMap.class.getDeclaredMethod("capacity");
        capacity.setAccessible(true);

        int max = 25;
        Map<String, String> map = new HashMap<>();
        for (int ii = 1; ii <= max; ii++) {
            put(map, String.format("%03d", ii), table, threshold, capacity);
        }
    }

    private static void put(Map<String, String> map, String key$value, Field table, Field threshold, Method capacity) throws Exception {
        int $capacity = (int) capacity.invoke(map);
        System.out.println(String.format("%s : %d : mod(%d)", key$value, key$value.hashCode(), key$value.hashCode() % $capacity));
        map.put(key$value, key$value);
        System.out.println(String.format("%02d, %02d, %02d", map.size(), threshold.getInt(map), capacity.invoke(map)));
        Object[] nodes = (Object[]) table.get(map);
        System.out.println("--------------------------------------------------------------------");
        for (int index = 0; index < nodes.length; index++) {
            printNode(0, index, nodes[index]);
        }
        System.out.println("--------------------------------------------------------------------");
    }

    private static void printNode(int times, int index, Object node) throws Exception {
        if (times == 0) {
            System.out.print(String.format("[%03d]:", index));
        }
        if (node == null) {
            System.out.println(node);
            return;
        }
        System.out.print(node);
        System.out.print(" -> ");
        Field next = node.getClass().getDeclaredField("next");
        next.setAccessible(true);
        printNode(++times, index, next.get(node));
    }
}

25 個のキーを入れました。サイズ、拡張閾値、キャパシティの変化は以下の通り
※ new HashMap<>(); なので、負荷係数=0.75、初期容量=16

01, 12, 16
02, 12, 16
03, 12, 16
04, 12, 16
05, 12, 16
06, 12, 16
07, 12, 16
08, 12, 16
09, 12, 16
10, 12, 16
11, 12, 16
12, 12, 16
13, 24, 32 <---------- rehash 1回目
14, 24, 32
15, 24, 32
16, 24, 32
17, 24, 32
18, 24, 32
19, 24, 32
20, 24, 32
21, 24, 32
22, 24, 32
23, 24, 32
24, 24, 32
25, 48, 64 <---------- rehash 2回目

"001" の hashcode は 47665 なので、
47665 mod 16 = 1
47665 mod 32 = 17
47665 mod 64 = 49
よって、
"001" を put した時:(1番目になったことがわかる)

-[000]:null
-[001]:001=001 -> null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null

rehash 1回目後 (17番目になったことがわかる)

-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> null
-[020]:004=004 -> null
-[021]:005=005 -> null
-[022]:006=006 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null

rehash 2回目後 (49番目になったことがわかる)

-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:012=012 -> null
-[018]:013=013 -> null
-[019]:014=014 -> null
-[020]:015=015 -> null
-[021]:016=016 -> null
-[022]:017=017 -> null
-[023]:018=018 -> null
-[024]:019=019 -> null
-[025]:null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
-[032]:null
-[033]:null
-[034]:null
-[035]:null
-[036]:null
-[037]:null
-[038]:null
-[039]:null
-[040]:null
-[041]:null
-[042]:null
-[043]:null
-[044]:null
-[045]:null
-[046]:020=020 -> null
-[047]:021=021 -> null
-[048]:022=022 -> null
-[049]:001=001 -> 023=023 -> null
-[050]:002=002 -> 024=024 -> null
-[051]:003=003 -> 025=025 -> null
-[052]:004=004 -> null
-[053]:005=005 -> null
-[054]:006=006 -> null
-[055]:007=007 -> null
-[056]:008=008 -> null
-[057]:009=009 -> null
-[058]:null
-[059]:null
-[060]:null
-[061]:null
-[062]:null
-[063]:null

すべての実行結果を貼っどきます

001 : 47665 : mod(1)
01, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
002 : 47666 : mod(2)
02, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
003 : 47667 : mod(3)
03, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
004 : 47668 : mod(4)
04, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
005 : 47669 : mod(5)
05, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
006 : 47670 : mod(6)
06, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
007 : 47671 : mod(7)
07, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
008 : 47672 : mod(8)
08, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:008=008 -> null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
009 : 47673 : mod(9)
09, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:008=008 -> null
-[009]:009=009 -> null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:null
--------------------------------------------------------------------
010 : 47695 : mod(15)
10, 12, 16
--------------------------------------------------------------------
-[000]:null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:008=008 -> null
-[009]:009=009 -> null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
--------------------------------------------------------------------
011 : 47696 : mod(0)
11, 12, 16
--------------------------------------------------------------------
-[000]:011=011 -> null
-[001]:001=001 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:008=008 -> null
-[009]:009=009 -> null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
--------------------------------------------------------------------
012 : 47697 : mod(1)
12, 12, 16
--------------------------------------------------------------------
-[000]:011=011 -> null
-[001]:001=001 -> 012=012 -> null
-[002]:002=002 -> null
-[003]:003=003 -> null
-[004]:004=004 -> null
-[005]:005=005 -> null
-[006]:006=006 -> null
-[007]:007=007 -> null
-[008]:008=008 -> null
-[009]:009=009 -> null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
--------------------------------------------------------------------
013 : 47698 : mod(2)
13, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> null
-[020]:004=004 -> null
-[021]:005=005 -> null
-[022]:006=006 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
014 : 47699 : mod(19)
14, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> null
-[021]:005=005 -> null
-[022]:006=006 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
015 : 47700 : mod(20)
15, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> null
-[022]:006=006 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
016 : 47701 : mod(21)
16, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
017 : 47702 : mod(22)
17, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
018 : 47703 : mod(23)
18, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
019 : 47704 : mod(24)
19, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
020 : 47726 : mod(14)
20, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:020=020 -> null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
021 : 47727 : mod(15)
21, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:020=020 -> null
-[015]:010=010 -> 021=021 -> null
-[016]:011=011 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
022 : 47728 : mod(16)
22, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:020=020 -> null
-[015]:010=010 -> 021=021 -> null
-[016]:011=011 -> 022=022 -> null
-[017]:001=001 -> 012=012 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
023 : 47729 : mod(17)
23, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:020=020 -> null
-[015]:010=010 -> 021=021 -> null
-[016]:011=011 -> 022=022 -> null
-[017]:001=001 -> 012=012 -> 023=023 -> null
-[018]:002=002 -> 013=013 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
024 : 47730 : mod(18)
24, 24, 32
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:020=020 -> null
-[015]:010=010 -> 021=021 -> null
-[016]:011=011 -> 022=022 -> null
-[017]:001=001 -> 012=012 -> 023=023 -> null
-[018]:002=002 -> 013=013 -> 024=024 -> null
-[019]:003=003 -> 014=014 -> null
-[020]:004=004 -> 015=015 -> null
-[021]:005=005 -> 016=016 -> null
-[022]:006=006 -> 017=017 -> null
-[023]:007=007 -> 018=018 -> null
-[024]:008=008 -> 019=019 -> null
-[025]:009=009 -> null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
--------------------------------------------------------------------
025 : 47731 : mod(19)
25, 48, 64
--------------------------------------------------------------------
-[000]:null
-[001]:null
-[002]:null
-[003]:null
-[004]:null
-[005]:null
-[006]:null
-[007]:null
-[008]:null
-[009]:null
-[010]:null
-[011]:null
-[012]:null
-[013]:null
-[014]:null
-[015]:010=010 -> null
-[016]:011=011 -> null
-[017]:012=012 -> null
-[018]:013=013 -> null
-[019]:014=014 -> null
-[020]:015=015 -> null
-[021]:016=016 -> null
-[022]:017=017 -> null
-[023]:018=018 -> null
-[024]:019=019 -> null
-[025]:null
-[026]:null
-[027]:null
-[028]:null
-[029]:null
-[030]:null
-[031]:null
-[032]:null
-[033]:null
-[034]:null
-[035]:null
-[036]:null
-[037]:null
-[038]:null
-[039]:null
-[040]:null
-[041]:null
-[042]:null
-[043]:null
-[044]:null
-[045]:null
-[046]:020=020 -> null
-[047]:021=021 -> null
-[048]:022=022 -> null
-[049]:001=001 -> 023=023 -> null
-[050]:002=002 -> 024=024 -> null
-[051]:003=003 -> 025=025 -> null
-[052]:004=004 -> null
-[053]:005=005 -> null
-[054]:006=006 -> null
-[055]:007=007 -> null
-[056]:008=008 -> null
-[057]:009=009 -> null
-[058]:null
-[059]:null
-[060]:null
-[061]:null
-[062]:null
-[063]:null
--------------------------------------------------------------------

HashMap 意外と無駄が多いね。。。。

以上。

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

【Java】AtcorderのABC-192に参加しました。

こんばんは。

2021/2/20に、AtcorderのABC-192に参加しました。
レートは以下の通りとなっています。

image.png

https://atcoder.jp/users/ishikawaryou

開始から1時間くらいでC問題を突破しました。
D問題の途中で時間切れとなりました。D問題も内容は理解し、ロジックが良い感じに組み立てられそう・・というところでした。

順位は6236位でした。はあ、先は長いなあ・・。
レートは173→184に更新!数字の伸びが鈍くなってきたぞ。

A問題

今までに集めたコインはX枚。
100の倍数ごとにご褒美がもらえる。あと何枚でご褒美がもらえるか。
下2桁を算出し、100から減算して残りの枚数を求める事に。
でも、そうするとXが1桁の時にエラーになりますね。考慮が抜けて一度エラーになりました。
ソースは汚いですが、3桁以上か否かで分岐しています。

import java.util.Scanner;

public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
  String x = sc.next();
  int len = x.length();
  if(len>=3){
  String ju = String.valueOf(x.charAt(len-2));
  String ichi = String.valueOf(x.charAt(len-1));
  //System.out.println(" ju " + ju);
  //System.out.println(" ichi " + ichi);
  String s2 = ju + ichi; 
  int s2i = Integer.parseInt(s2);
  System.out.println(100-s2i);
  }else{
  int s2i2 = Integer.parseInt(x);  
  System.out.println(100-s2i2);
  }
}
}

B問題

先頭から奇数番目の文字が全て英小文字であり、かつ、先頭から偶数番目の文字が全て英大文字であるような文字列を 読みにくい文字列 と呼ぶ。
入力した文字列は読みにくい文字列かどうか。

大文字・小文字の判別・・これか!!
http://www.kenmiya.info/?p=115

強引にこんなロジックにしてしまいました・・・(笑)

import java.util.Scanner;

public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
  String s = sc.next();
  int len = s.length();
  char flg = '0';
  for(int i=0;i<len;i++){
      if(i%2!=0){
      if((s.charAt(i)=='A')||
         (s.charAt(i)=='B')||
         (s.charAt(i)=='C')||
         (s.charAt(i)=='D')||
         (s.charAt(i)=='E')||
         (s.charAt(i)=='F')||
         (s.charAt(i)=='G')||
         (s.charAt(i)=='H')||
         (s.charAt(i)=='I')||
         (s.charAt(i)=='J')||
         (s.charAt(i)=='K')||
         (s.charAt(i)=='L')||
         (s.charAt(i)=='M')||
         (s.charAt(i)=='N')||
         (s.charAt(i)=='O')||
         (s.charAt(i)=='P')||
         (s.charAt(i)=='Q')||
         (s.charAt(i)=='R')||
         (s.charAt(i)=='S')||
         (s.charAt(i)=='T')||
         (s.charAt(i)=='U')||
         (s.charAt(i)=='V')||
         (s.charAt(i)=='W')||
         (s.charAt(i)=='X')||
         (s.charAt(i)=='Y')||
         (s.charAt(i)=='Z')){
         }else{
          flg = '1';
         }
      }else{
      if((s.charAt(i)=='a')||
         (s.charAt(i)=='b')||
         (s.charAt(i)=='c')||
         (s.charAt(i)=='d')||
         (s.charAt(i)=='e')||
         (s.charAt(i)=='f')||
         (s.charAt(i)=='g')||
         (s.charAt(i)=='h')||
         (s.charAt(i)=='i')||
         (s.charAt(i)=='j')||
         (s.charAt(i)=='k')||
         (s.charAt(i)=='l')||
         (s.charAt(i)=='m')||
         (s.charAt(i)=='n')||
         (s.charAt(i)=='o')||
         (s.charAt(i)=='p')||
         (s.charAt(i)=='q')||
         (s.charAt(i)=='r')||
         (s.charAt(i)=='s')||
         (s.charAt(i)=='t')||
         (s.charAt(i)=='u')||
         (s.charAt(i)=='v')||
         (s.charAt(i)=='w')||
         (s.charAt(i)=='x')||
         (s.charAt(i)=='y')||
         (s.charAt(i)=='z')){
         }else{
          flg = '1';
         }
      }        

  }

  if(flg=='1'){
  System.out.println("No");
  }else{
  System.out.println("Yes");
  }

  //System.out.println(100-s2i);
  //System.out.println(100-s2i2);
}
}

C問題

C問題は、かみ砕いてここに記載するのも難しいので、ソースだけ載せたいと思います。
問題文の式の通りに処理を積み上げてテストをしながら、何とか形にできました。

こんなプログラムになったっす!!

import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;

public class Main{
public static void main(String args[]){
  Scanner sc = new Scanner(System.in);
  int n = sc.nextInt();
  String ns = String.valueOf(n);
  int k = sc.nextInt();
  int fwk = n;
  int g1 = 0;
  int g2 = 0;
  char gwk[] = new char[ns.length()]; 
  String gstwk = "";
  int gstwk1 = 0;
  int gstwk2 = 0;
  for(int i=0;i<ns.length();i++){
    gwk[i] = '-';
  }
  String fwk2 = String.valueOf(fwk);
  for(int a=1;a<=k;a++){
      fwk2 = String.valueOf(fwk);
      for(int i=0;i<fwk2.length();i++){
         gwk[i]=fwk2.charAt(i);
      }
      Arrays.sort(gwk);
      gstwk1=0;
      gstwk2=0;
      //g1//
      gstwk = "";
      //for(int i=fwk2.length()-1;i>=0;i--){
      for(int i=ns.length()-1;i>=0;i--){
        if(gwk[i]!='-'){
        gstwk = gstwk + String.valueOf(gwk[i]);
        }
      }
      gstwk1 = Integer.parseInt(gstwk);
      //g1//
      //g2//
      gstwk = "";
      //System.out.println(" gstwk1 " + gstwk1);
      //for(int i=0;i<fwk2.length();i++){
      for(int i=0;i<ns.length();i++){
        if(gwk[i]!='-'){
        gstwk = gstwk + String.valueOf(gwk[i]);
        }
      }
      gstwk2 = Integer.parseInt(gstwk);
      //System.out.println(" gstwk2 " + gstwk2);
      //g1//

      fwk = gstwk1 - gstwk2;
      //System.out.println(" fwk " + fwk);

      //clear
      for(int i=0;i<ns.length();i++){
      gwk[i] = '-';
      }
      //clear
  }
  //test
  //for(int i=0;i<3;i++){
  //System.out.println(gwk[i]);
  //}
  //test

  System.out.println(fwk);

}
}

感想

今回は、自分に深く関わりのある企業が主催のコンテストでした。
それだけにC問題までは絶対に解きたかったので、最低ラインはクリアできました。
D問題、もうちょっと早くできるといいな~。

レート200を直近の目標にして頑張ります。

これからもコツコツ取り組んで、茶色を目指したいと思います!
同じく灰色レベルで頑張っている方、お互い頑張りましょう!

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

【Java】AtCoderのABC-192に参加しました。

こんばんは。

2021/2/20に、AtCoderのABC-192に参加しました。
レートは以下の通りとなっています。

image.png

https://atcoder.jp/users/ishikawaryou

開始から1時間くらいでC問題を突破しました。
D問題の途中で時間切れとなりました。D問題も内容は理解し、ロジックが良い感じに組み立てられそう・・というところでした。

順位は6236位でした。はあ、先は長いなあ・・。
レートは173→184に更新!数字の伸びが鈍くなってきたぞ。

A問題

今までに集めたコインはX枚。
100の倍数ごとにご褒美がもらえる。あと何枚でご褒美がもらえるか。
下2桁を算出し、100から減算して残りの枚数を求める事に。
でも、そうするとXが1桁の時にエラーになりますね。考慮が抜けて一度エラーになりました。
ソースは汚いですが、3桁以上か否かで分岐しています。

import java.util.Scanner;

public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
  String x = sc.next();
  int len = x.length();
  if(len>=3){
  String ju = String.valueOf(x.charAt(len-2));
  String ichi = String.valueOf(x.charAt(len-1));
  //System.out.println(" ju " + ju);
  //System.out.println(" ichi " + ichi);
  String s2 = ju + ichi; 
  int s2i = Integer.parseInt(s2);
  System.out.println(100-s2i);
  }else{
  int s2i2 = Integer.parseInt(x);  
  System.out.println(100-s2i2);
  }
}
}

B問題

先頭から奇数番目の文字が全て英小文字であり、かつ、先頭から偶数番目の文字が全て英大文字であるような文字列を 読みにくい文字列 と呼ぶ。
入力した文字列は読みにくい文字列かどうか。

大文字・小文字の判別・・これか!!
http://www.kenmiya.info/?p=115

強引にこんなロジックにしてしまいました・・・(笑)

import java.util.Scanner;

public class Main{
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
  String s = sc.next();
  int len = s.length();
  char flg = '0';
  for(int i=0;i<len;i++){
      if(i%2!=0){
      if((s.charAt(i)=='A')||
         (s.charAt(i)=='B')||
         (s.charAt(i)=='C')||
         (s.charAt(i)=='D')||
         (s.charAt(i)=='E')||
         (s.charAt(i)=='F')||
         (s.charAt(i)=='G')||
         (s.charAt(i)=='H')||
         (s.charAt(i)=='I')||
         (s.charAt(i)=='J')||
         (s.charAt(i)=='K')||
         (s.charAt(i)=='L')||
         (s.charAt(i)=='M')||
         (s.charAt(i)=='N')||
         (s.charAt(i)=='O')||
         (s.charAt(i)=='P')||
         (s.charAt(i)=='Q')||
         (s.charAt(i)=='R')||
         (s.charAt(i)=='S')||
         (s.charAt(i)=='T')||
         (s.charAt(i)=='U')||
         (s.charAt(i)=='V')||
         (s.charAt(i)=='W')||
         (s.charAt(i)=='X')||
         (s.charAt(i)=='Y')||
         (s.charAt(i)=='Z')){
         }else{
          flg = '1';
         }
      }else{
      if((s.charAt(i)=='a')||
         (s.charAt(i)=='b')||
         (s.charAt(i)=='c')||
         (s.charAt(i)=='d')||
         (s.charAt(i)=='e')||
         (s.charAt(i)=='f')||
         (s.charAt(i)=='g')||
         (s.charAt(i)=='h')||
         (s.charAt(i)=='i')||
         (s.charAt(i)=='j')||
         (s.charAt(i)=='k')||
         (s.charAt(i)=='l')||
         (s.charAt(i)=='m')||
         (s.charAt(i)=='n')||
         (s.charAt(i)=='o')||
         (s.charAt(i)=='p')||
         (s.charAt(i)=='q')||
         (s.charAt(i)=='r')||
         (s.charAt(i)=='s')||
         (s.charAt(i)=='t')||
         (s.charAt(i)=='u')||
         (s.charAt(i)=='v')||
         (s.charAt(i)=='w')||
         (s.charAt(i)=='x')||
         (s.charAt(i)=='y')||
         (s.charAt(i)=='z')){
         }else{
          flg = '1';
         }
      }        

  }

  if(flg=='1'){
  System.out.println("No");
  }else{
  System.out.println("Yes");
  }

  //System.out.println(100-s2i);
  //System.out.println(100-s2i2);
}
}

C問題

C問題は、かみ砕いてここに記載するのも難しいので、ソースだけ載せたいと思います。
問題文の式の通りに処理を積み上げてテストをしながら、何とか形にできました。

こんなプログラムになったっす!!

import java.util.Scanner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;

public class Main{
public static void main(String args[]){
  Scanner sc = new Scanner(System.in);
  int n = sc.nextInt();
  String ns = String.valueOf(n);
  int k = sc.nextInt();
  int fwk = n;
  int g1 = 0;
  int g2 = 0;
  char gwk[] = new char[ns.length()]; 
  String gstwk = "";
  int gstwk1 = 0;
  int gstwk2 = 0;
  for(int i=0;i<ns.length();i++){
    gwk[i] = '-';
  }
  String fwk2 = String.valueOf(fwk);
  for(int a=1;a<=k;a++){
      fwk2 = String.valueOf(fwk);
      for(int i=0;i<fwk2.length();i++){
         gwk[i]=fwk2.charAt(i);
      }
      Arrays.sort(gwk);
      gstwk1=0;
      gstwk2=0;
      //g1//
      gstwk = "";
      //for(int i=fwk2.length()-1;i>=0;i--){
      for(int i=ns.length()-1;i>=0;i--){
        if(gwk[i]!='-'){
        gstwk = gstwk + String.valueOf(gwk[i]);
        }
      }
      gstwk1 = Integer.parseInt(gstwk);
      //g1//
      //g2//
      gstwk = "";
      //System.out.println(" gstwk1 " + gstwk1);
      //for(int i=0;i<fwk2.length();i++){
      for(int i=0;i<ns.length();i++){
        if(gwk[i]!='-'){
        gstwk = gstwk + String.valueOf(gwk[i]);
        }
      }
      gstwk2 = Integer.parseInt(gstwk);
      //System.out.println(" gstwk2 " + gstwk2);
      //g1//

      fwk = gstwk1 - gstwk2;
      //System.out.println(" fwk " + fwk);

      //clear
      for(int i=0;i<ns.length();i++){
      gwk[i] = '-';
      }
      //clear
  }
  //test
  //for(int i=0;i<3;i++){
  //System.out.println(gwk[i]);
  //}
  //test

  System.out.println(fwk);

}
}

感想

今回は、自分に深く関わりのある企業が主催のコンテストでした。
それだけにC問題までは絶対に解きたかったので、最低ラインはクリアできました。
D問題、もうちょっと早くできるといいな~。

レート200を直近の目標にして頑張ります。

これからもコツコツ取り組んで、茶色を目指したいと思います!
同じく灰色レベルで頑張っている方、お互い頑張りましょう!

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

【入門】Droolsのサンプルプログラムを実行するまで

当記事を投稿する動機

配送スケジュールなどの組み合わせ最適化を取り扱うOptaPlanner(※1)は、
ルール言語「DRL(Drools Rule Language)」というものを用いています。
OptaPlannerを使いこなすためには、このDroolsの理解が必要不可欠となります。

一方、日本国内にはDroolsの情報は自分が検索した限りでは多くない状況です。
しかし、参考情報として数少ないDroolsの入門記事(※2)がありましたので、自分の理解を深めるためにゼロから自分でDroolsプロジェクトを作成してみることにしました。
この作業内容をまとめたものが、初めてDroolsを導入する際に他の人が少しでも参考になってもらいたく記事を投稿することにしました。


※1 参考:OptaPlannerの紹介記事
 ・オブジェクト広場 OptaPlannerによる組み合わせ最適化
 ・株式会社日立ソリューションズ OSSプランニングエンジンOptaPlannerを使ってみよう!

OpatPlannerだけでなくDroolsに触れている説明部分もあります。

※2:参考:
 ・赤帽エンジニアブログ
公式リファレンスを見ても情報量が多すぎて理解できなかった自分にとって、Droolsを理解するきっかけとなりました。非常に助かりました。


前提情報

記事を読むにあたっての前提条件:

・Eclipseの基本操作を知っている人。
・「DRL(Drools Rule Language)」を新規プロジェクト作成から疎通用のプログラムを実行するまで分からない人

動作環境

 - Linux Ubuntu 18.04.5.LTS
 - Eclipse Version: 2020-12 (4.18.0)

EclipseでDroolsを使えるようにセットアップ

インストール手順はこちらを参考にして下さい。
細かく手順が説明されていてわかりやすいです。

参考:オブジェクト広場 第3回:EclipseでOptaPlannerを動かしてみる

Droolsプロジェクト新規作成

下記のWebページではサンプル資産が提供されていますが、Droolsの理解を深めるためにゼロからプロジェクトを作成してきたいと思います。

Droolsブログ : 01 Drools HelloWorld

Eclipseを立ち上げまして、新規プロジェクトを作成します。
"Drools Project"を探し出します。

Screenshot from 2021-02-20 16-27-08.png

”Drools Project”を選択して、次へ進みます。

新規作成するDroolsProjectの雛形を選択しろ、という画面が出ました。
とりあえず、一番上の空っぽ(empty project)を選択します。

Screenshot from 2021-02-20 16-27-15.png

作成するプロジェクトの設定画面2が出ましたが、
プロジェクト名、プロジェクトを格納する場所は適当にセットします。
よくわからないので、とりあえずMaven形式を選択しました。
完了ボタンを押下して、初期設定が完了です。

Screenshot from 2021-02-20 16-28-16.png

初期設定が完了すると、下記のような画面が表示されます。
実装には不要となるので、この画面はクローズします。
Screenshot from 2021-02-20 16-28-33.png

Eclipse左側のパッケージエクスプローラを見ると、
自動的にフォルダの階層が作成されています。
Screenshot from 2021-02-20 16-28-59.png

実装

処理全体の流れを説明

以下のルールが適用される処理を実装します。
・ある人の年齢が30歳以上であれば、"30歳以上です"とメッセージを出力する。
・ある人の年齢が30歳未満であれば、"30歳未満です"とメッセージを出力する。

実装する上で、主役は3つの資産となります。

●ルール適用するデータ:Personクラス
ルールには"ある人"とあるように、年齢の情報を持つPersonクラスが必要となります。
これがルール適用させるインプットデータとなるクラスとなります。

●ルールファイル
ルールは"drl"という拡張子のファイルに定義されますが、簡単に説明してしまえば
手続き型言語で登場する if** then** の塊を定義ファイルに集約したもの、というイメージです。

●ルール適用処理を行うmainクラス
ルール適用するデータ、ルールファイルが作成したら、ルールを実際に適用する処理を実装します。

ルールに適用させるインプットデータとなるクラスを作成

この後にルールを作成しますが、そのルール(条件判定して、マッチすれば何かするといった定義ファイル)に適用させるクラスを作成します。
ルール適用に必要なメンバ変数として年齢を定義します。名前のメンバ変数はおまけで定義しました。

Screenshot from 2021-02-20 18-10-24.png

ルールの新規作成

パッケージエクスプローラの "src/main/resources/rules" を選択し右クリックで
新規リソースを作成します。"Rule Resource"を選択して次へ進みます。

Screenshot from 2021-02-20 17-24-34.png

ルールファイルの新規作成ワイザード画面が開きました。
ルールのファイル名とそのパッケージ名を入力します。

rule作成.jpg

雛形のルールが新規されました。あとで、このルールファイルに追記していきます。
Screenshot from 2021-02-20 17-34-47.png

先に完成したものを提示致します。

rule作成2.jpg

ルール名を定義

ユニークなルール名を rule の後ろに文字列として記述します。

whenを定義

whenはif--then のifに対応する部分です。
今回作成するサンプルでは、Personクラスの年齢(age)が30歳以上の場合を条件として定義します。

  when
    $p:Person(age <= 30)

上記"Person"というのは、クラス名のPersonです。
プロジェクト内で存在するクラス名を記述する必要があります。(実行時エラーとなりますので、importするのをお忘れなく。)

上記”age"というのは、Personクラスのメンバ変数です。
ルール適用するクラスに存在するメンバ変数をwhenの条件式で使うようにします。

上記"$p"は、バインド変数と呼ばれるものです。
whenでマッチしたPersonオブジェクトをthenの部分で参照することができます。

 then
   System.out.println( "Under 30 ages :" + $p.getName() );

when部分でマッチした場合に実行する処理を記述します。
処理はJavaで記述しますので、サンプルのためsysoutをしています。

(新規作成における時留意点)不要な設定を削除

Screenshot from 2021-02-20 18-18-38.png

ルールファイルを新規作成した際にテンプレとして3つのルールがありましたが、
そのルールが設定ファイル(kmodule.xml)に反映されたままになっています(kbaseという要素)。そのテンプルは不要なため削除済みですが、その設定も不要となりますので削除します。
削除しなれば上記のような実行時エラーになってしまうために、設定ファイルkmodule.xmlから削除する必要があります。

mainの新規作成

ルール適用するデータ、ルールファイルが作成しましたので、ルールを実際に適用する処理を実装していきます。

Screenshot from 2021-02-20 18-45-45.png

サンプル例では、
 ・ジョン:30歳
 ・ポール:29歳
というPersonクラスオブジェクトを生成して、年齢判定するルールを適用させています。
適用は"insert"メソッドに引数として渡します。
実際にルール適用の実行はfireAllRulesメソッドです。

気になって点として、ジョン→ポールという順番でinsertしていますが
コンソール画面を見るとポール→ジョンの順番でルール適用した場合の処理(then処理)が実行されいます。
insertした順番と実際にルール適用処理の順番は一致しない、ように見えます。

以上が、プロジェクト新規作成から簡単なサンプルプログラムを動作させるまでの作業手順となります。

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

layout:decorateが反映されない【書籍 はじめてのSpring Boot】

環境

Thymeleaf 3.0
Spring Boot 2.4.2
Mac OS Big Sur
Maven

状況

以下のコードのようにlayout:flagmentとlayout:decorateを用いてテンプレートを取り込もうとしていた。

ディレクトリ構造

|-template
    |
    |-- customers
    |      |
    |      |-- edit.html
    |      |-- list.html
    |
    |-- commons
           |
           |-- layout.html

layout.html(テンプレート部分)

<!DOCTYPE html>
<html  xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="http://www.thymeleaf.org"
       xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
  <title>layout</title>
</head>
<body>
  <div class="container">
    <h1 class="text-center text-primary mt-5">顧客管理システム</h1>
    <div>
    </div>
    <div layout:fragment="content">
      Fake Content
    </div>
  </div>
</body>
</html>

list.html (フラグメント部分)

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org/" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
  layout:decorate ="~{/commons/layout}">

<head>
  <title>顧客一覧</title>
</head>

<body>
  <div layout:fragment="content" class="col-sm-12">
    <form th:action="@{/customers/create}" th:object="${customerForm}" method="POST">
      <dl>
        <dt><label for="lastName"></label></dt>
        <dd>
          <input type="text" id="lastName" name="lastName" th:field="*{lastName}" th:errorclass="error-input"
            value="山田" />
          <span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" class="error-messages">error!</span>
        </dd>
      </dl>
      <dl>
        <dt><label for="firstName"></label></dt>
        <dd>
          <input type="text" id="firstName" name="firstName" th:field="*{firstName}" th:errorclass="error-input"
            value="太郎" />
          <span th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}" class="error-messages">error!</span>
        </dd>
      </dl>
      <input type="submit" value="作成">
    </form>
    <table>
      <tr th:each="customer : ${customers}">
        <td th:text="${customer.id}">100</td>
        <td th:text="${customer.lastName}">山田</td>
        <td th:text="${customer.firstName}">太郎</td>
        <td>
          <form th:action="@{/customers/edit}" method="GET">
            <input type="submit" name="form" value="編集">
            <input type="hidden" name="id" th:value="${customer.id}" />
          </form>
        </td>
        <td>
          <form th:action="@{/customers/delete}" method="POST">
            <input type="submit" value="削除">
            <input type="hidden" name="id" th:value="${customer.id}" />
          </form>
        </td>
      </tr>
    </table>
  </div>
</body>

</html>

list.htmlの冒頭にlayout:decorate ="~{/commons/layout}"を記述することでフラグメントとして導入されるテンプレートファイルを指定することができる。

しかし、このlayout:deocorateを書籍通り記述するもフラグメントとして組み込まれない。

pom.xmlに追記

layout:decorateが機能しない理由はdecorateが依存関係に追加されていないから。
どうやらみなさんがよく使うであろう「spring-boot-starter-thymeleaf」にはdocorate機能がない。
だからdocorateを依存関係に追加するためpom.xmlに以下のコードを追記する。

<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>

これで正しく反映されます!

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

sp_executesqlを使用してエスケープ処理を行った

テスト中に画面でエラーが発生してしまった。

調査の結果以下のような原因だった。

  • 検索条件に'(シングルクオーテーション)が含まれていてストアドプロシージャでエラーになっているようだ。

使用しているストアドでは動的sql※1(呼び方がわからないので以下このように呼びます。)を使用していた。

解決方法

以下の解決法が選択肢として上がりました。

  • ストアドプロシージャに渡すパラメータはすべてエスケープ処理を行う(SQLServerの場合はシングルクオーテーションをもう一つつける)
  • sp_executesqlの使用を検討する

検討の結果後者のsp_executesqlを使用することにしました。

実際にどのようなエラーだったか、
テストソースで解説します。

呼び出し元Java
 public static void callProcedure() throws SQLException {

        CallableStatement  cs = null;
        try (Connection con= DriverManager.getConnection(sqlcall.calltest())){
            cs = con.prepareCall("{? = call dbo.sp_TESTA(?)}");
            cs.registerOutParameter(1, java.sql.Types.INTEGER);
            cs.setString(2, "ff'ffff");
            cs.execute();
            ResultSet rs = cs.getResultSet();

            while(rs.next()) {
                System.out.println(rs.getString(1));
            }
            System.out.println("calltest"+ cs.getInt(1));

        }catch(SQLException e){
            e.printStackTrace();

        }
    }

testTableのデータ

Test1
ff'ffff
動的SQL※1

CREATE PROCEDURE [dbo].[sp_TESTA] 
    @TESTA nvarchar(40)
AS
BEGIN try
    -- SET NOCOUNT ON added to prevent extra result sets from
    SET NOCOUNT ON;

    DECLARE @query nvarchar(MAX) = '';
    DECLARE @parameters nvarchar(max) = '';



    SET @query = N' SELECT Test1 '
    SET @query = @query + N' FROM dbo.testTable '
    SET @query = @query + N' where Test1 = '''+ @TESTA+''''

    execute ( @query);

    return 0;

END try
begin catch

    SELECT   
        ERROR_NUMBER() AS ErrorNumber  
       ,ERROR_MESSAGE() AS ErrorMessage; 
    return  -1

end catch
GO


'ffff' 付近に不適切な構文があります。
calltest-1

文字内にある'(シングルクオーテーション)が文字の区切りとして認識され、後ろ4文字が文字として認識されませんでした。

これを以下のようにsp_executesqlを使用するように修正しました。

sp_executesql
CREATE PROCEDURE [dbo].[sp_TESTA] 
    -- Add the parameters for the stored procedure here
    @TESTA nvarchar(40)
AS
BEGIN try
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    DECLARE @query nvarchar(MAX) = '';
    DECLARE @parameters nvarchar(max) = '';


    SET @query = N' SELECT Test1 '
    SET @query = @query + N' FROM dbo.testTable '
    SET @query = @query + N' where Test1 = @Test1 '

    set @parameters = N'@Test1 nvarchar(40) '


    execute sp_executesql @query,@parameters, @Test1 = @TESTA

    return 0;

END try
begin catch

    SELECT   
        ERROR_MESSAGE() AS ErrorMessage; 
    return  -1

end catch
GO



ff'ffff
calltest0

無事文字として認識され正常に検索条件として反映されました。

まとめ

画面で渡された値を使用する場合にはsp_executesqlをなるべく使うこと。インジェクション対策にもつながるので、sp_executesqlで仕様を実装できるかを確認したほうが良い

参考

Microsoft SQLドキュメント sp_executesql (Transact-sql)

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