您的位置:首页 > 编程语言 > Java开发

Java 集合

2016-02-07 10:52 567 查看

Java 集合

标签 : Java基础

集合/容器

Java集合由
Collection
Map
两个接口派生而出,
Collection
代表序列式容器,
Map
代表关联式容器.



Collection

Collection
作为
List
Queue
Set
等序列式容器的父接口, 提供了一些公共基础方法:

update相关方法:

boolean add(E e)


boolean    addAll(Collection<? extends E> c)


void clear()


boolean    remove(Object o)


boolean    removeAll(Collection<?> c)


boolean    retainAll(Collection<?> c)
(取交集)

select相关方法

boolean contains(Object o)


boolean containsAll(Collection<?> c)


Iterator<E>    iterator()


Object[]   toArray()


<T> T[]    toArray(T[] a)


boolean    isEmpty()


int    size()


详细可参考JDK文档

Iterator

iterator()
方法返回一个迭代器
Iterator
.与其他容器主要用于存储数据不同,
Iterator
主要用于遍历容器.

Iterator
隐藏了各类容器的底层实现细节,向应用程序提供了一个遍历容器的统一接口:

方法释义
boolean hasNext()
Returns true if the iteration has more elements.
E next()
Returns the next element in the iteration.
void remove()
Removes from the underlying collection the last element returned by this iterator (optional operation).
注意: 当遍历
Collection
时不要使用
Collection
自带的
remove
方法删除数据,确实需要删除时,需要使用
Iterator
提供的
remove
.

/**
* @author jifang
* @since 16/1/25 上午9:59.
*/
public class RemoveClient {

Collection<Integer> collection = new ArrayList<>();

@Before
public void setUp() {
Random random = new Random();
for (int i = 0; i < 10; ++i) {
collection.add(random.nextInt(i + 1));
}
}

@Test
public void client() {
System.out.print("before:");
for (Iterator<Integer> iterator = collection.iterator(); iterator.hasNext(); ) {
Integer integer = iterator.next();
System.out.printf(" %d", integer);
if (integer == 0) {
//collection.remove(i);
iterator.remove();
}
}
System.out.printf("%n after:");
for (Integer integer : collection) {
System.out.printf(" %d", integer);
}
}
}


Java 1.5提供
foreach
循环使得代码更加简洁,但实际
foreach
迭代容器元素底层也是用的
Iterator
,这一点可以在调试时看得很清楚.

List

List
代表有序/可重复集合,因此
List
Collection
的基础上添加了根据索引来操作元素的方法:

方法描述
void add(int index, E element)
Inserts the specified element at the specified position in this list (optional operation).
E    get(int index)
Returns the element at the specified position in this list.
int  indexOf(Object o)
Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.
int  lastIndexOf(Object o)
Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.
E    remove(int index)
Removes the element at the specified position in this list (optional operation).
E    set(int index, E element)
Replaces the element at the specified position in this list with the specified element (optional operation).
List<E>  subList(int fromIndex, int toIndex)
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
List
判断两个元素是否相等是通过
equals()
方法.

public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}


ListIterator

List
增加了返回
ListIterator
的方法:

方法描述
ListIterator<E>  listIterator()
Returns a list iterator over the elements in this list (in proper sequence).
ListIterator<E>  listIterator(int index)
Returns a list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.
ListIterator
继承自
Iterator
,专门用于操作
List
, 在
Iterator
的基础上增加了如下方法:

方法描述
void add(E e)
Inserts the specified element into the list (optional operation).
void set(E e)
Replaces the last element returned by next() or previous() with the specified element (optional operation).
boolean  hasPrevious()
Returns true if this list iterator has more elements when traversing the list in the reverse direction.
E    previous()
Returns the previous element in the list and moves the cursor position backwards.
int  previousIndex()
Returns the index of the element that would be returned by a subsequent call to previous().
int  nextIndex()
Returns the index of the element that would be returned by a subsequent call to next().
Iterator
相比增加了前向迭代 获取迭代元素index 以及add set的功能.

ArrayList

ArrayList
List
基于数组的实现,它封装了一个动态自增长/允许再分配的
Object[]
数组:

/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;


ArrayList
可以使用
initialCapacity
参数来设置该数组的初始长度
ArrayList(int initialCapacity)
,或者使用默认长度
DEFAULT_CAPACITY = 10
; 当添加元素超过
elementData
数组容量时,
ArrayList
会重新分配数组, 以容纳新元素:

/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1);  // Increments modCount!!
elementData[size++] = e;
return true;
}


private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}

ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;

// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}


如果在创建时就知道
ArrayList
的容量,最好同时指定
initialCapacity
的大小,以避免重新分配数组,耗费性能.

ArrayList
还提供如下方法来调整
initialCapacity
大小:

方法描述
void ensureCapacity(int minCapacity)
Increases the capacity of this ArrayList instance, if necessary, to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
void trimToSize()
Trims the capacity of this ArrayList instance to be the list’s current size.
工具类
Arrays
还提供了一个
static
方法
List<T> asList(T... a)
, 该方法可以把数组N个对象转换成一个
List
集合, 这个
List
集合并不是普通的
ArrayList
,而是
Arrays
内部实现的一个
Arrays.ArrayList
(一个固定长度的
List
,不可对该集合做add/remove操作).

关于
ArrayList
实现原理还可以参考ArrayList源码解析

LinkedList

LinkedList
是基于双向链表实现的List,虽然可以根据索引来访问集合中的元素,但性能不高(平均时间复杂度为
O(N)
),但其插入/删除操作非常迅速(尤其是在头尾,平均时间复杂度为
O(1)
);除此之外,
LinkedList
还实现了
Deque
接口,因此还可以当成[双端]队列/栈来使用.

关于
LinkedList
的实现原理还可以参考 [1.LinkedList源码解析, 2. 双向循环链表的设计与实现]

/**
* @author jifang
* @since 16/1/23 下午9:07.
*/
public class ListClient {

private Random random = new Random();

@Test
public void client() {
List<Integer> list = new LinkedList<>();
for (int i = 0; i < 10; ++i) {
list.add(random.nextInt(i + 1));
}

for (ListIterator<Integer> i = list.listIterator(); i.hasNext(); ) {
if (i.next() == 0) {
i.set(188);
i.add(-1);
}
}

System.out.println(list);
}
}


Queue

Queue
用于模拟队列,队列是一种先进先出/FIFO容器,新元素插到队尾(
offer
), 访问操作会返回队首元素(
poll
). 通常, 队列不允许随机访问队列中的元素:

方法描述
boolean  add(E e)
Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available.
boolean  offer(E e)
Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions.
E    element()
Retrieves, but does not remove, the head of this queue.
E    peek()
Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.
E    poll()
Retrieves and removes the head of this queue, or returns null if this queue is empty.
E    remove()
Retrieves and removes the head of this queue.
Queue
有一个
PriorityQueue
实现类,另外
Queue
还有一个
Deque
子接口,代表可以从两端存取数据的队列(因此
Deque
可以当成
Stack
使用),Java为
Deque
提供了
ArrayDeque
LinkedList
两个实现类.

PriorityQueue

PriorityQueue
并不是按照插入队列顺序进行排序,而是按照队列元素的大小(权重)进行排序, 因此
element/peek/poll/remove
返回的并不是最早进入队列的元素,而是队列中[权重]最小的元素:

/**
* @author jifang
* @since 16/1/28 下午6:20.
*/
public class QueueClient {

@Test
public void clientPriorityQueue() {

Random random = new Random();

Queue<Integer> queue = new PriorityQueue<>();
for (int i = 0; i < 10; ++i) {
queue.add(random.nextInt(100));
}

// 无序
System.out.print("iterator:");
for (Integer i : queue) {
System.out.printf(" %d", i);
}
System.out.println();

// 有序
System.out.print("pool:");
while (!queue.isEmpty()) {
System.out.printf(" %d", queue.poll());
}
System.out.println();
}
}


可以看到遍历
PriorityQueue
得到的并不是有序序列, 因为
PriorityQueue
内部并不是一个按照顺序排序的数组, 而是一个二叉堆(详细可以参考[1. PriorityQueue源码解析, 2. 堆与堆排序 ]).

由于需要排序,
PriorityQueue
不允许插入
null
;

PriorityQueue
的元素有两种排序方式

自然排序: 采用自然排序的元素必须实现了
Comparable
接口.

定制排序: 创建
PriorityQueue
时,传入一个
Comparator
实例,该对象负责对元素进行排序,采用定制排序时不要求队列元素实现
Comparable
接口.

关于两种排序的详细内容可以参考下面关于
TreeMap
的讨论.

Deque-ArrayDeque

Deque
接口代表一个双端队列,提供了如下方法从队列的两端存取数据:



Java为
Deque
提供了两个实现类
ArrayDeque
(基于数组)与
LinkedList
(基于链表);由于
ArrayDeque
底层基于数组
E[] elements
实现,因此创建时可以指定一个
numElements
参数设置
elements
数组初始长度,如果不指定
numElements
参数,默认数组长度为
16
(关于
ArrayDeque
的实现原理可参考ArrayDeque源码解析).

Deque
还可以作为栈
stack
使用, 他提供了如下方法:



@Test
public void asStack() {
Deque<Integer> stack = new ArrayDeque<>();

for (int i = 0; i < 10; ++i) {
stack.push(i);
}

while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}


此外,
LinkedList
也实现了
Deque
接口,因此也可以作为
Queue
/
Deque
的实现类.

Map

Map
用于保存具有映射关系的
key-value
数据,
key
value
之间存在单向一对一关系,通过指定的
key
,总能找到唯一确定的
value
.

update相关

V  put(K key, V value)


void   putAll(Map<? extends K,? extends V> m)


V  remove(Object key)


void   clear()


select相关

V  get(Object key)


Set<K> keySet()


Collection<V>  values()


Set<Map.Entry<K,V>>    entrySet()


boolean    containsKey(Object key)


boolean    containsValue(Object value)


boolean    isEmpty()


int    size()


Map
内部定义一个
Map.Entry<K,V>
接口,封装
key-value
对,
Entry
提供如下方法:

方法描述
K getKey()
Returns the key corresponding to this entry.
V getValue()
Returns the value corresponding to this entry.
V setValue(V value)
Replaces the value corresponding to this entry with the specified value (optional operation).

HashMap

HashMap
是基于
hash
算法的
Map
实现(用它代替
Hashtable
),针对
key-value
的插入/检索,这种形式具有最稳定的性能(
O(1)
),还可通过构造器对这一性能进行调整.

为了成功在
HashMap
中存取数据,
key
对象必须实现
hashCode()
equals()
方法,
HashMap
先通过
key
hashCode()
定位到元素所在桶,如果两个元素在同一个桶,再用
equals()
进行判断是否相等.如果两个对象的
hashCode()
相同,但
equals()
不同, 则将两个对象放在同一个桶的不同链表位置(这样会导致
hash
效率下降).如果两个对象通过
equals()
返回
true
, 但这
hashCode()
不同,则非常有可能导致
HashMap
将这两个对象分配在不同桶中,从而使这两个对象都添加成功,这就与
Map
规则冲突了.(关于
HashMap
详细原理可以参考: [1. 哈希表的设计与实现, 2.HashMap源码解析]).

建议: 如果两个对象通过
equals()
方法比较返回
true
, 则两个对象的
hashCode()
值也相同.

hashCode()
重写规则:

运行过程中, 同一个对象多次调用
hashCode()
应具有相同返回值;

当两个对象通过
equals()
比较返回
true
时,
hashCode()
应具有相同返回值;

对象中用作
equals()
比较标准的实例变量, 都应该用于计算
hashCode()
.

hashCode()
重写方法:
将每个有意义的实例变量都计算出一个
int
hashcode
值.

类型计算方式
boolean
hashCode = (true ? 1 : 0);
float
hashCode = Float.floatToIntBits(f);
double
long value = Double.doubleToLongBits(f);
hashCode = (int)(value^(value>>>32));
int
/
short
/
byte
hashCode = (int)i;
long
hashCode = (int)(l^(l>>>32));
引用类型
hashCode = object.hashCode();
用上面计算出来的多个
hashcode
组合计算成一个最终的
hashcode
,为了避免直接相加产生偶然相等,可以为各个
hashcode
乘以任意一个质数再相加:

String
实现

public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;

for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}


String
hashCode()
方法做了一些优化, 叫闪存散列码, 详见数据结构与算法分析 : Java语言描述

自定义
Bean


/**
* @author jifang
* @since 16/1/13下午7:50.
*/
public class Bean implements Serializable {

private static final long serialVersionUID = 2975296536292876992L;

private boolean isUsed;

private double rate;

private String name;

@Override
public int hashCode() {
long rateHash = Double.doubleToLongBits(rate);
int isUsedHash = isUsed ? 1 : 0;
int nameHash = name.hashCode();

return nameHash * 11 + (int) (rateHash ^ (rateHash >>> 32)) * 13 + isUsedHash;
}

// ..

}


HashMap
的主要实现逻辑:

/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
*         <tt>null</tt> if there was no mapping for <tt>key</tt>.
*         (A <tt>null</tt> return can also indicate that the map
*         previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}

modCount++;
addEntry(hash, key, value, i);
return null;
}


/**
* Adds a new entry with the specified key, value and hash code to
* the specified bucket.  It is the responsibility of this
* method to resize the table if appropriate.
*
* Subclass overrides this to alter the behavior of put method.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}

createEntry(hash, key, value, bucketIndex);
}

/**
* Like addEntry except that this version is used when creating entries
* as part of Map construction or "pseudo-construction" (cloning,
* deserialization).  This version needn't worry about resizing the table.
*
* Subclass overrides this to alter the behavior of HashMap(Map),
* clone, and readObject.
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}


注意

当向
Map
类容器(如
HashMap
TreeMap
或后面的
HashSet
TreeSet
)中添加可变对象时,必须十分小心,如果修改
Map
中的
key
,有可能导致该
key
与集合中的其他
key
相等,从而导致无法准确访问该
key-value
.因此尽量不要使用可变对象作为
Map
key
,或不要修改作为
key
的对象(
Set
value
于此类同)

Map
还支持
containsValue()
方法来判断一个
value
是否存在于
Map
中, 但该方法会遍历所有的桶查找这个值, 因此性能较差, 不推荐使用

public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();

Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}

/**
* Special-case code for containsValue with null argument
*/
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}


LinkedHashMap

LinkedHashMap
使用双向链表来维护
key-value
插入顺序,因此性能略低于
HashMap
,但在需要顺序迭代
Map
的场景下会有非常好的效率.

LinkedHashMap
提供的
addEntry()
方法与
HashMap
有所不同,当使用
LinkedHashMap
put()
时, 会从
HashMap
调回到
LinkedHashMap
addEntry()
方法,将新元素添加到链表尾:

/**
* This override alters behavior of superclass put method. It causes newly
* allocated entry to get inserted at the end of the linked list and
* removes the eldest entry if appropriate.
*/
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);

// Remove eldest entry if instructed
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
}
}

/**
* This override differs from addEntry in that it doesn't resize the
* table or remove the eldest entry.
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);
size++;
}


使用
LinkedHashMap
统计word出现次数

/**
* @author jifang
* @since 16/1/28 上午10:33.
*/
public class MapClient {

private Random random = new Random();

@Test
public void clientLinkedHashMap() {
Map<String, Integer> map = new LinkedHashMap<>();
System.out.print("insert key:");
for (int i = 0; i < 20; ++i) {
String key = String.valueOf(random.nextInt(10));
System.out.printf(" %s", key);
if (map.get(key) == null) {
map.put(key, 1);
} else {
map.put(key, map.get(key) + 1);
}
}
System.out.printf("%n iterator:");

for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.printf(" <%s -> %s>", entry.getKey(), entry.getValue());
}

}
}


WeakHashMap

WeakHashMap
HashMap
的区别在于:
HashMap
key
保留了对实际对象的强引用, 这意味着只要该
HashMap
不被销毁,则
Map
的所有
key
所引用的对象不会被垃圾回收;但
WeakHashMap
key
只保留对实际对象的弱引用, 这意味着如果该
key
所引用的对象没有被其他强引用变量引用,则该对象可能被垃圾回收,
WeakHashMap
也会自动删除这些
key
对应的
key-value
对.

@Test
public void clientWeakHashMap() {
Map<String, String> map = new WeakHashMap<>();
String key = "key";
map.put(key, "value");
map.put(new String("key1"), "value");
map.put(new String("key2"), "value");
System.out.printf("Before : %d%n", map.size());

System.gc();
System.runFinalization();
System.out.printf("After : %d ", map.size());
}


如果使用
WeakHashMap
来保留对象的弱引用,则不要让该
key
所引用的对象具有任何强引用, 否则将失去使用
WeakHashMap
的意义.

IdentityHashMap

HashMap
不同,
IdentityHashMap
判断元素是否相等的标准是用
==
而不是
equals()
;

public boolean containsKey(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return true;
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}


SortedMap-TreeMap

Map
接口派生出
SortedMap
接口代表根据
key
排序的
key-value
集合,
TreeMap
作为
SortedMap
的实现类是一个红黑树结构,每个
key-value
作为红黑树的一个节点.
TreeMap
存储
key-value
时,根据
key
值进行排序.因此
TreeMap
可以保证所有元素都处于有序状态,因此
SortedMap
Map
的基础上又添加了如下方法:

方法描述
Comparator<? super K> comparator()
Returns the comparator used to order the keys in this map, or null if this map uses the natural ordering of its keys.
K    firstKey()
Returns the first (lowest) key currently in this map.
K    lastKey()
Returns the last (highest) key currently in this map.
SortedMap<K,V>   headMap(K toKey)
Returns a view of the portion of this map whose keys are strictly less than toKey.
SortedMap<K,V>   tailMap(K fromKey)
Returns a view of the portion of this map whose keys are greater than or equal to fromKey.
SortedMap<K,V>   subMap(K fromKey, K toKey)
Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive.
TreeMap
又在
SortedMap
的基础上扩展了如下方法:

方法描述
Map.Entry<K,V>   ceilingEntry(K key)
Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
K    ceilingKey(K key)
Returns the least key greater than or equal to the given key, or null if there is no such key.
Map.Entry<K,V>   floorEntry(K key)
Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key.
K    floorKey(K key)
Returns the greatest key less than or equal to the given key, or null if there is no such key.
Map.Entry<K,V>   higherEntry(K key)
Returns a key-value mapping associated with the least key strictly greater than the given key, or null if there is no such key.
K    higherKey(K key)
Returns the least key strictly greater than the given key, or null if there is no such key.
Map.Entry<K,V>   lowerEntry(K key)
Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.
K    lowerKey(K key)
Returns the greatest key strictly less than the given key, or null if there is no such key.
Map.Entry<K,V>   pollFirstEntry()
Removes and returns a key-value mapping associated with the least key in this map, or null if the map is empty.
Map.Entry<K,V>   pollLastEntry()
Removes and returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
Map.Entry<K,V>    firstEntry()
Returns a key-value mapping associated with the least key in this map, or null if the map is empty.
Map.Entry<K,V>   lastEntry()
Returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
TreeMap
有两种排序方式:

自然排序

TreeMap
的所有
key
必须实现
Comparable
接口,
TreeMap
会调用
key
int compareTo(T o);
方法来比较元素的大小,然后将集合元素升序排列.

/**
* Compares two keys using the correct comparison method for this TreeMap.
*/
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}


Java提供的
java.lang.Comparable
接口仅包含一个
int compareTo(T o);
方法.大部分常用类都实现了该接口(如
String
,
Integer
等).

public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;

int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}


HashMap
不同,
TreeMap
判断两个
key
是否相等的唯一标准是:通过
compareTo
方法比较返回值是否为0.

public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}

final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}


实现
Comparable


/**
* @author jifang
* @since 16/1/13下午7:50.
*/
public class Bean implements Comparable<Bean> {

private boolean isUsed;

private double rate;

private String name;

public Bean(boolean isUsed, double rate, String name) {
this.isUsed = isUsed;
this.rate = rate;
this.name = name;
}

@Override
public int compareTo(Bean anotherBean) {
double another = (anotherBean.isUsed ? 1 : 0) +
anotherBean.rate + anotherBean.name.length();
double self = (isUsed ? 1 : 0) + rate + name.length();
return (int) (self - another);
}

@Override
public String toString() {
return "Bean{" +
"isUsed=" + isUsed +
", rate=" + rate +
", name='" + name + '\'' +
'}';
}
}


@Test
public void clientSortedMap() {
// value作为期望的order
SortedMap<Bean, Integer> map = new TreeMap<>();
map.put(new Bean(true, 3.14, "true"), 1);
// 该对象与上面的bean compare会返回0
map.put(new Bean(false, 3.14, "false"), 1);
map.put(new Bean(true, 3.14, "false"), 2);
map.put(new Bean(false, 3.14, "true"), 0);
System.out.println(map);

Bean firstKey = map.firstKey();
System.out.printf("first: %s -> %d%n", firstKey, map.get(firstKey));
Bean lastKey = map.lastKey();
System.out.printf("last: %s -> %d%n", lastKey, map.get(lastKey));

map.remove(firstKey);
Map.Entry<Bean, Integer> firstEntry = ((TreeMap<Bean, Integer>) map).firstEntry();
System.out.printf("A first: %s -> %d%n", firstEntry.getKey(), firstEntry.getValue());
}


当执行了
remove
方法后,
TreeMap
会对集合中的元素重新索引, 这一点可以在调试时看到.

自定义排序

TreeMap
默认的是使用升序排序,如果需要自定义排序规则,需要为其传入一个
Comparator
实例, 采用定制排序时不要求
key
实现
Comparable
.

public class MapClient {

private Comparator<Bean> comparator = new Comparator<Bean>() {
@Override
public int compare(Bean o1, Bean o2) {
// 返回正数: 说明o1 > o2
// 返回负数: 说明o1 < o2
return -o1.compareTo(o2);
}
};

@Test
public void clientSortedMap() {
SortedMap<Bean, Integer> map = new TreeMap<>(comparator);
// ...
}
}


由于
TreeMap
是基于红黑树实现,因此相比
HashMap
性能要慢一点(Hash平均
O(1)
,Tree平均
O(lgN)
详细可参考[1. TreeMap源码解析, 2.红黑树的设计与实现(上)]),但其中的
key-value
已是有序状态,无需再有专门的排序操作.因此适用于
key
有序的场景.

EnumMap

EnumMap
是一个需要与枚举类一起使用的
Map
,其所有
key
都必须是单个枚举类的枚举值.
EnumMap
具有以下特征:

EnumMap
内部以数组形式存储,紧凑/高效,是
Map
所有实现中性能最好的.

EnumMap
根据
key
的自然顺序(枚举值在枚举类的定义顺序)来维护
key-value
顺序.

EnumMap
不允许
key
null
, 但允许使用
null
作为
value
.

/**
* @author jifang
* @since 16/1/27 下午4:01.
*/
public enum ShopListType {

BLACK_LIST(0, "黑名单"),
WHITE_LIST(1, "白名单"),
INVITE_LIST(2, "邀请名单"),
RECOMMEND_WHITE_LIST(3, "推荐白名单"),
RECOMMEND_BLACK_LIST(4, "推荐黑名单");

private int type;

private String description;

ShopListType(int type, String description) {
this.type = type;
this.description = description;
}

public int getValue() {
return type;
}

public String getDescription() {
return description;
}
}


@Test
public void clientEnumMap() {
EnumMap<ShopListType, String> map = new EnumMap<>(ShopListType.class);
map.put(ShopListType.BLACK_LIST, "黑名单");
map.put(ShopListType.WHITE_LIST, "白名单");
System.out.println(map);
}


Set

Set
Map
关系非常密切, 虽然
Map
中存放的是
key-value
,
Set
中存放的是单个对象, 但从JDK源代码看, Java是先实现了
Map
,然后包装一个空
Object
来填充所有的
value
来实现的
Set
.

private transient HashMap<E,Object> map;

// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();


public boolean add(E e) {
return map.put(e, PRESENT)==null;
}


Set
继承自
Collection
, 没有提供额外的方法;

HashSet

HashSet
Set
接口的典型实现,是
Set
中用的最多的实现.由于
HashSet
是基于
HashMap
实现的,因此具有如下特点:

不保证元素的排列顺序;

不能同步,如果有多个线程同步访问/修改
HashSet
, 需要开发人员自己保证同步;

集合元素值可以为
null
;

LinkedHashSet

由于
LinkedHashSet
底层是基于
LinkedHashMap
实现,因此
Set
可以记录元素的插入顺序,当遍历
LinkedHashSet
时,将会按照元素的添加顺序来访问集合中的元素:

/**
* @author jifang
* @since 16/1/26 下午2:09.
*/
public class SetClient {

@Test
public void clientLinkedHashSet() {
Set<Integer> set = new LinkedHashSet<>();
for (int i = 0; i < 10; ++i) {
set.add(i);
}
for (int i = 19; i >= 10; --i) {
set.add(i);
}
System.out.println(set);
}
}


LinkedHashSet
的优缺点与
LinkedHashMap
类似.

SortedSet-TreeSet

SortedSet
接口继承自
Set
,Java为
SortedSet
提供了
TreeSet
实现,由于
SortedSet
可以确保集合元素可以处于已排序状态, 因此在
Set
的基础上又提供了如下方法:

类型计算方式
Comparator<? super E>    comparator()
Returns the comparator used to order the elements in this set, or null if this set uses the natural ordering of its elements.
E    first()
Returns the first (lowest) element currently in this set.
E    last()
Returns the last (highest) element currently in this set.
SortedSet<E> tailSet(E fromElement)
Returns a view of the portion of this set whose elements are greater than or equal to fromElement.
SortedSet<E> headSet(E toElement)
Returns a view of the portion of this set whose elements are strictly less than toElement.
SortedSet<E> subSet(E fromElement, E toElement)
Returns a view of the portion of this set whose elements range from fromElement, inclusive, to toElement, exclusive.
TreeSet
相比于
SortedSet
还提供了如下实用方法:

类型计算方式
E    ceiling(E e)
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
E    floor(E e)
Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.
Iterator<E>  descendingIterator()
Returns an iterator over the elements in this set in descending order.
NavigableSet<E>  descendingSet()
Returns a reverse order view of the elements contained in this set.
E    higher(E e)
Returns the least element in this set strictly greater than the given element, or null if there is no such element.
E    lower(E e)
Returns the greatest element in this set strictly less than the given element, or null if there is no such element.
E    pollFirst()
Retrieves and removes the first (lowest) element, or returns null if this set is empty.
E    pollLast()
Retrieves and removes the last (highest) element, or returns null if this set is empty.
由于
TreeSet
底层采用
TreeMap
实现, 因此其性能特点以及排序规则可以参考
TreeMap
.

EnumSet

EnumSet
是专门为枚举设计的
Set
,所有的元素必须是单一枚举类的枚举值.
EnumSet
也是有序的,以枚举值在
Enum
类内定义的顺序来排序;由于
EnumSet
没有暴露任何构造器,因此需要通过他提供的如下
static
方法来创建
EnumSet
实例:

allOf(Class<E> elementType)


complementOf(EnumSet<E> s)


copyOf(Collection<E> c)


noneOf(Class<E> elementType)


of(E first, E... rest)


range(E from, E to)


@Test
public void clientEnumSet() {
EnumSet<ShopListType> set1 = EnumSet.allOf(ShopListType.class);
System.out.println(set1);

EnumSet<ShopListType> set2 = EnumSet.noneOf(ShopListType.class);
System.out.println(set2);
set2.add(ShopListType.BLACK_LIST);

System.out.println(set2);
}


EnumSet
的内部以位向量的形式存储,紧凑/高效,因此
EnumSet
占用内存小,运行效率高,是
Set
实现类中性能最好的. 尤其是批量操作(
containsAll()
,
retainAll()
)时,如果参数也是
EnumSet
, 则执行效率非常快(详细可参考Java EnumSet工作原理初窥).

Collections

Java提供了一个操作
List
Map
Set
等集合的工具类
Collections
, 其提供了大量的工具方法对集合元素进行排序 查找 更新等操作:

排序相关

sort(List<T> list)
sort(List<T> list, Comparator<? super T> c)
shuffle(List<?> list)
swap(List<?> list, int i, int j)
reverse(List<?> list)
reverseOrder(Comparator<T> cmp)
rotate(List<?> list, int distance)


查找相关

binarySearch(List<? extends Comparable<? super T>> list, T key)
binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
indexOfSubList(List<?> source, List<?> target)
lastIndexOfSubList(List<?> source, List<?> target)


max(Collection<? extends T> coll)


max(Collection<? extends T> coll, Comparator<? super T> comp)


min(Collection<? extends T> coll)


min(Collection<? extends T> coll, Comparator<? super T> comp)


更新相关

addAll(Collection<? super T> c, T... elements)


fill(List<? super T> list, T obj)


nCopies(int n, T o)


不可变集合视图

unmodifiableCollection(Collection<? extends T> c)


unmodifiableList(List<? extends T> list)


unmodifiableMap(Map<? extends K,? extends V> m)


unmodifiableSet(Set<? extends T> s)


unmodifiableSortedMap(SortedMap<K,? extends V> m)


unmodifiableSortedSet(SortedSet<T> s)


单元素集合

Set<T> singleton(T o)


singletonList(T o)


singletonMap(K key, V value)


空集合

emptyList()


emptyMap()


emptySet()


Collections
提供了三个静态变量来代表一个空集合

static List    EMPTY_LIST


static Map EMPTY_MAP


static Set EMPTY_SET


同步集合

详见Java 并发基础

遗留的集合

Java还提供了一些集合工具:
Hashtable
Vactor
Stack
Enumeration
StringTokenizer
(
Enumeration
的一个实现类,其功能类似于
String
split()
,但不支持正则,实现将字符串进行分割, 然后迭代取出), 这些集合工具都是从Java 1.0开始就存在的, 但其实现要么性能较低(需要保持线程同步), 要么方法名繁琐(如
hasMoreElements()
), 现在已经很少使用,而且其使用方法也与前面的集合类似, 因此在此就不做过多介绍了. 如果在实际开发中会遇到还在使用这些工具的代码(比如Dom4j),可以参考JDK文档.

Properties

Properties
Hashtable
的子类,他可以把
Map
和属性文件关联起来,从而可以把
Map
对象中的
key-value
写入属性文件, 也可以将属性文件中的”属性名=属性值”加载到
Map
中,由于属性文件中的属性名/属性值都是
String
,因此
Properties
key-value
都只能是
String
.
Properties
提供了如下方法来读写内存中的
key-value
.

方法描述
String getProperty(String key)
Searches for the property with the specified key in this property list.
String getProperty(String key, String defaultValue)
Searches for the property with the specified key in this property list.
Object setProperty(String key, String value)
Calls the Hashtable method put.
Enumeration<?>   propertyNames()
Returns an enumeration of all the keys in this property list, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list.
Set<String>  stringPropertyNames()
Returns a set of keys in this property list where the key and its corresponding value are strings, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list.
Properties
还提供了读写属性文件的方法:

方法描述
void list(PrintStream/PrintWriter out)
Prints this property list out to the specified output stream/writer.
void load(InputStream/Reader in)
Reads a property list (key and element pairs) from the input byte/character stream.
void store(OutputStream/Write out, String comments)
Writes this property list (key and element pairs) in this Properties table to the output stream/write in a format suitable for loading into a Properties table using the load(InputStream/Reader) method.
common.properties

dubbo.version=1.0.0

## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://192.168.9.166:3306/common
mysql.user=admin
mysql.password=admin


client

@Test
public void clientProperties() throws IOException {
Properties properties = new Properties();
properties.load(ClassLoader.getSystemResourceAsStream("common.properties"));
System.out.println(properties.get("mysql.driver.class"));
properties.put("mysql.user", "root");
properties.put("mysql.password", "root");
properties.store(new FileOutputStream("common.properties"), "comment");
}


Properties
还可以从XML中加载
key-value
,也可以以XML形式保存,其用法与普通
.properties
文件类似.

参考:
给jdk写注释系列之jdk1.6容器

grepcode.com

数据结构与STL系列博客

oracle.javase.docs.api

Java编程思想

疯狂Java讲义

Google Guava官方教程

数据结构与算法分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息