You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hivemall.apache.org by my...@apache.org on 2017/04/09 21:32:18 UTC

[06/12] incubator-hivemall git commit: Close #51: [HIVEMALL-75] Support Sparse Vector Format as the input of RandomForest

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/OpenHashTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/OpenHashTable.java b/core/src/main/java/hivemall/utils/collections/OpenHashTable.java
deleted file mode 100644
index 1a3dff7..0000000
--- a/core/src/main/java/hivemall/utils/collections/OpenHashTable.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package hivemall.utils.collections;
-
-import hivemall.utils.lang.Copyable;
-import hivemall.utils.math.Primes;
-
-import java.io.Externalizable;
-import java.io.IOException;
-import java.io.ObjectInput;
-import java.io.ObjectOutput;
-import java.util.Arrays;
-import java.util.HashMap;
-
-import javax.annotation.Nonnull;
-
-/**
- * An open-addressing hash table with double-hashing that requires less memory to {@link HashMap}.
- */
-public final class OpenHashTable<K, V> implements Externalizable {
-
-    public static final float DEFAULT_LOAD_FACTOR = 0.7f;
-    public static final float DEFAULT_GROW_FACTOR = 2.0f;
-
-    protected static final byte FREE = 0;
-    protected static final byte FULL = 1;
-    protected static final byte REMOVED = 2;
-
-    protected/* final */float _loadFactor;
-    protected/* final */float _growFactor;
-
-    protected int _used = 0;
-    protected int _threshold;
-
-    protected K[] _keys;
-    protected V[] _values;
-    protected byte[] _states;
-
-    public OpenHashTable() {} // for Externalizable
-
-    public OpenHashTable(int size) {
-        this(size, DEFAULT_LOAD_FACTOR, DEFAULT_GROW_FACTOR);
-    }
-
-    @SuppressWarnings("unchecked")
-    public OpenHashTable(int size, float loadFactor, float growFactor) {
-        if (size < 1) {
-            throw new IllegalArgumentException();
-        }
-        this._loadFactor = loadFactor;
-        this._growFactor = growFactor;
-        int actualSize = Primes.findLeastPrimeNumber(size);
-        this._keys = (K[]) new Object[actualSize];
-        this._values = (V[]) new Object[actualSize];
-        this._states = new byte[actualSize];
-        this._threshold = Math.round(actualSize * _loadFactor);
-    }
-
-    public OpenHashTable(@Nonnull K[] keys, @Nonnull V[] values, @Nonnull byte[] states, int used) {
-        this._used = used;
-        this._threshold = keys.length;
-        this._keys = keys;
-        this._values = values;
-        this._states = states;
-    }
-
-    public Object[] getKeys() {
-        return _keys;
-    }
-
-    public Object[] getValues() {
-        return _values;
-    }
-
-    public byte[] getStates() {
-        return _states;
-    }
-
-    public boolean containsKey(final K key) {
-        return findKey(key) >= 0;
-    }
-
-    public V get(final K key) {
-        final int i = findKey(key);
-        if (i < 0) {
-            return null;
-        }
-        return _values[i];
-    }
-
-    public V put(final K key, final V value) {
-        int hash = keyHash(key);
-        int keyLength = _keys.length;
-        int keyIdx = hash % keyLength;
-
-        boolean expanded = preAddEntry(keyIdx);
-        if (expanded) {
-            keyLength = _keys.length;
-            keyIdx = hash % keyLength;
-        }
-
-        K[] keys = _keys;
-        V[] values = _values;
-        byte[] states = _states;
-
-        if (states[keyIdx] == FULL) {
-            if (equals(keys[keyIdx], key)) {
-                V old = values[keyIdx];
-                values[keyIdx] = value;
-                return old;
-            }
-            // try second hash
-            int decr = 1 + (hash % (keyLength - 2));
-            for (;;) {
-                keyIdx -= decr;
-                if (keyIdx < 0) {
-                    keyIdx += keyLength;
-                }
-                if (isFree(keyIdx, key)) {
-                    break;
-                }
-                if (states[keyIdx] == FULL && equals(keys[keyIdx], key)) {
-                    V old = values[keyIdx];
-                    values[keyIdx] = value;
-                    return old;
-                }
-            }
-        }
-        keys[keyIdx] = key;
-        values[keyIdx] = value;
-        states[keyIdx] = FULL;
-        ++_used;
-        return null;
-    }
-
-    private static boolean equals(final Object k1, final Object k2) {
-        return k1 == k2 || k1.equals(k2);
-    }
-
-    /** Return weather the required slot is free for new entry */
-    protected boolean isFree(int index, K key) {
-        byte stat = _states[index];
-        if (stat == FREE) {
-            return true;
-        }
-        if (stat == REMOVED && equals(_keys[index], key)) {
-            return true;
-        }
-        return false;
-    }
-
-    /** @return expanded or not */
-    protected boolean preAddEntry(int index) {
-        if ((_used + 1) >= _threshold) {// filled enough
-            int newCapacity = Math.round(_keys.length * _growFactor);
-            ensureCapacity(newCapacity);
-            return true;
-        }
-        return false;
-    }
-
-    protected int findKey(final K key) {
-        K[] keys = _keys;
-        byte[] states = _states;
-        int keyLength = keys.length;
-
-        int hash = keyHash(key);
-        int keyIdx = hash % keyLength;
-        if (states[keyIdx] != FREE) {
-            if (states[keyIdx] == FULL && equals(keys[keyIdx], key)) {
-                return keyIdx;
-            }
-            // try second hash
-            int decr = 1 + (hash % (keyLength - 2));
-            for (;;) {
-                keyIdx -= decr;
-                if (keyIdx < 0) {
-                    keyIdx += keyLength;
-                }
-                if (isFree(keyIdx, key)) {
-                    return -1;
-                }
-                if (states[keyIdx] == FULL && equals(keys[keyIdx], key)) {
-                    return keyIdx;
-                }
-            }
-        }
-        return -1;
-    }
-
-    public V remove(final K key) {
-        K[] keys = _keys;
-        V[] values = _values;
-        byte[] states = _states;
-        int keyLength = keys.length;
-
-        int hash = keyHash(key);
-        int keyIdx = hash % keyLength;
-        if (states[keyIdx] != FREE) {
-            if (states[keyIdx] == FULL && equals(keys[keyIdx], key)) {
-                V old = values[keyIdx];
-                states[keyIdx] = REMOVED;
-                --_used;
-                return old;
-            }
-            //  second hash
-            int decr = 1 + (hash % (keyLength - 2));
-            for (;;) {
-                keyIdx -= decr;
-                if (keyIdx < 0) {
-                    keyIdx += keyLength;
-                }
-                if (states[keyIdx] == FREE) {
-                    return null;
-                }
-                if (states[keyIdx] == FULL && equals(keys[keyIdx], key)) {
-                    V old = values[keyIdx];
-                    states[keyIdx] = REMOVED;
-                    --_used;
-                    return old;
-                }
-            }
-        }
-        return null;
-    }
-
-    public int size() {
-        return _used;
-    }
-
-    public void clear() {
-        Arrays.fill(_states, FREE);
-        this._used = 0;
-    }
-
-    public IMapIterator<K, V> entries() {
-        return new MapIterator();
-    }
-
-    @Override
-    public String toString() {
-        int len = size() * 10 + 2;
-        final StringBuilder buf = new StringBuilder(len);
-        buf.append('{');
-        final IMapIterator<K, V> i = entries();
-        while (i.next() != -1) {
-            String key = i.getKey().toString();
-            buf.append(key);
-            buf.append('=');
-            buf.append(i.getValue());
-            if (i.hasNext()) {
-                buf.append(',');
-            }
-        }
-        buf.append('}');
-        return buf.toString();
-    }
-
-    protected void ensureCapacity(int newCapacity) {
-        int prime = Primes.findLeastPrimeNumber(newCapacity);
-        rehash(prime);
-        this._threshold = Math.round(prime * _loadFactor);
-    }
-
-    @SuppressWarnings("unchecked")
-    private void rehash(int newCapacity) {
-        int oldCapacity = _keys.length;
-        if (newCapacity <= oldCapacity) {
-            throw new IllegalArgumentException("new: " + newCapacity + ", old: " + oldCapacity);
-        }
-        final K[] newkeys = (K[]) new Object[newCapacity];
-        final V[] newValues = (V[]) new Object[newCapacity];
-        final byte[] newStates = new byte[newCapacity];
-        int used = 0;
-        for (int i = 0; i < oldCapacity; i++) {
-            if (_states[i] == FULL) {
-                used++;
-                K k = _keys[i];
-                V v = _values[i];
-                int hash = keyHash(k);
-                int keyIdx = hash % newCapacity;
-                if (newStates[keyIdx] == FULL) {// second hashing
-                    int decr = 1 + (hash % (newCapacity - 2));
-                    while (newStates[keyIdx] != FREE) {
-                        keyIdx -= decr;
-                        if (keyIdx < 0) {
-                            keyIdx += newCapacity;
-                        }
-                    }
-                }
-                newStates[keyIdx] = FULL;
-                newkeys[keyIdx] = k;
-                newValues[keyIdx] = v;
-            }
-        }
-        this._keys = newkeys;
-        this._values = newValues;
-        this._states = newStates;
-        this._used = used;
-    }
-
-    private static int keyHash(final Object key) {
-        int hash = key.hashCode();
-        return hash & 0x7fffffff;
-    }
-
-    private final class MapIterator implements IMapIterator<K, V> {
-
-        int nextEntry;
-        int lastEntry = -1;
-
-        MapIterator() {
-            this.nextEntry = nextEntry(0);
-        }
-
-        /** find the index of next full entry */
-        int nextEntry(int index) {
-            while (index < _keys.length && _states[index] != FULL) {
-                index++;
-            }
-            return index;
-        }
-
-        public boolean hasNext() {
-            return nextEntry < _keys.length;
-        }
-
-        public int next() {
-            if (!hasNext()) {
-                return -1;
-            }
-            int curEntry = nextEntry;
-            this.lastEntry = nextEntry;
-            this.nextEntry = nextEntry(nextEntry + 1);
-            return curEntry;
-        }
-
-        public K getKey() {
-            if (lastEntry == -1) {
-                throw new IllegalStateException();
-            }
-            return _keys[lastEntry];
-        }
-
-        public V getValue() {
-            if (lastEntry == -1) {
-                throw new IllegalStateException();
-            }
-            return _values[lastEntry];
-        }
-
-        @Override
-        public <T extends Copyable<V>> void getValue(T probe) {
-            probe.copyFrom(getValue());
-        }
-    }
-
-    @Override
-    public void writeExternal(ObjectOutput out) throws IOException {
-        out.writeFloat(_loadFactor);
-        out.writeFloat(_growFactor);
-        out.writeInt(_used);
-
-        final int size = _keys.length;
-        out.writeInt(size);
-
-        for (int i = 0; i < size; i++) {
-            out.writeObject(_keys[i]);
-            out.writeObject(_values[i]);
-            out.writeByte(_states[i]);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
-        this._loadFactor = in.readFloat();
-        this._growFactor = in.readFloat();
-        this._used = in.readInt();
-
-        final int size = in.readInt();
-        final Object[] keys = new Object[size];
-        final Object[] values = new Object[size];
-        final byte[] states = new byte[size];
-        for (int i = 0; i < size; i++) {
-            keys[i] = in.readObject();
-            values[i] = in.readObject();
-            states[i] = in.readByte();
-        }
-        this._threshold = size;
-        this._keys = (K[]) keys;
-        this._values = (V[]) values;
-        this._states = states;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/SparseDoubleArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/SparseDoubleArray.java b/core/src/main/java/hivemall/utils/collections/SparseDoubleArray.java
deleted file mode 100644
index c4dbbb5..0000000
--- a/core/src/main/java/hivemall/utils/collections/SparseDoubleArray.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package hivemall.utils.collections;
-
-import hivemall.utils.lang.ArrayUtils;
-import hivemall.utils.lang.Preconditions;
-
-import java.util.Arrays;
-
-import javax.annotation.Nonnull;
-
-public final class SparseDoubleArray implements DoubleArray {
-    private static final long serialVersionUID = -2814248784231540118L;
-
-    @Nonnull
-    private int[] mKeys;
-    @Nonnull
-    private double[] mValues;
-    private int mSize;
-
-    public SparseDoubleArray() {
-        this(10);
-    }
-
-    public SparseDoubleArray(int initialCapacity) {
-        mKeys = new int[initialCapacity];
-        mValues = new double[initialCapacity];
-        mSize = 0;
-    }
-
-    private SparseDoubleArray(@Nonnull int[] mKeys, @Nonnull double[] mValues, int mSize) {
-        this.mKeys = mKeys;
-        this.mValues = mValues;
-        this.mSize = mSize;
-    }
-
-    @Nonnull
-    public SparseDoubleArray deepCopy() {
-        int[] newKeys = new int[mSize];
-        double[] newValues = new double[mSize];
-        System.arraycopy(mKeys, 0, newKeys, 0, mSize);
-        System.arraycopy(mValues, 0, newValues, 0, mSize);
-        return new SparseDoubleArray(newKeys, newValues, mSize);
-    }
-
-    @Override
-    public double get(int key) {
-        return get(key, 0);
-    }
-
-    @Override
-    public double get(int key, double valueIfKeyNotFound) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i < 0) {
-            return valueIfKeyNotFound;
-        } else {
-            return mValues[i];
-        }
-    }
-
-    public void delete(int key) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            removeAt(i);
-        }
-    }
-
-    public void removeAt(int index) {
-        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
-        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
-        mSize--;
-    }
-
-    @Override
-    public void put(int key, double value) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            mValues[i] = value;
-        } else {
-            i = ~i;
-            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
-            mValues = ArrayUtils.insert(mValues, mSize, i, value);
-            mSize++;
-        }
-    }
-
-    public void increment(int key, double value) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            mValues[i] += value;
-        } else {
-            i = ~i;
-            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
-            mValues = ArrayUtils.insert(mValues, mSize, i, value);
-            mSize++;
-        }
-    }
-
-    @Override
-    public int size() {
-        return mSize;
-    }
-
-    @Override
-    public int keyAt(int index) {
-        return mKeys[index];
-    }
-
-    public double valueAt(int index) {
-        return mValues[index];
-    }
-
-    public void setValueAt(int index, double value) {
-        mValues[index] = value;
-    }
-
-    public int indexOfKey(int key) {
-        return Arrays.binarySearch(mKeys, 0, mSize, key);
-    }
-
-    public int indexOfValue(double value) {
-        for (int i = 0; i < mSize; i++) {
-            if (mValues[i] == value) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    public void clear() {
-        clear(true);
-    }
-
-    public void clear(boolean zeroFill) {
-        mSize = 0;
-        if (zeroFill) {
-            Arrays.fill(mKeys, 0);
-            Arrays.fill(mValues, 0.d);
-        }
-    }
-
-    public void append(int key, double value) {
-        if (mSize != 0 && key <= mKeys[mSize - 1]) {
-            put(key, value);
-            return;
-        }
-        mKeys = ArrayUtils.append(mKeys, mSize, key);
-        mValues = ArrayUtils.append(mValues, mSize, value);
-        mSize++;
-    }
-
-    @Override
-    public double[] toArray() {
-        return toArray(true);
-    }
-
-    @Override
-    public double[] toArray(boolean copy) {
-        if (mSize == 0) {
-            return new double[0];
-        }
-
-        int last = mKeys[mSize - 1];
-        final double[] array = new double[last + 1];
-        for (int i = 0; i < mSize; i++) {
-            int k = mKeys[i];
-            double v = mValues[i];
-            Preconditions.checkArgument(k >= 0, "Negative key is not allowed for toArray(): " + k);
-            array[k] = v;
-        }
-        return array;
-    }
-
-    @Override
-    public String toString() {
-        if (size() <= 0) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i = 0; i < mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            int key = keyAt(i);
-            buffer.append(key);
-            buffer.append('=');
-            double value = valueAt(i);
-            buffer.append(value);
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/SparseIntArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/SparseIntArray.java b/core/src/main/java/hivemall/utils/collections/SparseIntArray.java
deleted file mode 100644
index 7a4ba69..0000000
--- a/core/src/main/java/hivemall/utils/collections/SparseIntArray.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package hivemall.utils.collections;
-
-import hivemall.utils.lang.ArrayUtils;
-import hivemall.utils.lang.Preconditions;
-
-import java.util.Arrays;
-
-import javax.annotation.Nonnull;
-
-public final class SparseIntArray implements IntArray {
-    private static final long serialVersionUID = -2814248784231540118L;
-
-    private int[] mKeys;
-    private int[] mValues;
-    private int mSize;
-
-    public SparseIntArray() {
-        this(10);
-    }
-
-    public SparseIntArray(int initialCapacity) {
-        mKeys = new int[initialCapacity];
-        mValues = new int[initialCapacity];
-        mSize = 0;
-    }
-
-    private SparseIntArray(int[] mKeys, int[] mValues, int mSize) {
-        this.mKeys = mKeys;
-        this.mValues = mValues;
-        this.mSize = mSize;
-    }
-
-    public IntArray deepCopy() {
-        int[] newKeys = new int[mSize];
-        int[] newValues = new int[mSize];
-        System.arraycopy(mKeys, 0, newKeys, 0, mSize);
-        System.arraycopy(mValues, 0, newValues, 0, mSize);
-        return new SparseIntArray(newKeys, newValues, mSize);
-    }
-
-    @Override
-    public int get(int key) {
-        return get(key, 0);
-    }
-
-    @Override
-    public int get(int key, int valueIfKeyNotFound) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i < 0) {
-            return valueIfKeyNotFound;
-        } else {
-            return mValues[i];
-        }
-    }
-
-    public void delete(int key) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            removeAt(i);
-        }
-    }
-
-    public void removeAt(int index) {
-        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
-        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
-        mSize--;
-    }
-
-    @Override
-    public void put(int key, int value) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            mValues[i] = value;
-        } else {
-            i = ~i;
-            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
-            mValues = ArrayUtils.insert(mValues, mSize, i, value);
-            mSize++;
-        }
-    }
-
-    public void increment(int key, int value) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
-        if (i >= 0) {
-            mValues[i] += value;
-        } else {
-            i = ~i;
-            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
-            mValues = ArrayUtils.insert(mValues, mSize, i, value);
-            mSize++;
-        }
-    }
-
-    @Override
-    public int size() {
-        return mSize;
-    }
-
-    @Override
-    public int keyAt(int index) {
-        return mKeys[index];
-    }
-
-    public int valueAt(int index) {
-        return mValues[index];
-    }
-
-    public void setValueAt(int index, int value) {
-        mValues[index] = value;
-    }
-
-    public int indexOfKey(int key) {
-        return Arrays.binarySearch(mKeys, 0, mSize, key);
-    }
-
-    public int indexOfValue(int value) {
-        for (int i = 0; i < mSize; i++) {
-            if (mValues[i] == value) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    public void clear() {
-        clear(true);
-    }
-
-    public void clear(boolean zeroFill) {
-        mSize = 0;
-        if (zeroFill) {
-            Arrays.fill(mKeys, 0);
-            Arrays.fill(mValues, 0);
-        }
-    }
-
-    public void append(int key, int value) {
-        if (mSize != 0 && key <= mKeys[mSize - 1]) {
-            put(key, value);
-            return;
-        }
-        mKeys = ArrayUtils.append(mKeys, mSize, key);
-        mValues = ArrayUtils.append(mValues, mSize, value);
-        mSize++;
-    }
-
-    @Nonnull
-    public int[] toArray() {
-        return toArray(true);
-    }
-
-    @Override
-    public int[] toArray(boolean copy) {
-        if (mSize == 0) {
-            return new int[0];
-        }
-
-        int last = mKeys[mSize - 1];
-        final int[] array = new int[last + 1];
-        for (int i = 0; i < mSize; i++) {
-            int k = mKeys[i];
-            int v = mValues[i];
-            Preconditions.checkArgument(k >= 0, "Negative key is not allowed for toArray(): " + k);
-            array[k] = v;
-        }
-        return array;
-    }
-
-    @Override
-    public String toString() {
-        if (size() <= 0) {
-            return "{}";
-        }
-
-        StringBuilder buffer = new StringBuilder(mSize * 28);
-        buffer.append('{');
-        for (int i = 0; i < mSize; i++) {
-            if (i > 0) {
-                buffer.append(", ");
-            }
-            int key = keyAt(i);
-            buffer.append(key);
-            buffer.append('=');
-            int value = valueAt(i);
-            buffer.append(value);
-        }
-        buffer.append('}');
-        return buffer.toString();
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/DenseDoubleArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/DenseDoubleArray.java b/core/src/main/java/hivemall/utils/collections/arrays/DenseDoubleArray.java
new file mode 100644
index 0000000..f79f039
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/DenseDoubleArray.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fixed double array that has keys greater than or equals to 0.
+ */
+public final class DenseDoubleArray implements DoubleArray {
+    private static final long serialVersionUID = 4282904528662802088L;
+
+    @Nonnull
+    private final double[] array;
+    private final int size;
+
+    public DenseDoubleArray(@Nonnull int size) {
+        this.array = new double[size];
+        this.size = size;
+    }
+
+    public DenseDoubleArray(@Nonnull double[] array) {
+        this.array = array;
+        this.size = array.length;
+    }
+
+    @Override
+    public double get(int index) {
+        return array[index];
+    }
+
+    @Override
+    public double get(int index, double valueIfKeyNotFound) {
+        if (index >= size) {
+            return valueIfKeyNotFound;
+        }
+        return array[index];
+    }
+
+    @Override
+    public void put(int index, double value) {
+        array[index] = value;
+    }
+
+    @Override
+    public int size() {
+        return array.length;
+    }
+
+    @Override
+    public int keyAt(int index) {
+        return index;
+    }
+
+    @Override
+    public double[] toArray() {
+        return toArray(true);
+    }
+
+    @Override
+    public double[] toArray(boolean copy) {
+        if (copy) {
+            return Arrays.copyOf(array, size);
+        } else {
+            return array;
+        }
+    }
+
+    @Override
+    public void clear() {
+        Arrays.fill(array, 0.d);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/DenseIntArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/DenseIntArray.java b/core/src/main/java/hivemall/utils/collections/arrays/DenseIntArray.java
new file mode 100644
index 0000000..0869ff2
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/DenseIntArray.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A fixed INT array that has keys greater than or equals to 0.
+ */
+public final class DenseIntArray implements IntArray {
+    private static final long serialVersionUID = -1450212841013810240L;
+
+    @Nonnull
+    private final int[] array;
+    private final int size;
+
+    public DenseIntArray(@Nonnull int size) {
+        this.array = new int[size];
+        this.size = size;
+    }
+
+    public DenseIntArray(@Nonnull int[] array) {
+        this.array = array;
+        this.size = array.length;
+    }
+
+    @Override
+    public int get(int index) {
+        return array[index];
+    }
+
+    @Override
+    public int get(int index, int valueIfKeyNotFound) {
+        if (index >= size) {
+            return valueIfKeyNotFound;
+        }
+        return array[index];
+    }
+
+    @Override
+    public void put(int index, int value) {
+        array[index] = value;
+    }
+
+    @Override
+    public void increment(int index, int value) {
+        array[index] += value;
+    }
+
+    @Override
+    public int size() {
+        return array.length;
+    }
+
+    @Override
+    public int keyAt(int index) {
+        return index;
+    }
+
+    @Override
+    public int[] toArray() {
+        return toArray(true);
+    }
+
+    @Override
+    public int[] toArray(boolean copy) {
+        if (copy) {
+            return Arrays.copyOf(array, size);
+        } else {
+            return array;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray.java b/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray.java
new file mode 100644
index 0000000..c8f3e17
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public interface DoubleArray extends Serializable {
+
+    public double get(int key);
+
+    public double get(int key, double valueIfKeyNotFound);
+
+    public void put(int key, double value);
+
+    public int size();
+
+    public int keyAt(int index);
+
+    @Nonnull
+    public double[] toArray();
+
+    @Nonnull
+    public double[] toArray(boolean copy);
+    
+    public void clear();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray3D.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray3D.java b/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray3D.java
new file mode 100644
index 0000000..35feff9
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/DoubleArray3D.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import hivemall.utils.lang.Primitives;
+
+import java.nio.ByteBuffer;
+import java.nio.DoubleBuffer;
+
+import javax.annotation.Nonnull;
+
+public final class DoubleArray3D {
+    private static final int DEFAULT_SIZE = 100 * 100 * 10; // feature * field * factor
+
+    private final boolean direct;
+
+    @Nonnull
+    private DoubleBuffer buffer;
+    private int capacity;
+
+    private int size;
+    // number of array in each dimension
+    private int n1, n2, n3;
+    // pointer to each dimension
+    private int p1, p2;
+
+    private boolean sanityCheck;
+
+    public DoubleArray3D() {
+        this(DEFAULT_SIZE, true);
+    }
+
+    public DoubleArray3D(int initSize, boolean direct) {
+        this.direct = direct;
+        this.buffer = allocate(direct, initSize);
+        this.capacity = initSize;
+        this.size = -1;
+        this.sanityCheck = true;
+    }
+
+    public DoubleArray3D(int dim1, int dim2, int dim3) {
+        this.direct = true;
+        this.capacity = -1;
+        configure(dim1, dim2, dim3);
+        this.sanityCheck = true;
+    }
+
+    public void setSanityCheck(boolean enable) {
+        this.sanityCheck = enable;
+    }
+
+    public void configure(final int dim1, final int dim2, final int dim3) {
+        int requiredSize = cardinarity(dim1, dim2, dim3);
+        if (requiredSize > capacity) {
+            this.buffer = allocate(direct, requiredSize);
+            this.capacity = requiredSize;
+        }
+        this.size = requiredSize;
+        this.n1 = dim1;
+        this.n2 = dim2;
+        this.n3 = dim3;
+        this.p1 = n2 * n3;
+        this.p2 = n3;
+    }
+
+    public void clear() {
+        buffer.clear();
+        this.size = -1;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    int getCapacity() {
+        return capacity;
+    }
+
+    public double get(final int i, final int j, final int k) {
+        int idx = idx(i, j, k);
+        return buffer.get(idx);
+    }
+
+    public void set(final int i, final int j, final int k, final double val) {
+        int idx = idx(i, j, k);
+        buffer.put(idx, val);
+    }
+
+    private int idx(final int i, final int j, final int k) {
+        if (sanityCheck == false) {
+            return i * p1 + j * p2 + k;
+        }
+
+        if (size == -1) {
+            throw new IllegalStateException("Double3DArray#configure() is not called");
+        }
+        if (i >= n1 || i < 0) {
+            throw new ArrayIndexOutOfBoundsException("Index '" + i
+                    + "' out of bounds for 1st dimension of size " + n1);
+        }
+        if (j >= n2 || j < 0) {
+            throw new ArrayIndexOutOfBoundsException("Index '" + j
+                    + "' out of bounds for 2nd dimension of size " + n2);
+        }
+        if (k >= n3 || k < 0) {
+            throw new ArrayIndexOutOfBoundsException("Index '" + k
+                    + "' out of bounds for 3rd dimension of size " + n3);
+        }
+        final int idx = i * p1 + j * p2 + k;
+        if (idx >= size) {
+            throw new IndexOutOfBoundsException("Computed internal index '" + idx
+                    + "' exceeds buffer size '" + size + "' where i=" + i + ", j=" + j + ", k=" + k);
+        }
+        return idx;
+    }
+
+    private static int cardinarity(final int dim1, final int dim2, final int dim3) {
+        if (dim1 <= 0 || dim2 <= 0 || dim3 <= 0) {
+            throw new IllegalArgumentException("Detected negative dimension size. dim1=" + dim1
+                    + ", dim2=" + dim2 + ", dim3=" + dim3);
+        }
+        return dim1 * dim2 * dim3;
+    }
+
+    @Nonnull
+    private static DoubleBuffer allocate(final boolean direct, final int size) {
+        int bytes = size * Primitives.DOUBLE_BYTES;
+        ByteBuffer buf = direct ? ByteBuffer.allocateDirect(bytes) : ByteBuffer.allocate(bytes);
+        return buf.asDoubleBuffer();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/FloatArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/FloatArray.java b/core/src/main/java/hivemall/utils/collections/arrays/FloatArray.java
new file mode 100644
index 0000000..b72bdef
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/FloatArray.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public interface FloatArray extends Serializable {
+
+    public float get(int key);
+
+    public float get(int key, float valueIfKeyNotFound);
+
+    public void put(int key, float value);
+
+    public int size();
+
+    public int keyAt(int index);
+
+    @Nonnull
+    public float[] toArray();
+
+    @Nonnull
+    public float[] toArray(boolean copy);
+
+    public void clear();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/IntArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/IntArray.java b/core/src/main/java/hivemall/utils/collections/arrays/IntArray.java
new file mode 100644
index 0000000..8edb0d4
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/IntArray.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public interface IntArray extends Serializable {
+
+    public int get(int key);
+
+    public int get(int key, int valueIfKeyNotFound);
+
+    public void put(int key, int value);
+
+    public void increment(int key, int value);
+
+    public int size();
+
+    public int keyAt(int index);
+
+    @Nonnull
+    public int[] toArray();
+
+    @Nonnull
+    public int[] toArray(boolean copy);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/SparseDoubleArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/SparseDoubleArray.java b/core/src/main/java/hivemall/utils/collections/arrays/SparseDoubleArray.java
new file mode 100644
index 0000000..ac41951
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/SparseDoubleArray.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import hivemall.math.vector.VectorProcedure;
+import hivemall.utils.lang.ArrayUtils;
+import hivemall.utils.lang.Preconditions;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+public final class SparseDoubleArray implements DoubleArray {
+    private static final long serialVersionUID = -2814248784231540118L;
+
+    @Nonnull
+    private int[] mKeys;
+    @Nonnull
+    private double[] mValues;
+    private int mSize;
+
+    public SparseDoubleArray() {
+        this(10);
+    }
+
+    public SparseDoubleArray(int initialCapacity) {
+        mKeys = new int[initialCapacity];
+        mValues = new double[initialCapacity];
+        mSize = 0;
+    }
+
+    private SparseDoubleArray(@Nonnull int[] mKeys, @Nonnull double[] mValues, int mSize) {
+        this.mKeys = mKeys;
+        this.mValues = mValues;
+        this.mSize = mSize;
+    }
+
+    @Nonnull
+    public SparseDoubleArray deepCopy() {
+        int[] newKeys = new int[mSize];
+        double[] newValues = new double[mSize];
+        System.arraycopy(mKeys, 0, newKeys, 0, mSize);
+        System.arraycopy(mValues, 0, newValues, 0, mSize);
+        return new SparseDoubleArray(newKeys, newValues, mSize);
+    }
+
+    @Override
+    public double get(int key) {
+        return get(key, 0);
+    }
+
+    @Override
+    public double get(int key, double valueIfKeyNotFound) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i < 0) {
+            return valueIfKeyNotFound;
+        } else {
+            return mValues[i];
+        }
+    }
+
+    public void delete(int key) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            removeAt(i);
+        }
+    }
+
+    public void removeAt(int index) {
+        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+        mSize--;
+    }
+
+    @Override
+    public void put(int key, double value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] = value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    public void increment(int key, double value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] += value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    public int keyAt(int index) {
+        return mKeys[index];
+    }
+
+    public double valueAt(int index) {
+        return mValues[index];
+    }
+
+    public void setValueAt(int index, double value) {
+        mValues[index] = value;
+    }
+
+    public int indexOfKey(int key) {
+        return Arrays.binarySearch(mKeys, 0, mSize, key);
+    }
+
+    public int indexOfValue(double value) {
+        for (int i = 0; i < mSize; i++) {
+            if (mValues[i] == value) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public void clear() {
+        clear(true);
+    }
+
+    public void clear(boolean zeroFill) {
+        mSize = 0;
+        if (zeroFill) {
+            Arrays.fill(mKeys, 0);
+            Arrays.fill(mValues, 0.d);
+        }
+    }
+
+    public void append(int key, double value) {
+        if (mSize != 0 && key <= mKeys[mSize - 1]) {
+            put(key, value);
+            return;
+        }
+        mKeys = ArrayUtils.append(mKeys, mSize, key);
+        mValues = ArrayUtils.append(mValues, mSize, value);
+        mSize++;
+    }
+
+    @Override
+    public double[] toArray() {
+        return toArray(true);
+    }
+
+    @Override
+    public double[] toArray(boolean copy) {
+        if (mSize == 0) {
+            return new double[0];
+        }
+
+        int last = mKeys[mSize - 1];
+        final double[] array = new double[last + 1];
+        for (int i = 0; i < mSize; i++) {
+            int k = mKeys[i];
+            double v = mValues[i];
+            Preconditions.checkArgument(k >= 0, "Negative key is not allowed for toArray(): " + k);
+            array[k] = v;
+        }
+        return array;
+    }
+
+    public void each(@Nonnull final VectorProcedure procedure) {
+        for (int i = 0; i < mSize; i++) {
+            int k = mKeys[i];
+            double v = mValues[i];
+            procedure.apply(k, v);
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i = 0; i < mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            double value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/SparseFloatArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/SparseFloatArray.java b/core/src/main/java/hivemall/utils/collections/arrays/SparseFloatArray.java
new file mode 100644
index 0000000..928de77
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/SparseFloatArray.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import hivemall.utils.lang.ArrayUtils;
+import hivemall.utils.lang.Preconditions;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+public final class SparseFloatArray implements FloatArray {
+    private static final long serialVersionUID = -2814248784231540118L;
+
+    private int[] mKeys;
+    private float[] mValues;
+    private int mSize;
+
+    public SparseFloatArray() {
+        this(10);
+    }
+
+    public SparseFloatArray(int initialCapacity) {
+        mKeys = new int[initialCapacity];
+        mValues = new float[initialCapacity];
+        mSize = 0;
+    }
+
+    private SparseFloatArray(@Nonnull int[] mKeys, @Nonnull float[] mValues, int mSize) {
+        this.mKeys = mKeys;
+        this.mValues = mValues;
+        this.mSize = mSize;
+    }
+
+    public SparseFloatArray deepCopy() {
+        int[] newKeys = new int[mSize];
+        float[] newValues = new float[mSize];
+        System.arraycopy(mKeys, 0, newKeys, 0, mSize);
+        System.arraycopy(mValues, 0, newValues, 0, mSize);
+        return new SparseFloatArray(newKeys, newValues, mSize);
+    }
+
+    @Override
+    public float get(int key) {
+        return get(key, 0.f);
+    }
+
+    @Override
+    public float get(int key, float valueIfKeyNotFound) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i < 0) {
+            return valueIfKeyNotFound;
+        } else {
+            return mValues[i];
+        }
+    }
+
+    public void delete(int key) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            removeAt(i);
+        }
+    }
+
+    public void removeAt(int index) {
+        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+        mSize--;
+    }
+
+    @Override
+    public void put(int key, float value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] = value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    public void increment(int key, float value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] += value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    public int keyAt(int index) {
+        return mKeys[index];
+    }
+
+    public float valueAt(int index) {
+        return mValues[index];
+    }
+
+    public void setValueAt(int index, float value) {
+        mValues[index] = value;
+    }
+
+    public int indexOfKey(int key) {
+        return Arrays.binarySearch(mKeys, 0, mSize, key);
+    }
+
+    public int indexOfValue(float value) {
+        for (int i = 0; i < mSize; i++) {
+            if (mValues[i] == value) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public void clear() {
+        clear(true);
+    }
+
+    public void clear(boolean zeroFill) {
+        mSize = 0;
+        if (zeroFill) {
+            Arrays.fill(mKeys, 0);
+            Arrays.fill(mValues, 0.f);
+        }
+    }
+
+    public void append(int key, float value) {
+        if (mSize != 0 && key <= mKeys[mSize - 1]) {
+            put(key, value);
+            return;
+        }
+        mKeys = ArrayUtils.append(mKeys, mSize, key);
+        mValues = ArrayUtils.append(mValues, mSize, value);
+        mSize++;
+    }
+
+    @Nonnull
+    public float[] toArray() {
+        return toArray(true);
+    }
+
+    @Override
+    public float[] toArray(boolean copy) {
+        if (mSize == 0) {
+            return new float[0];
+        }
+
+        int last = mKeys[mSize - 1];
+        final float[] array = new float[last + 1];
+        for (int i = 0; i < mSize; i++) {
+            int k = mKeys[i];
+            float v = mValues[i];
+            Preconditions.checkArgument(k >= 0, "Negative key is not allowed for toArray(): " + k);
+            array[k] = v;
+        }
+        return array;
+    }
+
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i = 0; i < mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            float value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/arrays/SparseIntArray.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/arrays/SparseIntArray.java b/core/src/main/java/hivemall/utils/collections/arrays/SparseIntArray.java
new file mode 100644
index 0000000..8de5476
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/arrays/SparseIntArray.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.arrays;
+
+import hivemall.utils.lang.ArrayUtils;
+import hivemall.utils.lang.Preconditions;
+
+import java.util.Arrays;
+
+import javax.annotation.Nonnull;
+
+public final class SparseIntArray implements IntArray {
+    private static final long serialVersionUID = -2814248784231540118L;
+
+    private int[] mKeys;
+    private int[] mValues;
+    private int mSize;
+
+    public SparseIntArray() {
+        this(10);
+    }
+
+    public SparseIntArray(int initialCapacity) {
+        mKeys = new int[initialCapacity];
+        mValues = new int[initialCapacity];
+        mSize = 0;
+    }
+
+    private SparseIntArray(int[] mKeys, int[] mValues, int mSize) {
+        this.mKeys = mKeys;
+        this.mValues = mValues;
+        this.mSize = mSize;
+    }
+
+    public IntArray deepCopy() {
+        int[] newKeys = new int[mSize];
+        int[] newValues = new int[mSize];
+        System.arraycopy(mKeys, 0, newKeys, 0, mSize);
+        System.arraycopy(mValues, 0, newValues, 0, mSize);
+        return new SparseIntArray(newKeys, newValues, mSize);
+    }
+
+    @Override
+    public int get(int key) {
+        return get(key, 0);
+    }
+
+    @Override
+    public int get(int key, int valueIfKeyNotFound) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i < 0) {
+            return valueIfKeyNotFound;
+        } else {
+            return mValues[i];
+        }
+    }
+
+    public void delete(int key) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            removeAt(i);
+        }
+    }
+
+    public void removeAt(int index) {
+        System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+        System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+        mSize--;
+    }
+
+    @Override
+    public void put(int key, int value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] = value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    @Override
+    public void increment(int key, int value) {
+        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        if (i >= 0) {
+            mValues[i] += value;
+        } else {
+            i = ~i;
+            mKeys = ArrayUtils.insert(mKeys, mSize, i, key);
+            mValues = ArrayUtils.insert(mValues, mSize, i, value);
+            mSize++;
+        }
+    }
+
+    @Override
+    public int size() {
+        return mSize;
+    }
+
+    @Override
+    public int keyAt(int index) {
+        return mKeys[index];
+    }
+
+    public int valueAt(int index) {
+        return mValues[index];
+    }
+
+    public void setValueAt(int index, int value) {
+        mValues[index] = value;
+    }
+
+    public int indexOfKey(int key) {
+        return Arrays.binarySearch(mKeys, 0, mSize, key);
+    }
+
+    public int indexOfValue(int value) {
+        for (int i = 0; i < mSize; i++) {
+            if (mValues[i] == value) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public void clear() {
+        clear(true);
+    }
+
+    public void clear(boolean zeroFill) {
+        mSize = 0;
+        if (zeroFill) {
+            Arrays.fill(mKeys, 0);
+            Arrays.fill(mValues, 0);
+        }
+    }
+
+    public void append(int key, int value) {
+        if (mSize != 0 && key <= mKeys[mSize - 1]) {
+            put(key, value);
+            return;
+        }
+        mKeys = ArrayUtils.append(mKeys, mSize, key);
+        mValues = ArrayUtils.append(mValues, mSize, value);
+        mSize++;
+    }
+
+    @Nonnull
+    public int[] toArray() {
+        return toArray(true);
+    }
+
+    @Override
+    public int[] toArray(boolean copy) {
+        if (mSize == 0) {
+            return new int[0];
+        }
+
+        int last = mKeys[mSize - 1];
+        final int[] array = new int[last + 1];
+        for (int i = 0; i < mSize; i++) {
+            int k = mKeys[i];
+            int v = mValues[i];
+            Preconditions.checkArgument(k >= 0, "Negative key is not allowed for toArray(): " + k);
+            array[k] = v;
+        }
+        return array;
+    }
+
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i = 0; i < mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            int value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/lists/DoubleArrayList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/lists/DoubleArrayList.java b/core/src/main/java/hivemall/utils/collections/lists/DoubleArrayList.java
new file mode 100644
index 0000000..feb614f
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/lists/DoubleArrayList.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.lists;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public final class DoubleArrayList implements Serializable {
+    private static final long serialVersionUID = -8155789759545975413L;
+    public static final int DEFAULT_CAPACITY = 12;
+
+    /** array entity */
+    private double[] data;
+    private int used;
+
+    public DoubleArrayList() {
+        this(DEFAULT_CAPACITY);
+    }
+
+    public DoubleArrayList(int size) {
+        this.data = new double[size];
+        this.used = 0;
+    }
+
+    public DoubleArrayList(double[] initValues) {
+        this.data = initValues;
+        this.used = initValues.length;
+    }
+
+    @Nonnull
+    public DoubleArrayList add(double value) {
+        if (used >= data.length) {
+            expand(used + 1);
+        }
+        data[used++] = value;
+        return this;
+    }
+
+    @Nonnull
+    public DoubleArrayList add(@Nonnull double[] values) {
+        final int needs = used + values.length;
+        if (needs >= data.length) {
+            expand(needs);
+        }
+        System.arraycopy(values, 0, data, used, values.length);
+        this.used = needs;
+        return this;
+    }
+
+    /**
+     * dynamic expansion.
+     */
+    private void expand(int max) {
+        while (data.length < max) {
+            final int len = data.length;
+            double[] newArray = new double[len * 2];
+            System.arraycopy(data, 0, newArray, 0, len);
+            this.data = newArray;
+        }
+    }
+
+    public double remove() {
+        return data[--used];
+    }
+
+    public double remove(int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        final double ret;
+        if (index == used) {
+            ret = data[index];
+            --used;
+        } else { // index < used
+            ret = data[index];
+            System.arraycopy(data, index + 1, data, index, used - index - 1);
+            --used;
+        }
+        return ret;
+    }
+
+    public void set(int index, double value) {
+        if (index > used) {
+            throw new IllegalArgumentException("Index MUST be less than \"size()\".");
+        } else if (index == used) {
+            ++used;
+        }
+        data[index] = value;
+    }
+
+    public double get(int index) {
+        if (index >= used)
+            throw new IndexOutOfBoundsException();
+        return data[index];
+    }
+
+    public double fastGet(int index) {
+        return data[index];
+    }
+
+    public int size() {
+        return used;
+    }
+
+    public boolean isEmpty() {
+        return used == 0;
+    }
+
+    public void clear() {
+        used = 0;
+    }
+
+    @Nonnull
+    public double[] toArray() {
+        return toArray(false);
+    }
+
+    @Nonnull
+    public double[] toArray(boolean close) {
+        final double[] newArray = new double[used];
+        System.arraycopy(data, 0, newArray, 0, used);
+        if (close) {
+            this.data = null;
+        }
+        return newArray;
+    }
+
+    public double[] array() {
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append('[');
+        for (int i = 0; i < used; i++) {
+            if (i != 0) {
+                buf.append(", ");
+            }
+            buf.append(data[i]);
+        }
+        buf.append(']');
+        return buf.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/lists/FloatArrayList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/lists/FloatArrayList.java b/core/src/main/java/hivemall/utils/collections/lists/FloatArrayList.java
new file mode 100644
index 0000000..54b214d
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/lists/FloatArrayList.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.lists;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public final class FloatArrayList implements Serializable {
+    private static final long serialVersionUID = 8764828070342317585L;
+
+    public static final int DEFAULT_CAPACITY = 12;
+
+    /** array entity */
+    private float[] data;
+    private int used;
+
+    public FloatArrayList() {
+        this(DEFAULT_CAPACITY);
+    }
+
+    public FloatArrayList(int size) {
+        this.data = new float[size];
+        this.used = 0;
+    }
+
+    public FloatArrayList(float[] initValues) {
+        this.data = initValues;
+        this.used = initValues.length;
+    }
+
+    @Nonnull
+    public FloatArrayList add(float value) {
+        if (used >= data.length) {
+            expand(used + 1);
+        }
+        data[used++] = value;
+        return this;
+    }
+
+    @Nonnull
+    public FloatArrayList add(@Nonnull float[] values) {
+        final int needs = used + values.length;
+        if (needs >= data.length) {
+            expand(needs);
+        }
+        System.arraycopy(values, 0, data, used, values.length);
+        this.used = needs;
+        return this;
+    }
+
+    /**
+     * dynamic expansion.
+     */
+    private void expand(int max) {
+        while (data.length < max) {
+            final int len = data.length;
+            float[] newArray = new float[len * 2];
+            System.arraycopy(data, 0, newArray, 0, len);
+            this.data = newArray;
+        }
+    }
+
+    public float remove() {
+        return data[--used];
+    }
+
+    public float remove(int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        final float ret;
+        if (index == used) {
+            ret = data[index];
+            --used;
+        } else { // index < used
+            ret = data[index];
+            System.arraycopy(data, index + 1, data, index, used - index - 1);
+            --used;
+        }
+        return ret;
+    }
+
+    public void set(int index, float value) {
+        if (index > used) {
+            throw new IllegalArgumentException("Index MUST be less than \"size()\".");
+        } else if (index == used) {
+            ++used;
+        }
+        data[index] = value;
+    }
+
+    public float get(int index) {
+        if (index >= used)
+            throw new IndexOutOfBoundsException();
+        return data[index];
+    }
+
+    public float fastGet(int index) {
+        return data[index];
+    }
+
+    public int size() {
+        return used;
+    }
+
+    public boolean isEmpty() {
+        return used == 0;
+    }
+
+    public void clear() {
+        this.used = 0;
+    }
+
+    public float[] toArray() {
+        return toArray(false);
+    }
+
+    public float[] toArray(boolean close) {
+        final float[] newArray = new float[used];
+        System.arraycopy(data, 0, newArray, 0, used);
+        if (close) {
+            this.data = null;
+        }
+        return newArray;
+    }
+
+    public float[] array() {
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append('[');
+        for (int i = 0; i < used; i++) {
+            if (i != 0) {
+                buf.append(", ");
+            }
+            buf.append(data[i]);
+        }
+        buf.append(']');
+        return buf.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/lists/IntArrayList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/lists/IntArrayList.java b/core/src/main/java/hivemall/utils/collections/lists/IntArrayList.java
new file mode 100644
index 0000000..ea17c5f
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/lists/IntArrayList.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.lists;
+
+import hivemall.utils.lang.ArrayUtils;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public final class IntArrayList implements Serializable {
+    private static final long serialVersionUID = -2147675120406747488L;
+    public static final int DEFAULT_CAPACITY = 12;
+
+    /** array entity */
+    private int[] data;
+    private int used;
+
+    public IntArrayList() {
+        this(DEFAULT_CAPACITY);
+    }
+
+    public IntArrayList(int size) {
+        this.data = new int[size];
+        this.used = 0;
+    }
+
+    public IntArrayList(int[] initValues) {
+        this.data = initValues;
+        this.used = initValues.length;
+    }
+
+    @Nonnull
+    public IntArrayList add(final int value) {
+        if (used >= data.length) {
+            expand(used + 1);
+        }
+        data[used++] = value;
+        return this;
+    }
+
+    @Nonnull
+    public IntArrayList add(@Nonnull final int[] values) {
+        final int needs = used + values.length;
+        if (needs >= data.length) {
+            expand(needs);
+        }
+        System.arraycopy(values, 0, data, used, values.length);
+        this.used = needs;
+        return this;
+    }
+
+    /**
+     * dynamic expansion.
+     */
+    private void expand(final int max) {
+        while (data.length < max) {
+            final int len = data.length;
+            int[] newArray = new int[len * 2];
+            System.arraycopy(data, 0, newArray, 0, len);
+            this.data = newArray;
+        }
+    }
+
+    public int remove() {
+        return data[--used];
+    }
+
+    public int remove(final int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        final int ret;
+        if (index == used) {
+            ret = data[index];
+            --used;
+        } else { // index < used
+            ret = data[index];
+            System.arraycopy(data, index + 1, data, index, used - index - 1);
+            --used;
+        }
+        return ret;
+    }
+
+    public void set(final int index, final int value) {
+        if (index > used) {
+            throw new IllegalArgumentException("Index " + index + " MUST be less than size() "
+                    + used);
+        } else if (index == used) {
+            ++used;
+        }
+        data[index] = value;
+    }
+
+    public int get(final int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException("Index " + index + " out of bounds " + used);
+        }
+        return data[index];
+    }
+
+    public int fastGet(final int index) {
+        return data[index];
+    }
+
+    /**
+     * @return -1 if not found.
+     */
+    public int indexOf(final int key) {
+        return ArrayUtils.indexOf(data, key, 0, used);
+    }
+
+    public boolean contains(final int key) {
+        return ArrayUtils.indexOf(data, key, 0, used) != -1;
+    }
+
+    public int size() {
+        return used;
+    }
+
+    public boolean isEmpty() {
+        return used == 0;
+    }
+
+    public void clear() {
+        used = 0;
+    }
+
+    @Nonnull
+    public int[] toArray() {
+        return toArray(false);
+    }
+
+    @Nonnull
+    public int[] toArray(boolean close) {
+        final int[] newArray = new int[used];
+        System.arraycopy(data, 0, newArray, 0, used);
+        if (close) {
+            this.data = null;
+        }
+        return newArray;
+    }
+
+    public int[] array() {
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append('[');
+        for (int i = 0; i < used; i++) {
+            if (i != 0) {
+                buf.append(", ");
+            }
+            buf.append(data[i]);
+        }
+        buf.append(']');
+        return buf.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-hivemall/blob/8dc3a024/core/src/main/java/hivemall/utils/collections/lists/LongArrayList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/hivemall/utils/collections/lists/LongArrayList.java b/core/src/main/java/hivemall/utils/collections/lists/LongArrayList.java
new file mode 100644
index 0000000..0786872
--- /dev/null
+++ b/core/src/main/java/hivemall/utils/collections/lists/LongArrayList.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package hivemall.utils.collections.lists;
+
+import java.io.Serializable;
+
+import javax.annotation.Nonnull;
+
+public final class LongArrayList implements Serializable {
+    private static final long serialVersionUID = 6928415231676568533L;
+
+    public static final int DEFAULT_CAPACITY = 12;
+
+    /** array entity */
+    private long[] data;
+    private int used;
+
+    public LongArrayList() {
+        this(DEFAULT_CAPACITY);
+    }
+
+    public LongArrayList(int size) {
+        this.data = new long[size];
+        this.used = 0;
+    }
+
+    public LongArrayList(@Nonnull long[] initValues) {
+        this.data = initValues;
+        this.used = initValues.length;
+    }
+
+    @Nonnull
+    public LongArrayList add(final long value) {
+        if (used >= data.length) {
+            expand(used + 1);
+        }
+        data[used++] = value;
+        return this;
+    }
+
+    @Nonnull
+    public LongArrayList add(@Nonnull final long[] values) {
+        final int needs = used + values.length;
+        if (needs >= data.length) {
+            expand(needs);
+        }
+        System.arraycopy(values, 0, data, used, values.length);
+        this.used = needs;
+        return this;
+    }
+
+    /**
+     * dynamic expansion.
+     */
+    private void expand(final int max) {
+        while (data.length < max) {
+            final int len = data.length;
+            long[] newArray = new long[len * 2];
+            System.arraycopy(data, 0, newArray, 0, len);
+            this.data = newArray;
+        }
+    }
+
+    public long remove() {
+        return data[--used];
+    }
+
+    public long remove(final int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        final long ret;
+        if (index == used) {
+            ret = data[index];
+            --used;
+        } else { // index < used
+            ret = data[index];
+            System.arraycopy(data, index + 1, data, index, used - index - 1);
+            --used;
+        }
+        return ret;
+    }
+
+    public void set(final int index, final long value) {
+        if (index > used) {
+            throw new IllegalArgumentException("Index MUST be less than \"size()\".");
+        } else if (index == used) {
+            ++used;
+        }
+        data[index] = value;
+    }
+
+    public long get(final int index) {
+        if (index >= used) {
+            throw new IndexOutOfBoundsException();
+        }
+        return data[index];
+    }
+
+    public long fastGet(final int index) {
+        return data[index];
+    }
+
+    public int size() {
+        return used;
+    }
+
+    public boolean isEmpty() {
+        return used == 0;
+    }
+
+    public void clear() {
+        this.used = 0;
+    }
+
+    @Nonnull
+    public long[] toArray() {
+        return toArray(false);
+    }
+
+    @Nonnull
+    public long[] toArray(boolean close) {
+        final long[] newArray = new long[used];
+        System.arraycopy(data, 0, newArray, 0, used);
+        if (close) {
+            this.data = null;
+        }
+        return newArray;
+    }
+
+    @Nonnull
+    public long[] array() {
+        return data;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append('[');
+        for (int i = 0; i < used; i++) {
+            if (i != 0) {
+                buf.append(", ");
+            }
+            buf.append(data[i]);
+        }
+        buf.append(']');
+        return buf.toString();
+    }
+}