2018年06月16日

Java コレクションクラスについての備忘録

自前のクラスをArrayListなどに入れてまとめて扱うことは多々あると思いますが、
同値判定にかかわる部分ついて押さえておかないと、
期待したとおりの動作にならずつまづくことがあるのでメモしておきます。

サンプルのソースでは呼び出し側のMyClassで、自作したMyComparableClassのインスタンスを各コレクションクラスに挿入し、
別のインスタンスを使って「既に存在するか」のチェックをさせます。

結果は一番下に示した様になります。

判定に使われるメソッドについてまとめると以下のようになります。

(1)ArrayListとLinkedList
equalsが呼ばれる。

(2)HashMapとHashSet
hashCodeが呼ばれ、同値だった場合にequalsが呼ばれる。

(3)TreeMapとTreeSet
compareToが呼ばれる。

equalsとcompareToの関係についてはこちらで詳しく解説されています。
「equalsとcompareToが一貫性してることは強く推奨されるけど、BigDecimalは例外」だそうな。

■自前のクラス MyComparableClass
/**
* Comparableを実装したクラス
*/
public class MyComparableClass<T extends Comparable<T>> implements Comparable<MyComparableClass<T>> {

/** フィールド1 */
private T field1;

/**
* フィールド1を返す
* @return field1
*/
public T getField1() {
return field1;
}

/**
* コンストラクタ
* @param field1 フィールド1
*/
public MyComparableClass(T field1) {
this.field1 = field1;
}

/**
* このオブジェクトと指定されたオブジェクトの順序を比較します。
* @param other
* @return
*/
@Override
public int compareTo(MyComparableClass<T> other) {
System.out.println("compareToが呼ばれました:" + field1.toString());
return field1.compareTo(other.getField1());
}

/**
* オブジェクトのハッシュ・コード値を返します。
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
System.out.println("hashCodeが呼ばれました:" + field1.toString());
final int prime = 31;
int result = 1;
result = prime * result + ((field1 == null) ? 0 : field1.hashCode());
return result;
}

/**
* このオブジェクトと他のオブジェクトが等しいかどうかを示します。
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
System.out.println("equalsが呼ばれました:" + field1.toString());
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
MyComparableClass<T> other = (MyComparableClass<T>) obj;
if (field1 == null) {
if (other.field1 != null)
return false;
} else if (!field1.equals(other.field1))
return false;
return true;
}
}
■呼び出し側(MyClass)
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class MyClass {

public static void main(String... args) {

MyComparableClass<BigDecimal> val1 = new MyComparableClass<>(new BigDecimal("100"));
MyComparableClass<BigDecimal> val2 = new MyComparableClass<>(new BigDecimal("100.00"));

System.out.println("-----ArrayList---------------");
List<MyComparableClass<BigDecimal>> myArrayList = new ArrayList<>();
myArrayList.add(val1);

if (myArrayList.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}

System.out.println("-----LinkedList---------------");
List<MyComparableClass<BigDecimal>> myLinkedList = new LinkedList<>();
myLinkedList.add(val1);

if (myLinkedList.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}

System.out.println("-----HashMap---------------");
Map<MyComparableClass<BigDecimal>, Integer> myHashMap = new HashMap<>();
myHashMap.put(val1, 0);

if (myHashMap.containsKey(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}

System.out.println("-----TreeMap---------------");
Map<MyComparableClass<BigDecimal>, Integer> myTreeMap = new TreeMap<>();
myTreeMap.put(val1, 0);

if (myTreeMap.containsKey(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}

System.out.println("-----HashSet---------------");
Set<MyComparableClass<BigDecimal>> myHashSet = new HashSet<>();
myHashSet.add(val1);

if (myHashSet.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}

System.out.println("-----TreeSet---------------");
Set<MyComparableClass<BigDecimal>> myTreeSet = new TreeSet<>();
myTreeSet.add(val1);

if (myTreeSet.contains(val2)) {
System.out.println(">>ok");
} else {
System.out.println(">>ng");
}
}
}
■実行結果
-----ArrayList---------------
equalsが呼ばれました:100.00
>>ng
-----LinkedList---------------
equalsが呼ばれました:100.00
>>ng
-----HashMap---------------
hashCodeが呼ばれました:100
hashCodeが呼ばれました:100.00
>>ng
-----TreeMap---------------
compareToが呼ばれました:100
compareToが呼ばれました:100.00
>>ok
-----HashSet---------------
hashCodeが呼ばれました:100
hashCodeが呼ばれました:100.00
>>ng
-----TreeSet---------------
compareToが呼ばれました:100
compareToが呼ばれました:100.00
>>ok
タグ:JAVA
posted by Hiro at 19:50| Comment(0) | プログラム

Excel ADOを使ったデータベースへの接続とデータ取得

ExcelでADOを使った接続とデータ取得の方法です。

メモ:xlsファイルをODBCへ登録する場合は、データの範囲を「名前の定義」で登録しておく。
'変数宣言の強制
Option Explicit

'ODBC接続文字列(※ODBCは事前に登録しておくこと)
Const strConnectDB As String = "DSN=MYDB;UID=***;PWD=***;"

'データ貼り付け先頭セル
Const topCell As String = "A4"

'==================================================
'ADOを使ったデータベースへの接続とデータ取得
'[参照設定]ダイアログで「Microsoft ActiveX Data Objects x.x Library」をOnにする。
'
'引数
' (1)sourceTable 取得元テーブル名
' (2)targetSheet (参照渡し)データ挿入先シート
'==================================================
'呼び出し元記述例
'Private Sub CommandButton1_Click()
' Dim sheet As Worksheet
' Set sheet = ThisWorkbook.Sheets("AAAA出力先")
' sheet.Cells.NumberFormatLocal = "@"
' 'データの取得とシートへの貼り付け
' Call getTable("AAAA_TABLE", sheet)
'End Sub
'==================================================
Public Sub getTable(ByVal sourceTable As String, ByRef targetSheet As Worksheet)

On Error GoTo ErrorRoutine

'ADOオブジェクトを作成(参照設定:Microsoft ActiveX Data Object2.x Library)
Dim ADO As New ADODB.Connection
Dim RS As New ADODB.Recordset

'データベースへ接続
Call ADO.Open(strConnectDB, , , adConnectUnspecified)
'テーブルのレコードを取得
Call RS.Open(sourceTable, ADO, adOpenForwardOnly, adLockReadOnly, adCmdTable)

'レコード取得
If Not RS.EOF Then

'(1)一括で張り付ける方法
'targetSheet.Range(topCell).CopyFromRecordset RS

'(2) (1)でエラーとなる場合は一件ずつ挿入する方法を採用
Do Until RS.EOF
Dim row As Integer
Dim clm As Integer
For clm = 0 To RS.Fields.count - 1
Dim curCell As Range
Set curCell = targetSheet.Range(topCell).Offset(row, clm)
'日付型は書式を指定
If RS.Fields(clm).Type = adDBTimeStamp Then
curCell.Value = Format(RS.Fields(clm).Value, "yyyy/mm/dd hh:nn:ss")
ElseIf RS.Fields(clm).Type = adDate Or RS.Fields(clm).Type = adDBDate Then
curCell.Value = Format(RS.Fields(clm).Value, "yyyy/mm/dd")
ElseIf RS.Fields(clm).Type = adDBTime Then
curCell.Value = Format(RS.Fields(clm).Value, "hh:nn:ss")
Else
curCell.Value = RS.Fields(clm).Value
End If
Next clm
RS.MoveNext
row = row + 1
Loop
End If

'データベースから切断
ADO.Close
Set RS = Nothing
Set ADO = Nothing

'呼び出しも度へ戻る
Exit Sub

'例外処理
ErrorRoutine:
Dim msg As String
msg = "ADO接続でエラーが発生しました" & vbNewLine & vbNewLine
msg = msg & "エラーコード : " & Err.Number & vbNewLine
msg = msg & "エラーメッセージ : " & Err.Description

Call MsgBox(msg, vbOKOnly + vbExclamation, "エラー")
End
End Sub
続きを読む
タグ:Excel VBA
posted by Hiro at 18:48| Comment(0) | プログラム