- boolean add(E e)
- void clear()
- boolean contains(Object o)
- boolean isEmpty()
- boolean equals(Object obj)
- Iterator<E> iterator()
- boolean remove(Object o)
- boolean removeAll(Collection<?> c)
- int size()
- Object[] toArray()
- <T> T[] toArray(T[] a)
关于 set 家族,有一下描述:
- boolean add(E e) //添加一个元素到队列中,若队列已满会抛出一个IllegalStateException异常
- E element() //获取队头元素
- boolean offer(E e) //添加一个元素到队列中,若队列已满返回false
- E peek() //获取队头元素,若队列为空返回null
- E poll() //返回并移除队头元素,若队列为空返回null
- E remove() //返回并移除队头元素
add 与 offer,element 与 peek,remove 与 poll 看似是三对儿功能相同的方法。它们之间的重要区别在于前者若操作失败会抛出一个异常,后者若操作失败会从返回值体现出来(比如返回 false 或 null),我们可以根据具体需求调用它们中的前者或后者。
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.The Map
interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings. The order of a map is defined as the order in which the iterators on the map's collection views return their elements. Some map implementations, like the TreeMap
class, make specific guarantees as to their order; others, like the HashMap
class, do not.
大概意思是:一个把键映射到值的对象被称作一个 Map 对象。映射表不能包含重复的键,每个键至多可以与一个值关联。
Map 接口提供了三个集合视图(关于集合视图的概念我们下面会提到):键的集合视图、值的集合视图以及键值对的集合视图。
一个映射表的顺序取决于它的集合视图的迭代器返回元素的顺序。一些 Map 接口的具体实现(比如 TreeMap), 保证元素有一定的顺序,其它一些实现(比如 HashMap)则不保证元素在其内部有序。
Map 接口让我们能够根据键快速检索到它所关联的值。也就是利用这个特性,Struts2 框架中用 ContextMap 作为容器封装一次请求所需的所有数据。 我们先来看看 Map 接口定义了哪些方法:
- void clear()
- boolean containsKey(Object key) //判断是否包含指定键
- boolean containsValue(Object value) //判断是否包含指定值
- boolean isEmpty()
- V get(Object key) //返回指定键映射的值
- V put(K key, V value) //放入指定的键值对
- V remove(Object key)
- int size()
- Set<Map.Entry<K,V>> entrySet()
- Set<K> keySet()
- Collection<V> values()
Map 接口的具体实现类主要有:AbstractMap,EnumMap,HashMap,LinkedHashMap,TreeMap。HashTable。
HashMap<K, V> 是基于哈希表这个数据结构的 Map 接口具体实现,允许 null 键和 null 值。这个类与 HashTable 近似等价,区别在于 HashMap 不是线程安全的并且允许 null 键和 null 值。由于基于哈希表实现,所以 HashMap 内部的元素是无序的。HashMap 对与 get 与 put 操作的时间复杂度是常数级别的(在散列均匀的前提下)。对 HashMap 的集合视图进行迭代所需时间与 HashMap 的 capacity(bucket 的数量)加上 HashMap 的尺寸(键值对的数量)成正比。因此,若迭代操作的性能很重要,不要把初始 capacity 设的过高(不要把 load factor 设的过低)。
有两个因素会影响一个 HashMap 对象的性能:intial capacity(初始容量)和 load factor(负载因子)。intial capacity 就是 HashMap 对象刚创建时其内部的哈希表的 "桶" 的数量(请参考哈希表的定义)。load factor 等于 maxSize / capacity,也就是 HashMap 所允许的最大键值对数与桶数的比值。增大 load factor 可以节省空间但查找一个元素的时间会增加,减小 load factor 会占用更多的存储空间,但是 get 与 put 的操作会更快。当 HashMap 中的键值对数量超过了 maxSize(即 load factor 与 capacity 的乘积),它会再散列,再散列会重建内部数据结构,桶数(capacity)大约会增加到原来的两倍。
HashMap 的构造器如下:
- HashMap()
- HashMap(int initialCapacity)
- HashMap(int initialCapacity, float loadFactor)
- HashMap(Map<? extends K,? extends V> m) //创建一个新的HashMap,用m的数据填充
常用方法如下:
- void clear()
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- V get(Object key)
- V put(K key, V value)
- boolean isEmpty()
- V remove(Object key)
- int size()
- Collection<V> values()
- Set<Map.Entry<K,V>> entrySet()
- Set<K> keySet()
它们的功能都很直观,更多的使用细节可以参考 Java 官方文档,这里就不贴上来了。这里简单地提一下 WeakHashMap,它与 HashMap 的区别在于,存储在其中的 key 是 "弱引用" 的,也就是说,当不再存在对 WeakHashMap 中的键的外部引用时,相应的键值对就会被回收。关于 WeakHashMap 和其他类的具体使用方法及注意事项,大家可以参考官方文档。
TreeMap<K, V> 一个基于红黑树的 Map 接口实现。TreeMap 中的元素的有序的,排序的依据是存储在其中的键的 natural ordering(自然序,也就是数字从小到大,字母的话按照字典序)或者根据在创建 TreeMap 时提供的 Comparator 对象,这取决于使用了哪个构造器。TreeMap 的 containsKey, get, put 和 remove 操作的时间复杂度均为 log(n)。
TreeMap 有以下构造器:
TreeMap() // 使用自然序对其元素进行排序
TreeMap(Comparator comparator) // 使用一个比较器对其元素进行排序
TreeMap(Map m) // 构造一个与映射表 m 含有相同元素的 TreeMap,用自然序进行排列
TreeMap(SortedMapm) // 构造一个与有序映射表 m 含有相同元素及元素顺序的 TreeMap
它的常见方法如下:
- K ceilingKey(K key)
- void clear()
- Comparator<? super K> comparator() //返回使用的比较器,若按自然序则返回null
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- NavigableSet<K> descendingKeySet() //返回一个包含在TreeMap中的键的逆序的NavigableSet视图
- NavigableMap<K,V> descendingMap()
- Set<Map.Entry<K,V>> entrySet()
- Map.Entry<K,V> firstEntry() //返回键最小的键值对
- Map.Entry<K,V> floorEntry(K key) //返回一个最接近指定key且小于等于它的键对应的键值对
- K floorKey(K key)
- V get(Object key)
- Set<K> keySet()
- Map.Entry<K,V> lastEntry() //返回与最大的键相关联的键值对
- K lastKey()
建议读者先了解下红黑树这个数据结构的原理及实现(可参考 算法(第 4 版) (豆瓣) ),然后再去看官方文档中关于这个类的介绍,这样学起来会事半功倍。
实现了这个接口的类支持一些 navigation methods,比如 lowerEntry(返回小于指定键的最大键所关联的键值对),floorEntry(返回小于等于指定键的最大键所关联的键值对),ceilingEntry(返回大于等于指定键的最小键所关联的键值对)和 higerEntry(返回大于指定键的最小键所关联的键值对)。一个 NavigableMap 支持对其中存储的键按键的递增顺序或递减顺序的遍历或访问。NavigableMap<K, V> 接口还定义了 firstEntry、pollFirstEntry、lastEntry 和 pollLastEntry 等方法,以准确获取指定位置的键值对。
总的来说,NavigableMap<K, V> 接口正如它的名字所示,支持我们在映射表中 "自由的航行",正向或者反向迭代其中的元素并获取我们需要的指定位置的元素。TreeMap 实现了这个接口。
- public static void main(String[] args) {
- String[] strings = {"first", "second", "third"};
- List<String> stringList = Arrays.asList(strings);
- String s1 = stringList.get(0);
- System.out.println(s1);
- stringList.add(0, "new first");
- }
注意:以上代码会编译成功,但是在运行时会抛出一个 UnsupportedOperationException 异常,原因是调用了改变列表大小的 add 方法。Arrays.asList 方法返回的封装了底层数组的集合视图不支持对改变数组大小的方法(如 add 方法和 remove 方法)的调用(但是可以改变数组中的元素)。
- List subgroup = group.subList(10, 20); //group为一个实现了List接口的集合
List 接口所定义的操作都可以应用于子范围,包括那些会改变列表大小的方法,比如以下方法会把 subgroup 列表清空,同时 group 中相应的元素也会从列表中移除:
subgroup.clear();
对于实现了 SortedSet<E> 接口的有序集或是实现了 SortedMap<K, V> 接口的有序映射表,我们也可以为他们创建子范围。SortedSet 接口定义了以下三个方法:
- SortedSet < E > subSet(E from, E to);
- SortedSet < E > headSet(E to);
- SortedSet < E > tailSet(E from);
SortedMap 也定义了类似的方法:
- SortedMap<K, V> subMap(K from, K to);
- SortedMap<K, V> headMap(K to);
- SortedMap<K, V> tailMap(K from);
- Collections.unmodifiableCollection
- Collections.unmodifiableList
- Collections.unmodifiableSet
- Collections.unmodifiableSortedSet
- Collections.unmodifiableMap
- Collections.unmodifiableSortedMap
- Map<String, Integer> map = Collections.synchronizedMap(new HashMap<String, Integer>());
集合视图本身不包含任何数据,它只是对相应接口的包装。集合视图所支持的所有操作都是通过访问它所关联的集合类实例来实现的。我们来看看 HashMap 的 keySet 方法的源码:
- public Set < K > keySet() {
- Set < K > ks;
- return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
- }
- final class KeySet extends AbstractSet < K > {
- public final int size() {
- return size;
- }
- public final void clear() {
- HashMap.this.clear();
- }
- public final Iterator < K > iterator() {
- return new KeyIterator();
- }
- public final boolean contains(Object o) {
- return containsKey(o);
- }
- public final boolean remove(Object key) {
- return removeNode(hash(key), key, null, false, true) != null;
- }
- public final Spliterator < K > spliterator() {
- return new KeySpliterator < >(HashMap.this, 0, -1, 0, 0);
- }
- public final void forEach(Consumer < ?super K > action) {
- Node < K,
- V > [] tab;
- if (action == null) throw new NullPointerException();
- if (size > 0 && (tab = table) != null) {
- int mc = modCount;
- for (int i = 0; i < tab.length; ++i) {
- for (Node < K, V > e = tab[i]; e != null; e = e.next) action.accept(e.key);
- }
- if (modCount != mc) throw new ConcurrentModificationException();
- }
- }
- }
可以看到,实际上 keySet() 方法返回一个内部 final 类 KeySet 的实例。我们可以看到 KeySet 类本身没有任何实例变量。我们再看 KeySet 类定义的 size() 实例方法,它的实现就是通过直接返回 HashMap 的实例变量 size。还有 clear 方法,实际上调用的就是 HashMap 对象的 clear 方法。
keySet 方法能够让你直接访问到 Map 的键集,而不需要复制数据或者创建一个新的数据结构,这样做往往比复制数据到一个新的数据结构更加高效。
Collections 类包含了大量用于操作或返回集合的静态方法。它包含操作集合的多态算法,还有包装集合的包装器方法等等。这个类中的所有方法在集合或类对象为空时均会抛出一个 NullPointerException。
关于 Java 集合框架,我们首先应该把握住几个核心的接口,请看下图:
来源: http://www.jianshu.com/p/def9dde93ade