配列とコレクション
アウトライン(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