配列とコレクション

アウトライン(Java)

List

ArrayList 動的配列 途中へ要素挿入・削除はNG

LinkedList 両方リスト INDEX指定getはNG

Vector スレッドセーフの動的配列

Stack FILO

Set

EnumSet Enum専用

HashSet HashMapと似る

TreeSet 自動ソート

LinkedHashSet FIFO

Map

ソートMap

TreeMap getが速い、put/removeが遅い、subMap(fromKey, toKey)が利用できる

Comparator或いはComparableの実装が必要

非ソートMap

HashMap

HashTable スレッドセーフ

Properties Propertiesファイル用

EnumMap Enum専用

LinkedHashMap FIFO

Queue

Blockキュー(満ちたキューにデータを追加すると、例外発生)

ArrayBlockingQueue 配列に基づく

PriorityBlockingQueue 優先順位を持つ

LinkedBlockingQueue リストに基づく

UnBlockキュー

PriorityQueue

両端キュー(両端からデータを挿入・削除)

ArrayDeque

LinkedBlockingDeque

LinkedList

配列

基本型の配列はOK、基本型のコレクションはNG

すべてのコレクションの本質は配列

Utilsクラス

配列

java.util.Arrays

java.lang.reflect.Array

コレクション

java.util.Collections

拡張Utilsクラス

Apacheのcommons-collections

Googleのgoogle-collections

★配列に対する操作

String[] array = new String[5];

String[] array = {"a","b","c", "d", "e"};

String[] array = new String[]{"a","b","c","d","e"};

int[] intArray = { 1, 2, 3, 4, 5 };

String strArray = Arrays.toString(intArray);// [1, 2, 3, 4, 5]

String[] strArray = { "a", "b", "c", "d", "e" };

boolean b = Arrays.asList(strArray).contains("a");

配列⇔リスト

String[] array = { "a", "b", "c", "d", "e" };

ArrayList<String> list = new ArrayList<String>(Arrays.asList(array));

String[] array = new String[list.size()];

list.toArray(array);

Array⇒Set

Set<String> set = new HashSet<String>(Arrays.asList(array));

int⇒byte

byte[] bytes = ByteBuffer.allocate(4).putInt(8).array();

for (byte t : bytes) {

System.out.format("0x%x ", t);

}

// 配列のコピーを作成

int[] intArray = ...;

int[] copyOfIntArray = Arrays.copyOf(intArray, intArray.length);

int[] copyOfRangeIntArray = Arrays.copyOfRange(copyOfIntArray, 3, 5);

Arrays.equals(intArray, copyOfIntArray)

Arrays.equals(copyOfRangeIntArray, new int[] { 4, 5 })

★配列の容量拡張

String[] strs = new String[20];

strs = Arrays.copyOf(strs, 30);

※copyOfメソッドはShallow Clone

自作(汎用)

import java.lang.reflect.Array;

public static Object resizeArray (Object oldArray, int newSize) {

int oldSize = Array.getLength(oldArray);

Class<?> type = oldArray.getClass().getComponentType();

Object newArray = Array.newInstance(type, newSize);

int len = Math.min(oldSize, newSize);

if (len > 0){

System.arraycopy(oldArray, 0, newArray, 0, len);

}

return newArray;

}

★配列の比較

int[] arr1 = new int[10];

int[] arr2 = new int[10];

arr1.equals(arr2); // 参照の比較

Arrays.equals(arr1, arr2); // 中身の比較

★初期容量

・ArrayList 初期容量:10、拡張公式:newCapacity = (oldCapacity * 3) / 2 + 1

・Vector 初期容量:10、拡張公式:

newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2)

・HashMap 初期容量:16、拡張公式:newCapacity = oldCapacity * 2

HashMap(int initialCapacity, float loadFactor)デフォルトloadFactor :0.75

Hashtable, Vectorも同様

※容量が50を予想できるなら、new ArrayList<Person>(50 * 1.5)を与えるのが最適

※容量が50を予想できるなら、new HashMap<String, String>(50 * 4/3)を与えるのが最適

ちなみに、

・StringBuffer 初期容量:16

★コレクション中のアルゴリズム

〇最大値を探す

方法1(配列:最速)

public static int max(int[] data){

int max = data[0];

for(int i : data){

max = max > i ? max : i;

}

return max;

}

方法2(コレクション:簡単)

public static int max(int[] data){

Arrays.sort(data.clone());

return data[data.length - 1];

}

※効率からみると、データ量が1万以内の場合に方法1と方法2は同じ

〇最大値に続く2番目を探す

public static int getSecond(Integer[] data){

// 配列⇒リスト

List<Integer> list = Arrays.asList(data);

// リスト⇒セット 重複を削除し、昇順でソート

TreeSet<Integer> ts = new TreeSet<Integer>(list);

// 最大値より小さい最大値

return ts.lower(ts.last());

}

★List比較について

ArrayList<String> strs1 = new ArrayList<String>();

strs1.add("a");

Vector<String> strs2 = new Vector<String>();

strs2.add("a");

LinkedList<String> strs3 = new LinkedList<String>();

strs3.add("a");

System.out.println(strs1.equals(strs2)); true

System.out.println(strs1.equals(strs3)); true

理由:ArrayList, Vector, LinkedListすべてはAbstractList(equalsメソッドを持つ)から継承するため

判断基準:Listから継承し、具体的な実現に関わらず、リストの中身が同じであれば。

Set,Mapも一緒。

★subListとsubstring

List<String> strs1 = new ArrayList<String>();

strs1.add("a");

strs1.add("b");

List<String> strs2 = new ArrayList<String>(strs1); // 実はpyOfを呼び出す

List<String> strs3 = strs1.subList(0, strs1.size()); // 元リストのViewと相当

strs3.add("c");

System.out.println(strs1.equals(strs2)); false

System.out.println(strs1.equals(strs3)); true

String str1 = "ab";

String str2 = new String(str1);

String str3 = str1.substring(0) + "c";

System.out.println(str1.equals(str2)); true

System.out.println(str1.equals(str3)); false

subListの用法(100個データ中に20~30個目のデータを削除)

方法1

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

if(i >= 20 && i < 30){

list.remove(i);

}

}

方法2

for(int i = 20; i < 30; i++){

if(i < list.size()){

list.remove(i);

}

}

方法3

list.subList(20, 30).clear();

subListの注意点

List<String> list = new ArrayList<String>();

list.add("a");

list.add("b");

List<String> subList = list.subList(0, 1);

//list.add("c"); ←元リストを操作するのは危険

list = Collections.unmodifiableList(list); // 読み取り専用に設定

※subListが複数使う場合、すべてのsubListは変更できない

★indexOfとbinarySearch

List<String> list = new ArrayList<String>(3);

...

int index1 = list.indexOf("b");

int index2 = Collections.binarySearch(list, "b");

indexOf:最初から検索、実はequalsメソッドを呼び出す

binarySearch:真ん中から検索(ソート済)、大量データに向かう、実はcompareToメソッドを呼び出す

★集合間の計算

和集合and

list1.addAll(list2);

積集合or

list1.retainAll(list2);

差集合not

list1.removeAll(list2);

和集合(重複除外)

list2.removeAll(list1);

list1.addAll(list2);

★Listに対する操作

List⇒int[]

int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));

あるいは

int[] array = new int[list.size()];

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

array[i] = list.get(i);

}

List⇒Integer[]

List.toArray()

int[]⇒List

List list = Arrays.asList(ArrayUtils.toObject(array));

あるいは

int[] array = {1,2,3,4,5};

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

for(int i: array) {

list.add(i);

}

方法1

Iterator<Integer> itr = list.iterator();

while(itr.hasNext()) {

int i = itr.next();

if (i > 5) {

itr.remove(); //OK

}

}

for(Integer i: list) {

...

list.remove(i); //NG: ConcurrentModificationException

}

方法2

public interface Predicate<T> {

boolean test(T o);

}

public static <T> void filter(Collection<T> collection, Predicate<T> predicate) {

if ((collection == null) || (predicate == null)) {

return;

}

Iterator<T> itr = collection.iterator();

while(itr.hasNext()) {

T obj = itr.next();

if (!predicate.test(obj)) {

itr.remove();

}

}

}

filter(list, new Predicate<Integer>() {

public boolean test(Integer i) {

return i <= 5;

}

});

List⇒Set

Set<Integer> set = new HashSet<Integer>(list);

Set<Integer> set = new TreeSet<Integer>(aComparator);

set.addAll(list);

//Remove repeated elements from ArrayList

Set<Integer> set = new HashSet<Integer>(list);

list.clear();

list.addAll(set);

//shallow copy

ArrayList<Integer> dstList = new ArrayList<Integer>(srcList);

あるいは

ArrayList<Integer> dstList = new ArrayList<Integer>(srcList.size());

Collections.copy(dstList, srcList);

繰り返し制御構文について

// IteratorA

Iterator<Data> it = list.iterator();

int size = list.size();

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

Data data = it.next();

System.out.printf("%d:%s\n", i, data.toString());

}

// IteratorB

int i = 0;

for (Iterator<Data> it = list.iterator(); it.hasNext(); i++) {

Data data = it.next();

System.out.printf("%d:%s\n", i, data.toString());

}

// foreach

int i = 0;

for (Data data : list) {

System.out.printf("%d:%s\n", i, data.toString());

i++;

}

// for

int size = list.size();

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

Data data = list.get(i);

System.out.printf("%d:%s\n", i, data.toString());

}

ですから、forとforeachの使い分け

public int method(List<Integer> list){

int sum = 0;

if(list instanceof RandomAccess){

// ★ArrayListの場合

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

sum += list.get(i);

}

}else{

// ★LinkedListの場合

for (int i : list) {

sum += i;

}

或いは

for (Iterator<Integer> it = list.iterator(); it.hasNext();) {

sum += it.next();

}

或いは

Iterator<Integer> it = list.iterable();

while (it.hasNext()) {

sum += it.next();

}

}

return sum;

}

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

for(Integer i : list) {...}

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

for(int j = 0; j < list.size() ; j++) {...}

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

int size = list.size();

for(int j = 0; j < size ; j++) {...}

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

for(int j = list.size(); j > size ; j--) {...}

foreach loop : 110

collection.size() : 37

for loop asc : 4

for loop desc : 1

// 最大・最小を取得

List<Integer> list = Arrays.asList(45, 76, 39);

Collections.min(list)

Collections.max(list)

User min = Collections.min(userList, new Comparator<User>() {

@Override

public int compare(User o1, User o2) {

return Integer.compare(o1.getId(), o2.getId());

あるいは

return o1.getId() - o2.getId();

}

});

User max = Collections.max(userList, new Comparator<User>() {

@Override

public int compare(User o1, User o2) {

return o1.getName().compareTo(o2.getName());

}

});

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));

list.addAll(Arrays.asList("d", "e", "f"));

Collections.addAll(list, "d", "e", "f");

★Listのページング

private static final int PAGE_SIZE = 3;

方法1

import org.apache.commons.collections4.ListUtils;

List<List<String>> partitions = ListUtils.partition(list, PAGE_SIZE);

方法2

import com.google.common.collect.Lists;

List<List<String>> partitions = Lists.partition(list, PAGE_SIZE);

方法3(自作)

List<T> datas = ...;

int totalCount = datas.size();

int requestCount = totalCount / PAGE_SIZE;

for (int i = 0; i <= requestCount; i++) {

int fromIndex = i * PAGE_SIZE;

int toIndex = Math.min(totalCount, (i + 1) * PAGE_SIZE);

List<T> subList = datas.subList(fromIndex, toIndex);

// totalCount =< PAGE_SIZE

if (toIndex == totalCount) {

break;

}

}

計算量(Java)

List

Map

Set

Queue

★効率比較

最後から追加

list.add(obj); ArrayList > LinkedList

任意に追加

list.add(0, obj); LinkedList > ArrayList

最初を削除

list.remove(0); LinkedList > ArrayList

中央を削除

list.remove(list.size() >> 1); ArrayList > LinkedList

最後を削除

list.remove(list.size() - 1); ArrayList = LinkedList

★Arrays.asListについて

int[] data = {1,2,3,4,5};

List list = Arrays.asList(data); // listのサイズ=1

System.out.println(data.equals(list.get(0))); true

※コレクションは基本型を含めいないため

Integer[] data = {1,2,3,4,5};

List list = Arrays.asList(data); // listのサイズ=5

list.add(6); 実行エラー

asListメソッドから戻った結果リストは変更できない、読み取り専用

改良版

class ArrayUtils{

public static <T> List<T> asList(T... t){

List<T> list = new ArrayList<T>();

Collections.addAll(list, t);

return list;

}

}

List<String> list1 = ArrayUtils.<String>asList("a", "b");

List<Integer> list2 = ArrayUtils.<Integer>asList();

List<Number> list3 = ArrayUtils.<Number>asList(1, 2, 3.6);

List<String> names = Arrays.asList(names); // read-only

List<String> names = new ArrayList<>(Arrays.asList(names));

List<String> names = Arrays.stream(names).collect(Collectors.toList()); // Java 8

// Collectors.toCollection(LinkedList::new)

★VectorとHashTable

Vector ArrayListのマルチスレッド版

HashTable HashMapのマルチスレッド版

並列処理の例

final List<Integer> tickets = new ArrayList<Integer>(); ×

final List<Integer> tickets = new Vector<Integer>(); ×

tickets.add(1);

...

Thread returnThread = new Thread(){

public void run(){

while(true){

tickets.add(new Random().nextInt());

}

}

};

Thread saleThread = new Thread(){

public void run(){

for(Integer ticket : tickets){

tickets.remove(ticket);

}

}

};

returnThread.start();

saleThread.start();

※実行エラー:java.util.ConcurrentModificationException

スレッドセーフの例

final List<Integer> tickets = new ArrayList<Integer>(); ×

final List<Integer> tickets = new Vector<Integer>(); 〇

tickets.add(1);

...

for(int i = 0; i < 10; i++){

new Thread(){

public void run(){

while(true){

System.out.println(

Thread.currentThread().getId() + " " + tickets.remove(0));

}

}

}.start();

}

★Thread Safeなコレクションに変換

JDKを参照

Map m = Collections.synchronizedMap(new HashMap());

// List l = Collections.sychronizedList(new ArrayList());

...

Set s = m.keySet(); // Needn't be in synchronized block

...

synchronized(m) { // Synchronizing on m, not s!

Iterator i = s.iterator(); // Must be in synchronized block

while (i.hasNext())

foo(i.next());

}

※変換処理より、java.util.concurrentパッケージのThread Safeなコレクションを使った方がよい

★初期化の方法

Set<String> set = new HashSet<String>();

set.add("a");

set.add("b");

private static final Set<String> set = new HashSet<String>();

static {

set.add("a");

set.add("b");

}

private static final Set<String> set = new HashSet<String>() {{

add("a");

add("b");

}};

List<String> list = new ArrayList<String>(Arrays.asList("a", "b"));

★PriorityQueueについて

・常に一番小さい値を最初に置く

PriorityQueue ソートなし

PriorityBlockingQueue Thread-safe

TreeSet 自動ソート

Queue<Item> items = new PriorityQueue<Item>();

items.add(new Item("xxx", 1000));

//items.add(null); ×

Item item = items.poll(); //poll()、peek()、element()

public class Item implements Comparable<Item> {

...

@Override

public int compareTo(Item item) {

if (this.price == item.price) {

return this.name.compareTo(item.name);

}

return this.price - item.price;

}

}

Queue<Integer> priorityQueue = new PriorityQueue<Integer>(10);

Queue<Customer> priorityQueue = new PriorityQueue<Customer>(10, comparator);

public static Comparator<Customer> comparator = new Comparator<Customer>(){

@Override

public int compare(Customer c1, Customer c2) {

return (int) (c1.getId() - c2.getId());

}

};

★TreeSetの注意点

Setの重複意味 equalsにより判断

TreeSetの順序意味 compareToにより判断

TreeSetとListの使い分け

TreeSet 定数(String, Integerなど基本型)のソートに向かう

List 変数(自作クラス)のソートに向かう

class Person implements Comparable<Person>{

private int height;

// constructor

// setter/getter

public int compareTo(Person o){

return this.height - o.height; // 昇順

}

}

SortedSet<Person> set = new TreeSet<Person>();

set.add(new Person(180));

set.add(new Person(175));

set.first().setHeight(185); ←途中変更後、順序が保証できない

解決方法1

set = new TreeSet<Person>(new ArrayList<Person>(set)); // 再度ソート

set = new TreeSet<Person>(set); × Shallow Cloneのため

解決方法2

List<Person> set = new ArrayList<Person>();

...

Collections.sort();

※なお、データの唯一性を保証するため、List⇒HashSet⇒List

★コレクション中の乱数

int num = 10;

List<String> list = new ArrayList<String>(num);

list.add("a");

...

Random rand = new Random();

for(int i = 0; i < num; i++){

int randPos = rand.nextInt(num);

// 方法1

String temp = list.get(i);

list.set(i, list.get(randPos));

list.set(randPos, temp);

// 方法2

Collections.swap(list, i, randPos);

// 方法3

Collections.shuffle(list);

}

※shuffleの応用例:ゲーム中に物の分配、宝くじ、セキュリティのデータ転送

★Arrays.sortとCollections.sort

java.util.Collections.sort(List<T>list, Comparator<? super T> c)

java.util.Arrays.sort(T[], Comparator <? super T> c)

>Strategy pattern

class SizeComparator implements Comparator<Dog>{

@Override

public int compare(Dog o1, Dog o2) {

return o1.size - o2.size;

}

}

class WeightComparator implements Comparator<Dog>{

@Override

public int compare(Dog o1, Dog o2) {

return o1.weight - o2.weight;

}

}

Dog[] dogArray = {...};

Arrays.sort(dogArray, new SizeComparator());

Arrays.sort(dogArray, new WeightComparator());

>superの作用

class Animal{

int size;

}

class Dog extends Animal{...}

class Cat extends Animal{...}

class SizeComparator implements Comparator<Animal>{

@Override

public int compare(Animal o1, Animal o2) {

return o1.size - o2.size;

}

}

Dog[] dogArray = {...};

Arrays.sort(dogArray, new SizeComparator());

Cat[] catArray = {...};

Arrays.sort(catArray, new SizeComparator());

★Mapに対する操作

Map⇒List

List keyList = new ArrayList(map.keySet());

List valueList = new ArrayList(map.valueSet());

List<Entry> entryList = new ArrayList<Entry>(map.entrySet());

for(Entry entry: map.entrySet()) {

K key = entry.getKey();

V value = entry.getValue();

}

あるいは

Iterator<Entry> itr = map.entrySet().iterator();

while(itr.hasNext()) {

Entry entry = itr.next();

K key = entry.getKey();

V value = entry.getValue();

}

Java8版

Map<Integer, String> map = students.stream()

.collect(Collectors.toMap(Student::getId, Student::getName));

//Handle Duplicate Keys

.collect(Collectors.toMap(Student::getId, Student::getName, (oldValue, newValue) -> oldValue));

// (oldValue, newValue) -> newValue

//Preserve List Order

.collect(Collectors.toMap(Student::getId, Student::getName, (oldValue, newValue) -> oldValue, LinkedHashMap::new));

List<Entry> list = new ArrayList<Entry>(map.entrySet());

Collections.sort(list, new Comparator<Entry>() {

@Override

public int compare(Entry e1, Entry e2) {

return e1.getKey().compareTo(e2.getKey()); //By Key

return e1.getValue().compareTo(e2.getValue()); //By Value

}

});

あるいは

SortedMap sortedMap = new TreeMap(new Comparator() {

@Override

public int compare(K k1, K k2) {

return k1.compareTo(k2);

}

});

sortedMap.putAll(map);

Map map = new HashMap();

map.put(1, "one");

...

mapX = Collections.unmodifiableMap(map);

Map<K, V> copiedMap = Collections.synchronizedMap(map); //Shallow copy

map = Collections.emptyMap(); //immutable

map = new HashMap();

for (String value : map.values()) {

...

}

繰り返し制御構文について

// Better way

for (Map.Entry<String,Integer> entry : map.entrySet()) {

String key = entry.getKey();

Integer value = entry.getValue();

}

for (String key : map.keySet()) {

Integer value = map.get(key);

}

Iterator<Map.Entry<String,Integer>> itr = map.entrySet().iterator();

while(itr.hasNext()) {

Map.Entry<String,Integer> entry = itr.next();

String key = entry.getKey();

Integer value = entry.getValue();

itr.remove(); // avoid ConcurrentModificationException

}

Iterator itr = map.keySet().iterator();

while(itr.hasNext()) {

String key = itr.next();

Integer value = map.get(key);

}

map.forEach((key, value)->{

...

});

entrySet() in foreach loop : 50

keySet() in foreach loop : 76

entrySet() and iterator : 50

keySet() and iterator : 75