文章基于JDK1.8 , MacBookPro CPU 2.9GHz 双核Intel Core i5
List接口
List类族关系,3中主要实现:ArrayList、LinkedList、Vector
ArrayList和Vector内部使用数组实现,唯一区别是对多线程的支持。ArrayList没有对方法做同步,不是线程安全的,Vector中绝大部分方法都做了线程同步,是一种线程安全的实现。
LinkedList使用了循环双向链表数据结构。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
……
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
ArrayList和LinkedList对比
增加元素到列表尾部
ArrayList.add(element)源码
/**
* 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;
}
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_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对容量的需求超过当前数组大小时,需要进行扩容。扩容时,会进行大量的数组复制操作。
LinkedList.add(element)源码
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
LinkedList使用了链表数据结构,不需要维护容量的大小。从这点上来说,比ArrayList有一定的优势,但是每次元素增加需要新建一个Node对象。
性能对比
-Xms500M -Xmx500M
public static void main(String[] args) throws Exception{
testArrAdd(); //耗时287ms
// testLinkAdd(); //耗时616ms
}
static void testArrAdd(){
List arrList = new ArrayList();
Object obj = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
arrList.add(obj);
}
System.out.println("ArrayList add, cost time:" + (System.currentTimeMillis() - start));
}
static void testLinkAdd(){
List linkList = new LinkedList();
Object obj = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
linkList.add(obj);
}
System.out.println("LinkedList add, cost time:" + (System.currentTimeMillis() - start));
}
增加元素到列表任意位置
ArrayList是基于数组实现的,数组是一块连续的内存空间,如果在数组任意位置插入元素,必然导致在该位置后的所有元素都需要重新排列,因此效率会相对较低。
ArrayList.add(index,element)源码
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
每次插入操作,都会进行一次数组复制。插入的位置在List中的位置越靠前,数组重组的开销也越大。
LinkedList.add(index,element)源码
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
对于LinkedList来说,在List尾端插入数据何在任意位置插入数据是一样的。
性能对比
public static void main(String[] args) throws Exception{
// testArrAdd(); //耗时785ms
testLinkAdd(); //耗时10ms
}
static void testArrAdd(){
List arrList = new ArrayList();
Object obj = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
arrList.add(0,obj);
}
System.out.println("ArrayList add, cost time:" + (System.currentTimeMillis() - start));
}
static void testLinkAdd(){
List linkList = new LinkedList();
Object obj = new Object();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
linkList.add(0, obj);
}
System.out.println("LinkedList add, cost time:" + (System.currentTimeMillis() - start));
}
删除任意位置元素
ArrayList.remove(index)源码
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
在ArrayList每一次删除操作后,都需要进行数组的重组。删除的元素位置越靠前,数组重组的开销也越大。
LinkedList.remove(index)源码
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
/**
* Unlinks non-null node x.
*/
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
在LinkedList的实现中,首先通过循环找到要删除的元素。如果要删除的位置处于List的前半段,则从前往后找,若其位置处于后半段,则从后往前找。删除靠前或者靠后比较高效,但是移除List中间元素几乎要遍历半个List。
性能对比
//头部删除
while (list.size() > 0){
list.remove(0);
}
//中间删除
while (list.size() > 0){
arrList.remove(list.size()>>1);
}
//尾部删除
while (list.size() > 0){
arrList.remove(list.size()-1);
}
List类型/删除位置 | 头部 | 中间 | 尾部 |
---|---|---|---|
ArrayList | 805 | 414 | 5 |
LinkedList | 7 | 4419 | 6 |
initialCapacity
ArrayList和Vector基于数组的初始化大小。默认大小10,扩容时将数组大小设置为原大小的1.5倍。
ArrayList初始化指定数组大小:
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
在能预估ArrayList数组大小的情况下,通过指定初始容量大小避免扩容对性能有较大提升。
long start = System.currentTimeMillis();
List arrList = new ArrayList(10000000);
Object obj = new Object();
for (int i = 0; i < 10000000; i++) {
arrList.add(obj);
}
System.out.println("cost time:" + (System.currentTimeMillis() - start));
output:
指定初始大小时耗时84ms,不指定时耗时293ms
遍历列表
foreach、迭代器、for循环
List list = new LinkedList();
Object obj = new Object();
Object tmp;
for (int i = 0; i < 10000000; i++) {
list.add(obj);
}
long start = System.currentTimeMillis();
for (Object o : list) {
tmp = o;
}
System.out.println("foreach cost time:" + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
Iterator iterator = list.iterator();
while (iterator.hasNext()){
tmp = iterator.next();
}
System.out.println("iterator cost time:" + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for (int i = 0; i < list.size();i++){
tmp = list.get(i);
}
System.out.println("fori cost time:" + (System.currentTimeMillis() - start));
Lis类型/遍历类型 | foreach | 迭代器 | for循环 |
---|---|---|---|
ArrayList | 16 | 11 | 6 |
LinkedList | 58 | 54 | 很长 |
foreach循环综合性能不如迭代器。使用for循环随机访问遍历列表时,ArrayList表现很好,LinkedList长时间未响应(等待时长超过2分钟,具体耗时未测出)
编译后的foreach与iterator几无差异:
List list = new LinkedList();
Object obj = new Object();
for(int i = 0; i < 10000000; ++i) {
list.add(obj);
}
long start = System.currentTimeMillis();
Iterator iterator;
Object o;
for(iterator = list.iterator(); iterator.hasNext(); o = iterator.next()) {
}
System.out.println("foreach cost time:" + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
Object var3;
for(iterator = list.iterator(); iterator.hasNext(); var3 = iterator.next()) {
}
System.out.println("iterator cost time:" + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
for(int i = 0; i < list.size(); ++i) {
list.get(i);
}
System.out.println("fori cost time:" + (System.currentTimeMillis() - start));
Map接口
关于HashMap的原理介绍主要基于JDK1.8。
HashMap和HashTable区别
- HashTable大部分方法做了同步,而HashMap没有,HashMap不是线程安全的。
- HashTable不允许key或者value使用null值,HashMap可以
- 对key的hash算法和hash值到内存索引的映射算法不同
HashMap实现原理
将key做hash算法,然后将hash值映射到内存地址,直接取得key所对应的数据。底层数据结构使用的是数组,内存地址即数组的下标索引。,数组里的元素可能为null,也有可能是单个对象,还有可能是单向链表或是红黑树(参考下面put源码)。
HashMap的高性能需保证以下几点:
- hash算法高效
- hash值到内存地址(数组索引)的算法是快速的
- 根据内存地址(数组索引)可以直接取得对应的值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
基本常量
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
put及hash冲突处理
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
//1. 放入第一个元素table为空,触发resize方法
n = (tab = resize()).length;
/**
3. 继续放入元素
i = (n - 1) & hash;//hash是传过来的,其中n是底层数组的长度,用&运算符计算出i的值
p = tab[i];//用计算出来的i的值作为下标从数组中元素
if(p == null){//如果这个元素为null,用key,value构造一个Node对象放入数组下标为i的位置
tab[i] = newNode(hash, key, value, null);
}
*/
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
/**
4. 放入元素时产生了hash冲突
*/
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
//5. new一个新的Node对象并把当前Node的next引用指向该对象,也就是说原来该位置上只有一个元素对象,现在转成了单向链表
p.next = newNode(hash, key, value, null);
//6. 当链表长度到8时,将链表转化为红黑树来处理
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
//2. 放入第一个元素触发下面代码,初始化一个长度为16的node数组
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
容量参数
HashMap提供了两个可以指定初始化大小的参数:
public HashMap(int initialCapacity, float loadFactor);
public HashMap(int initialCapacity)
initialCapacity指定了HashMap的初始容量,loadFactor指定了其负载因子。初始容量即数组的大小。HashMap会使用大于等于initialCapacity并且是2的指数幂的最小的整数作为内置数组的大小,负载因子又叫做填充比,是介于0和1之间的浮点数,决定HashMap在扩容之前,内部数组的填充度。默认initialCapacity=16,loadFactor=0.75.
实际使用中,负载因子也可以设置大于1,此时HashMap将产生大量冲突。
HashMap内部还维护了threshold变量,始终被定义为当前数组总容量和负载因子的乘机,表示HashMap的阈值。当HashMap的实际容量超过阈值时,HashMap便会进行扩容。因此,HashMap的实际填充率不会超过负载因子。
HashMap的扩容会操作遍历整个HashMap,设置合理的初始大小和负载因子,有效减少HashMap扩容次数。
LinkedHashMap
使用LinkedHashMap,可以保存元素输入时的顺序。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
{
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
……
}
LinkedHashMap继承自HashMap,具备了HashMap的高性能,内部增加了一个链表,用于存放元素的顺序。
LinkedHashMap提供了两种类型的顺序:一是元素插入时的顺序,二是最近访问的顺序。
/**
* Constructs an empty <tt>LinkedHashMap</tt> instance with the
* specified initial capacity, load factor and ordering mode.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @param accessOrder the ordering mode - <tt>true</tt> for
* access-order, <tt>false</tt> for insertion-order
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
accessOrder为true时,按照元素最后访问时间排序;accessOrder为false时,按照插入顺序排序,默认false。
TreeMap
TreeMap实现了SortedMap接口,可以对元素进行排序。
TreeMap提供的有关排序的接口如下:
public SortedMap<K,V> subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
public SortedMap<K,V> headMap(K toKey) {
return headMap(toKey, false);
}
public SortedMap<K,V> tailMap(K fromKey) {
return tailMap(fromKey, true);
}
K firstKey();
K lastKey();
TreeMap和LinkedHashMap排序方式对比
LinkedHashMap是基于元素进入集合的顺序或者元素被访问的顺序排序;TreeMap是基于元素的固有顺序(由Comparator或Comparable决定)
确定key排序算法
- 在TreeMap的构造函数中注入一个Comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
- 使用一个实现了Comparable接口的key
要正常使用TreeMap,一定要通过上述其中一种方式将排序规则传给TreeMap,否则在put操作时,会抛出java.lang.ClassCastException
TreeMap内部实现
内部基于红黑树,红黑树是一种平衡查找树,统计性能优于平衡二叉树。具有良好的最坏运行时间,可以在O(log n)时间内做查找、插入和删除,n表示树中元素的数目。
TreeMap实例
public static void main(String[] args) throws Exception{
Map map = new TreeMap();
Student s1 = new Student("tom", 65);
Student s2 = new Student("jack", 80);
Student s3 = new Student("lucy", 60);
Student s4 = new Student("bill", 70);
map.put(s1, new StudentDetailInfo(s1));
map.put(s2, new StudentDetailInfo(s2));
map.put(s3, new StudentDetailInfo(s3));
map.put(s4, new StudentDetailInfo(s4));
//筛选出成绩介于jack和bill之间的学生
Map map1 = ((TreeMap) map).subMap(s4, s2);
for (Iterator iterator = map1.keySet().iterator();iterator.hasNext();){
Student key = (Student) iterator.next();
System.out.println(key + "->" + map1.get(key));
}
System.out.println("subMap end");
//找出成绩低于tom的学生
map1 = ((TreeMap) map).headMap(s1);
for (Iterator iterator = map1.keySet().iterator();iterator.hasNext();){
Student key = (Student) iterator.next();
System.out.println(key + "->" + map1.get(key));
}
System.out.println("headMap end");
//找出成绩大于等于bill的学生
map1 = ((TreeMap) map).tailMap(s4);
for (Iterator iterator = map1.keySet().iterator();iterator.hasNext();){
Student key = (Student) iterator.next();
System.out.println(key + "->" + map1.get(key));
}
System.out.println("tailMap end");
}
public static class Student implements Comparable<Student>{
String name;
int score;
public Student(String name, int score){
this.name = name;
this.score = score;
}
@Override
public int compareTo(Student o) {
if(o.score < this.score){
return 1;
} else if(o.score > this.score){
return -1;
}
return 0;
}
@Override
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("name:").append(name).append(" ").append("score:").append(score);
return stringBuffer.toString();
}
}
public static class StudentDetailInfo{
Student student;
public StudentDetailInfo(Student student){
this.student = student;
}
@Override
public String toString() {
return student.name + "'s detail info";
}
}
Set接口
Set的实现都是关于Map的封装。