技術メモ

神奈川在住のITエンジニアの備忘録。おもにプログラミングやネットワーク技術について、学んだことを自分の中で整理するためにゆるゆると書いています。ちゃんと検証できていない部分もあるのでご参考程度となりますが、誰かのお役に立てれば幸いです。

yaml ファイル内のハイフン

先日、yaml ファイルを扱う機会があった時、

person:
 - id: 1
   name: xxx
   address: yyy

nextItem: zzz

と、

person:
 - id: 1
 - name: xxx
 - address: yyy

nextItem: zzz

の違いについて理解するのに少し時間がかかったので、ここに纏めておく。

Best Online YAML Converter - Convert YAML Strings into JSON, XML, CSV
のサイトで、 上記の yaml ファイル を json 形式に変換してみると、違いが分かりやすい。

一つ目は、

{
	"person": [
		{
			"id": 1,
			"name": "xxx",
			"address": "yyy"
		}
	],
	"nextItem": "zzz"
}

となり、2つ目は、

{
	"person": [
		{
			"id": 1
		},
		{
			"name": "xxx"
		},
		{
			"address": "yyy"
		}
	],
	"nextItem": "zzz"
}

となる。

つまり、yaml ファイルの中にハイフンがあると、それ以降は、次のハイフンかインデントが戻る (少なくなる) まで、一纏まりの key-valueストア=配列の1要素として扱われることになる。

Java の Map をソートする。

プログラミングの問題を解いていて必要になったので、Javaの Map をソートする方法を以下に纏める。調べてみたところ、いろんな方法があるようだが、自分で試してみてうまくいった方法をここにメモしておく。

例えば、以下の map をソートするとする。

        Map<Integer, String> map = new HashMap<>();
        map.put(1, "test1");
        map.put(4, "test4");
        map.put(2, "test2");
        map.put(3, "test3");

上記の map に対して、以下のコードにより、map の key や value でソートできる。

        // key で昇順にソート
        List<Map.Entry<Integer, String>> entries1 =
                map.entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .collect(Collectors.toList());

        // key で降順にソート
        List<Map.Entry<Integer, String>> entries2 =
                map.entrySet().stream()
                        .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                        .collect(Collectors.toList());

        // value で昇順にソート
        List<Map.Entry<Integer, String>> entries3 =
                map.entrySet().stream()
                        .sorted(Map.Entry.comparingByValue())
                        .collect(Collectors.toList());

        // value で降順にソート
        List<Map.Entry<Integer, String>> entries4 =
                map.entrySet().stream()
                        .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                        .collect(Collectors.toList());

上記のコードでは、map からエントリーを取得してソートし、Map.Entry の List に格納している。List に格納している理由は、順序性の保証されない map ではなく、順序性の保証される List で扱うため。
ちゃんとソートできているかどうかは、以下のコードで確認できる。

    for (Map.Entry<Integer, String> entry : entries1) {
        System.out.println("Key: "+ entry.getKey() + ", Value: " + entry.getValue());
    }

Map.Entry の詳細は以下。
Map.Entry (Java Platform SE 8)


ちなみに、TreeMap を使うと、自動的に key でソートされる。key でソートする分には、こっちの方が簡単に書ける。

        // key で昇順にソート
        Map<Integer, String> treeMap = new TreeMap<>();

        // key で降順にソート
        Map<Integer, String> treeMapReverse = new TreeMap<>(Comparator.reverseOrder());

SNMPv3のエンジンIDは何に使うのか。

このブログでも何度かSNMPv3のエンジンIDについて書いたが、そもそもエンジンIDが何に使われるのか、ちゃんと調べたことはなかった。

そこで、今回、RFC など調べてみたところ、エンジンID は SNMPv3 通信において、認証や暗号化(復号化)を行う時の鍵として使われるようだ。

 

https://tools.ietf.org/html/rfc3414#section-2.6

2.6. Key Localization Algorithm.

A localized key is a secret key shared between a user U and one
authoritative SNMP engine E. Even though a user may have only one
password and therefore one key for the whole network, the actual
secrets shared between the user and each authoritative SNMP engine
will be different. This is achieved by key localization [Localized-
key].

First, if a user uses a password, then the user's password is
converted into a key Ku using one of the two algorithms described in
Appendices A.2.1 and A.2.2.

★To convert key Ku into a localized key Kul of user U at the
authoritative SNMP engine E, one appends the snmpEngineID of the
authoritative SNMP engine to the key Ku and then appends the key Ku
to the result, thus enveloping the snmpEngineID within the two copies
of user's key Ku. Then one runs a secure hash function (which one
depends on the authentication protocol defined for this user U at
authoritative SNMP engine E; this document defines two authentication
protocols with their associated algorithms based on MD5 and SHA).
The output of the hash-function is the localized key Kul for user U
at the authoritative SNMP engine E.

 

ちなみに、上記に記載のある通り、rfc3414 の Appendices A.2.1 and A.2.2 には、エンジンIDをもとに鍵を生成する際のアルゴリズムの例が記載されている。

 

〇参考

https://tools.ietf.org/html/rfc3414

https://docs.vmware.com/jp/VMware-vSphere/6.0/com.vmware.vsphere.monitoring.doc/GUID-4AF8AA5F-D652-4080-B984-B36A25456A4B.html

https://www.dpstele.com/snmp/snmpv3-trap-format.php

Javaで 2つの配列の共通部分を取得する。

競技プログラミングの問題を解いていて必要になったので、Javaで 2つの配列の共通部分を取得するメソッドを作成した。1つ目のメソッドは int 用で、2つ目は String 用。

    // 二つの数値の配列のうち、共通する項目を取り出す。
    int[] getCommonNumbers (int[] x, int[] y) {
        // どちらかが null の場合は、空の配列を返す。(呼び出し元で NullPointerException を起こさないため。)
        if (x == null || y == null) {
            return new int[0];
        }
        List<Integer> commons = new ArrayList();
        int count = 0;
        for (int elemX : x) {
            for (int elemY : y) {
                if (elemX == elemY) {
                    commons.add(elemX);
                    count++;
                }
            }
        }
        int[] result = new int[count];
        for (int i = 0; i < count; i++) {
            result[i] = commons.get(i);
        }
        return result;
    }

    // 二つの文字列の配列のうち、共通する項目を取り出す。
    String[] getCommonStrings (String[] x, String[] y) {
        // どちらかが null の場合は、空の配列を返す。(呼び出し元で NullPointerException を起こさないため。)
        if (x == null || y == null) {
            return new String[0];
        }
        List<String> commons = new ArrayList();
        int count = 0;
        for (String elemX : x) {
            for (String elemY : y) {
                if (elemX.equals(elemY)) {
                    commons.add(elemX);
                    count++;
                }
            }
        }
        String[] result = new String[count];
        for (int i = 0; i < count; i++) {
            result[i] = commons.get(i);
        }
        return result;
    }

Javaでは可変長の配列がないので、あまりカッコよくないが、、共通部分の取得結果を一旦 List に格納して、最後に List を配列に変換している。

int型の配列に対する Arrays.asList().contains() は、意図通り動作しない。

Java で、int型の配列に特定の値が入っているかどうか確認するために、以下のようなコードを書いたら、意図通りに動かなかった。

int[] intArray = new int[]{0, 1, 2};
if (Arrays.asList(intArray).contains(1)) {
    System.out.print("Found from intArray"); // ここは出力されない。
}

デバッグしてみたところ、int型の配列に対して Arrays.asList() を適用すると、戻り値の List の最初の項目に int型の配列(全体)への参照が入ってしまうようだ。そして、List の2番目以降は存在しない。つまり、List の各項目として配列の各値が入るわけではない。(期待通りではない。)


以下のようにすると、意図通り動いた。

Integer[] intObjArray = new Integer[]{new Integer(0), new Integer(1), new Integer(2)};
if (Arrays.asList(intObjArray).contains(1)) {
    System.out.print("Found from intObjArray"); // ここは出力される。
}

Arrays.asList() に渡す配列は、オブジェクト型でないと、意図通りにならないのだと思う。

指定した桁数の全ての2進数文字列を生成する。

競技プログラミングの問題を解いていて必要になったので、Javaで、指定した桁数の全ての2進数文字列のリストを生成するメソッドを作成した。

    List<String> generateZeroOneCombination (int length) {
        if (length < 1) {
            throw new IllegalArgumentException("Input length is less than 1.");
        }
        int max = (int) Math.pow(2, length);
        List<String> zeroOneCombination = new ArrayList<>();
        // String.format() に 2進数に変換するオプション(書式)がないので、以下のようにちょっと回りくどい方法を用いている。
        for (int i = 0; i < max; i++) {
            String binStr = Integer.toBinaryString(i);
            String formatter = "%" + length + "s";
            // String.format() で桁数を揃えてから、String.replace() で先頭部分の空白を 0 に置き換える。
            String binStrFormatted = String.format(formatter, binStr).replace(" ", "0");
            zeroOneCombination.add(binStrFormatted);
        }
        return zeroOneCombination;
    }


generateZeroOneCombinationに、例えば、引数に 3 を与えると、戻り値として以下の2進数文字列を含んだ List を返す。
"000", "001", "010", "011", "100", "101", "110", "111"

SNMPのnoSuchNameとnoSuchObject/noSuchInstanceとの違い

主題の件について、簡単に纏める。

どちらも、SNMPエージェント内に対象のMIBが存在しないことを示すものだが、noSuchName はSNMPのエラー・ステータスであり、noSuchObject/noSuchInstance は varbind に入ってくるもの(MIB値)なので、そもそも種類が違うものである。

noSuchName はSNMPv1からある。noSuchObject/noSuchInstance はSNMPv2c から導入された。noSuchObject/noSuchInstance が導入されたことにより、SNMPエージェントは、存在しないMIB値について個別にSNMPマネージャに通知できるようになった。(存在しないMIBのせいで、他の正常なMIBについても応答できないという事態を防げるようになった。)

ちなみに、noSuchObject と noSuchInstance との違いは、前者がスカラー型のMIBに対して使用されるのに対して、後者はテーブル型のMIBに対して使用される、という点。

 

〇参考

https://www.ietf.org/rfc/rfc2576.txt

https://www.alaxala.com/jp/techinfo/archive/manual/AX3630S/HTML/11_10/CFGUIDE2/0413.HTM