1.Set
Set(interface): 存入Set的每个元素是唯一的,Set中不存在重复元素,加入Set的元素必须定义equals()方法一确保对象的唯一性,Set接口不保证维护元素的次序。
HashSet(默认): 快速查找,存入元素必须定义hashCode()
TreeSet : 保持次序,底层树结构,提取有序序列需要实现Comparable接口。
LinkedHashSet: 具有HashSet查询速度,并且内部维护链表元素顺序(插入的次序)。迭代遍历结果显示元素插入的次序。元素须定义hashCode()。
import java.util.*;
class SetType {
int i;
public SetType(int n) { i = n; }
public boolean equals(Object o) {
return o instanceof SetType && (i == ((SetType)o).i);
}
public String toString() { return Integer.toString(i); }
}
class HashType extends SetType {
public HashType(int n) { super(n); }
public int hashCode() { return i; }
}
class TreeType extends SetType
implements Comparable<TreeType> {
public TreeType(int n) { super(n); }
public int compareTo(TreeType arg) {
return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));
}
}
public class TypesForSets {
static <T> Set<T> fill(Set<T> set, Class<T> type) {
try {
for(int i = 0; i < 10; i++)
set.add(
type.getConstructor(int.class).newInstance(i));
} catch(Exception e) {
throw new RuntimeException(e);
}
return set;
}
static <T> void test(Set<T> set, Class<T> type) {
fill(set, type);
fill(set, type); // Try to add duplicates
fill(set, type);
System.out.println(set);
}
public static void main(String[] args) {
test(new HashSet<HashType>(), HashType.class);
test(new LinkedHashSet<HashType>(), HashType.class);
test(new TreeSet<TreeType>(), TreeType.class);
// Things that don't work:
test(new HashSet<SetType>(), SetType.class);
test(new HashSet<TreeType>(), TreeType.class);
test(new LinkedHashSet<SetType>(), SetType.class);
test(new LinkedHashSet<TreeType>(), TreeType.class);
try {
test(new TreeSet<SetType>(), SetType.class);
} catch(Exception e) {
System.out.println(e.getMessage());
}
try {
test(new TreeSet<HashType>(), HashType.class);
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} /* Output: (Sample)
[2, 4, 9, 8, 6, 1, 3, 7, 5, 0]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
[9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0, 8, 8, 8, 6, 5, 1]
[0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable
java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable
fill()三次尝试引入重复对象,SetType和TreeType没有重新定义HashCode(),它们放置任何散列实现中都会产生重复值,因为默认的hashCode()是合法,不会有运行错误。
2. Queue
队列,一端进,并且另一端出。
LinkedList
PriorityQueue 实现Comparable控制元素优先级
//: containers/ToDoList.java
// A more complex use of PriorityQueue.
import java.util.*;
class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
static class ToDoItem implements Comparable<ToDoItem> {
private char primary;
private int secondary;
private String item;
public ToDoItem(String td, char pri, int sec) {
primary = pri;
secondary = sec;
item = td;
}
public int compareTo(ToDoItem arg) {
if(primary > arg.primary)
return +1;
if(primary == arg.primary)
if(secondary > arg.secondary)
return +1;
else if(secondary == arg.secondary)
return 0;
return -1;
}
public String toString() {
return Character.toString(primary) +
secondary + ": " + item;
}
}
public void add(String td, char pri, int sec) {
super.add(new ToDoItem(td, pri, sec));
}
public static void main(String[] args) {
ToDoList toDoList = new ToDoList();
toDoList.add("Empty trash", 'C', 4);
toDoList.add("Feed dog", 'A', 2);
toDoList.add("Feed bird", 'B', 7);
toDoList.add("Mow lawn", 'C', 3);
toDoList.add("Water lawn", 'A', 1);
toDoList.add("Feed cat", 'B', 1);
while(!toDoList.isEmpty())
System.out.println(toDoList.remove());
}
} /* Output:
A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash
*///:~
HashMap(默认): Map基于散列表实现,代替HashTable。插入和查询key-value开销固定。通过构造器设置容量和负载因子以调整容器性能。
LinkedHashMap: 类似HashMap,迭代遍历顺序是插入顺序,或者是最近最少使用(LRU)次序,比HashMap慢一点,但是迭代访问反而更快,因为链表维护内部次序。
TreeMap: 基于红黑树实现。查看key或者key-value时,会排序(由Comparable或Comparator决定)。其特点是,得到的结构是排序的。TreeMap是唯一带有subMap()的Map,可以返回一个子树。
WeakHashMap: 允许释放指向的对象,如果映射之外没有引用的key,则该Key可以被回收。
ConcurrentHashMap: 线程安全,不涉及同和加锁。
IdentityHashMap: ==代替equals(),对key进行比较散列映射。
4.Hash&HashCode
一个结果错误的例子
public class Groundhog {
protected int number;
public Groundhog(int n) { number = n; }
public String toString() {
return "Groundhog #" + number;
}
}
import java.util.*;
public class Prediction {
private static Random rand = new Random(47);
private boolean shadow = rand.nextDouble() > 0.5;
public String toString() {
if(shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
}
import java.lang.reflect.*;
import java.util.*;
import static net.mindview.util.Print.*;
public class SpringDetector {
// Uses a Groundhog or class derived from Groundhog:
public static <T extends Groundhog>
void detectSpring(Class<T> type) throws Exception {
Constructor<T> ghog = type.getConstructor(int.class);
Map<Groundhog,Prediction> map =
new HashMap<Groundhog,Prediction>();
for(int i = 0; i < 10; i++)
map.put(ghog.newInstance(i), new Prediction());
print("map = " + map);
Groundhog gh = ghog.newInstance(3);
print("Looking up prediction for " + gh);
if(map.containsKey(gh))
print(map.get(gh));
else
print("Key not found: " + gh);
}
public static void main(String[] args) throws Exception {
detectSpring(Groundhog.class);
}
} /* Output:
map = {Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!}
Looking up prediction for Groundhog #3
Key not found: Groundhog #3
Groundhog自动继承Object,这里使用Object的hashCode()方法,默认是使用对象的地址计算hashCode,Object.equals()默认比较对象地址,所以Groundhog(3)第一个实例与第二个Groundhog(3)的hashCode是不同的
重写hashCode()同时,必须重写equals()
equals()方法满足以下:
1)自反性, 任意x.equals(x): true
2)对称性, x.equals(y):true -> y.equals(x):true
3)传递性, x.equals(y):true, x.equals(z):true -> y.equals(z):true
4)一致性, 对于x, y, 如果xy不变,x.equals(y)一直保持一致
5)x.equals(null) ---> false
上一例子修正
public class Groundhog2 extends Groundhog {
public Groundhog2(int n) { super(n); }
public int hashCode() { return number; }
public boolean equals(Object o) {
return o instanceof Groundhog2 &&
(number == ((Groundhog2)o).number);
}
}
public class SpringDetector2 {
public static void main(String[] args) throws Exception {
SpringDetector.detectSpring(Groundhog2.class);
}
} /* Output:
map = {Groundhog #2=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #1=Six more weeks of Winter!, Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #0=Six more weeks of Winter!}
Looking up prediction for Groundhog #3
Early Spring!
4.1 深入hashCode()
import java.util.*;
import net.mindview.util.Countries;
public class SlowMap<K,V> extends AbstractMap<K,V> {
private List<K> keys = new ArrayList<K>();
private List<V> values = new ArrayList<V>();
public V put(K key, V value){
V oldValue=get(key);
if(!keys.contains(key)){
keys.add(key);
values.add(value);
}else{
values.set(keys.indexOf(key), value);
}
return oldValue;
}
public V get(Object key){
if(!keys.contains(key))return null;
return values.get(keys.indexOf(key));
}
public static void main(String[] args) {
SlowMap<String,String> m = new SlowMap<String,String>();
m.putAll(Countries.capitals(15));
System.out.println(m);
// m.put("BURUNDI", "SHIT");
System.out.println(m.get("BURUNDI"));
System.out.println(m.entrySet());
System.out.println(m.entrySet().contains(new MapEntry<String,String>("ALGERIA", "Algiers")));
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K,V>>();
Iterator<K> ki = keys.iterator();
Iterator<V> vi = values.iterator();
while(ki.hasNext()){
set.add(new MapEntry<K,V>(ki.next(), vi.next()));
}
return set;
}
}
class MapEntry<K,V> implements Map.Entry<K, V>{
private K key;
private V value;
public MapEntry(K key, V valus){
this.key=key;
this.value=valus;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V v) {
V result = value;
value = v;
return result;
}
public int hashCode(){
return (key==null?0:key.hashCode())^(value==null?0:value.hashCode());
}
public boolean equals(Object o){
if(!(o instanceof MapEntry)) return false;
MapEntry me = (MapEntry)o;
return (key==null? me.getKey()==null:key.equals(me.getKey()))
&& (value==null?me.getValue()==null:value.equals(me.getValue()));
}
public String toString(){
return key + "-" + value;
}
}
output:
{CHAD=N'djamena, ALGERIA=Algiers, BOTSWANA=Gaberone, BURUNDI=Bujumbura, CONGO=Brazzaville, DJIBOUTI=Dijibouti, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, ANGOLA=Luanda, EGYPT=Cairo, CAPE VERDE=Praia, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, CENTRAL AFRICAN REPUBLIC=Bangui, CAMEROON=Yaounde}
Bujumbura
[CHAD-N'djamena, ALGERIA-Algiers, BOTSWANA-Gaberone, BURUNDI-Bujumbura, CONGO-Brazzaville, DJIBOUTI-Dijibouti, COMOROS-Moroni, EQUATORIAL GUINEA-Malabo, ANGOLA-Luanda, EGYPT-Cairo, CAPE VERDE-Praia, BURKINA FASO-Ouagadougou, BENIN-Porto-Novo, CENTRAL AFRICAN REPUBLIC-Bangui, CAMEROON-Yaounde]
true
SlowMap问题在于key的查询,key没有按照任何特定顺序保存,所以只能使用最简单,也是最慢的线性查询方式。
Hash的价值在于速度,Hash使得查询得意快速进行。
Hash将key保存在某处,以便能快速定位。存储一组元素最快的数据结构是数组,所以使用它来表示key的信息(不是key本身),
但是数组不能改变大小,而是通过key对象生成一个数字,将其作为数组的下标,这个数字就是hashCode。java中通过覆盖Object.hashCode()来产生。
为了解决数组容量固定的问题,不同的键可以产生相同的下标,这样可能发生冲突。所以数组的容量可以不关心,任何key都能在数组中找到位置。
于是查询的过程变成这样:计算hashCode,根据hashCode查找数组,如果无冲突,完美散列函数。冲突由外部链接处理,即数组不直接保存key,而是保存一个list。然后对list使用equals()方法进行线性查询。
线性查询较慢,但是如果hashCode()写得好(分布均匀),数组的每个位置的list只有较少值。
总结就是不用线性查询这个list,而是通过hashcode快速定位数组某个位置,然后进行很少的元素比较即可完成查询。这便是HashMap快的原因。
根据以上,实现一个HashMap例子:
import java.util.*;
import net.mindview.util.Countries;
public class SimpleHashMap<K,V> extends AbstractMap<K,V> {
static final int SIZE = 997;
LinkedList<MapEntry<K,V>>[] buckets = new LinkedList[SIZE];
public V put(K key, V value){
V oldValue = null;
int index = Math.abs(key.hashCode())%SIZE;
if(buckets[index]==null){
buckets[index] = new LinkedList<MapEntry<K,V>>();
}
LinkedList<MapEntry<K,V>> bucket = buckets[index];
MapEntry<K,V> pair = new MapEntry<K,V>(key,value);
boolean found = false;
ListIterator<MapEntry<K,V>> it = bucket.listIterator();
while(it.hasNext()){
MapEntry<K,V> ipair = it.next();
if(ipair.getKey().equals(key)){
oldValue = ipair.getValue();
it.set(pair);
found=true;
break;
}
}
if(!found){
buckets[index].add(pair);
}
return oldValue;
}
public V get(Object key){
int index = Math.abs(key.hashCode())%SIZE;
if(buckets[index]==null)return null;
for(MapEntry<K,V> ipair:buckets[index]){
if(ipair.getKey().equals(key)){
return ipair.getValue();
}
}
return null;
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K,V>>();
for(LinkedList<MapEntry<K,V>> bucket:buckets){
if(bucket==null)continue;
for(MapEntry<K,V> mpair:bucket){
set.add(mpair);
}
}
return set;
}
public static void main(String[] args){
SimpleHashMap<String, String> m = new SimpleHashMap<String,String>();
m.putAll(Countries.capitals(100));
System.out.println(m);
System.out.println(m.get("ERITREA"));
System.out.println(m.entrySet());
}
}
output:
{CHAD=N'djamena, ALGERIA=Algiers, BOTSWANA=Gaberone, BURUNDI=Bujumbura, CONGO=Brazzaville, DJIBOUTI=Dijibouti, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, ANGOLA=Luanda, EGYPT=Cairo, CAPE VERDE=Praia, BURKINA FASO=Ouagadougou, BENIN=Porto-Novo, CENTRAL AFRICAN REPUBLIC=Bangui, CAMEROON=Yaounde}
Gaberone
[CHAD-N'djamena, ALGERIA-Algiers, BOTSWANA-Gaberone, BURUNDI-Bujumbura, CONGO-Brazzaville, DJIBOUTI-Dijibouti, COMOROS-Moroni, EQUATORIAL GUINEA-Malabo, ANGOLA-Luanda, EGYPT-Cairo, CAPE VERDE-Praia, BURKINA FASO-Ouagadougou, BENIN-Porto-Novo, CENTRAL AFRICAN REPUBLIC-Bangui, CAMEROON-Yaounde]
设计hashCode()最重要一点是:无论何时,对同一个对象调用hashCode()都应该生产同样的值。如果put()产生一个hashCode,而get()取出来是另外一个hashCode,那么无法重现取得对象。
使用hashCode()不应该依赖具有唯一性的对象信息。比如第一个例子SpringDetector.java,因为使用Object.hashCode()是对象地址。
一个String设计hashCode的参考例子
//: containers/CountedString.java
// Creating a good hashCode().
import java.util.*;
import static net.mindview.util.Print.*;
public class CountedString {
private static List<String> created =
new ArrayList<String>();
private String s;
private int id = 0;
public CountedString(String str) {
s = str;
created.add(s);
// id is the total number of instances
// of this string in use by CountedString:
for(String s2 : created)
if(s2.equals(s))
id++;
}
public String toString() {
return "String: " + s + " id: " + id +
" hashCode(): " + hashCode();
}
public int hashCode() {
// The very simple approach:
// return s.hashCode() * id;
// Using Joshua Bloch's recipe:
int result = 17;
result = 37 * result + s.hashCode();
result = 37 * result + id;
return result;
}
public boolean equals(Object o) {
return o instanceof CountedString &&
s.equals(((CountedString)o).s) &&
id == ((CountedString)o).id;
}
public static void main(String[] args) {
Map<CountedString,Integer> map =
new HashMap<CountedString,Integer>();
CountedString[] cs = new CountedString[5];
for(int i = 0; i < cs.length; i++) {
cs[i] = new CountedString("hi");
map.put(cs[i], i); // Autobox int -> Integer
}
print(map);
for(CountedString cstring : cs) {
print("Looking up " + cstring);
print(map.get(cstring));
}
}
} /* Output: (Sample)
{String: hi id: 4 hashCode(): 146450=3, String: hi id: 1 hashCode(): 146447=0, String: hi id: 3 hashCode(): 146449=2, String: hi id: 5 hashCode(): 146451=4, String: hi id: 2 hashCode(): 146448=1}
Looking up String: hi id: 1 hashCode(): 146447
0
Looking up String: hi id: 2 hashCode(): 146448
1
Looking up String: hi id: 3 hashCode(): 146449
2
Looking up String: hi id: 4 hashCode(): 146450
3
Looking up String: hi id: 5 hashCode(): 146451
4
*///:~
List接口
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList类
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
Stack 类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
------------------------------------------------------------------------------------------------------------------------------------------------------
Set接口
Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不 会接受这两个对象。
HashSet
HashSet有以下特点
不能保证元素的排列顺序,顺序有可能发生变化
不是同步的
集合元素可以是null,但只能放入一个null
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
LinkedHashSet
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
------------------------------------------------------------------------------------------------------------------------------------------------------
Map 接口概述
java.util.Map 接口描述了映射结构, Map 接口允许以键集、值集合或键 - 值映射关系集的形式查看某个映射的内容。
Java 自带了各种 Map 类。 这些 Map 类可归为三种类型:
1. 通用 Map ,用于在应用程序中管理映射,通常在 java.util 程序包中实现
* HashMap
* Hashtable
* Properties
* LinkedHashMap
* IdentityHashMap
* TreeMap
* WeakHashMap
* ConcurrentHashMap
2. 专用 Map ,您通常不必亲自创建此类 Map ,而是通过某些其他类对其进行访问
* java.util.jar.Attributes
* javax.print.attribute.standard.PrinterStateReasons
* java.security.Provider
* java.awt.RenderingHints
* javax.swing.UIDefaults
3. 一个用于帮助实现您自己的 Map 类的抽象类
* AbstractMap
接口中的重要方法如下:
1, 覆盖的方法
equals(Object o) // 比较指定对象与此 Map 的等价性
hashCode() // 返回此 Map 的哈希码
2, Map 更新方法,可以更改 Map 的内容。
put(Object key, Object value) // 添加键值对,若键已存在,则覆盖旧值。
putAll(Map t) // 将指定 Map 中的所有映射复制到此 map
remove(Object key) // 从 Map 中删除与 key 关联的 value
clear() // 从 Map 中删除所有映射
3, 返回视图的 Map 方法:使用这些方法返回的对象,你可以遍历和删除 Map 的元素。
Set keySet() // 返回 Map 中所包含键的 Set 视图。
// 删除 Set 中的 key 元素还将删除 Map 中相应的映射(键和值)
Collection values() // 返回 map 中所包含值的 Collection 视图。
// 删除 Collection 中的 value 元素还将删除 Map 中相应的映射(键和值)
Set entrySet() // 返回 Map 中所包含映射的 Set 视图(键值对)。
Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问 Map.Entry 对象的键元素和值元素
关于 Map.Entry 接口
Map 的 entrySet() 方法返回一个实现 Map.Entry 接口的对象集合。集合中每个对象都是底层 Map 中一个特定的键 / 值对。通过这个集合的迭代器,您可以获得每一个条目 ( 唯一获取方式 ) 的键或值并对值进行更改。
(1) Object getKey(): 返回条目的关键字
(2) Object getValue(): 返回条目的值
(3) Object setValue(Object value): 将相关映像中的值改为 value ,并且返回旧值
当条目通过迭代器返回后,除非是迭代器自身的 remove() 方法或者迭代器返回的条目的 setValue() 方法,其余对源 Map 外部的修改都会导致此条目集变得无效,同时产生条目行为未定义。
4, Map 访问和测试方法:这些方法检索有关 Map 内容的信息但不更改 Map 内容。
get(Object key) // 返回与指定键关联的值 及此对象,若无,返回 null 。
boolean containsKey(Object key) // 如果 Map 包含指定键的映射,则返回 true
boolean containsValue(Object value) // 若此 Map 将一个或多个键映射到指定值,返回 true
isEmpty() // 如果 Map 不包含键 - 值映射,则返回 true
int size() // 返回 Map 中的键 - 值映射的数目
几乎所有通用 Map 都使用哈希映射。 这是一种将元素映射到数组的非常简单的机制,您应了解哈希映射的工作原理,以便充分利用 Map 。
哈希映射结构由一个存储元素的内部数组组成。 由于内部采用数组存储,因此必然存在一个用于确定任意键访问数组的索引机制。 实际上,该机制需要提供一个小于数组大小的整数索引值(即余数)。 该机制称作哈希函数。 在 Java 基于哈希的 Map 中,哈希函数将对象转换为一个适合内部数组的整数。 您不必为寻找一个易于使用的哈希函数而大伤脑筋: 每个对象都包含一个返回整数值的 hashCode() 方法。 要将该值映射到数组,只需将其转换为一个正值,然后在将该值除以数组大小后取余数即可。
哈希函数将任意对象映射到一个数组位置,但如果两个不同的键映射到相同的位置,情况将会如何? 这是一种必然发生的情况。 在哈希映射的术语中,这称作冲突。 Map 处理这些冲突的方法是在索引位置处插入一个链接列表,并简单地将元素添加到此链接列表。
HashMap与HashTable的区别:
1、同步性:Hashtable是同步的,这个类中的一些方法保证了Hashtable中的对象是线程安全的。而HashMap则是异步的,因此HashMap中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用HashMap是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销,从而提高效率。
2、值:HashMap可以让你将空值作为一个表的条目的key或value,但是Hashtable是不能放入空值的。HashMap最多只有一个key值为null,但可以有无数多个value值为null。
注意:
1、用作key的对象必须实现hashCode和equals方法。
2、不能保证其中的键值对的顺序
3、尽量不要使用可变对象作为它们的key值。
LinkedHashMap:
它的父类是HashMap,使用双向链表来维护键值对的次序,迭代顺序与键值对的插入顺序保持一致。LinkedHashMap需要维护元素的插入顺序,so性能略低于HashMap,但在迭代访问元素时有很好的性能,因为它是以链表来维护内部顺序。
TreeMap:
Map接口派生了一个SortMap子接口,SortMap的实现类为TreeMap。TreeMap也是基于红黑树对所有的key进行排序,有两种排序方式:自然排序和定制排序。Treemap的key以TreeSet的形式存储,对key的要求与TreeSet对元素的要求基本一致。
1、Map.Entry firstEntry():返回最小key所对应的键值对,如Map为空,则返回null。
2、Object firstKey():返回最小key,如果为空,则返回null。
3、Map.Entry lastEntry():返回最大key所对应的键值对,如Map为空,则返回null。
4、Object lastKey():返回最大key,如果为空,则返回null。
5、Map.Entry higherEntry(Object key):返回位于key后一位的键值对,如果为空,则返回null。
6、Map.Entry lowerEntry(Object key):返回位于key前一位的键值对,如果为空,则返回null。
7、Object lowerKey(Object key):返回位于key前一位key值,如果为空,则返回null。
8、NavigableMap subMap(Object fromKey,boolean fromlnclusive,Object toKey,boolean toInciusive):返回该Map的子Map,其key范围从fromKey到toKey。
9、SortMap subMap(Object fromKey,Object toKey );返回该Map的子Map,其key范围从fromkey(包括)到tokey(不包括)。
10、SortMap tailMap(Object fromkey ,boolean inclusive):返回该Map的子Map,其key范围大于fromkey(是否包括取决于第二个参数)的所有key。
11、 SortMap headMap(Object tokey ,boolean inclusive):返回该Map的子Map,其key范围小于tokey(是否包括取决于第二个参数)的所有key。
WeakHashMap:
WeakHashMap与HashMap的用法基本相同,区别在于:后者的key保留对象的强引用,即只要HashMap对象不被销毁,其对象所有key所引用的对象不会被垃圾回收,HashMap也不会自动删除这些key所对应的键值对对象。但WeakHashMap的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被回收。WeakHashMap中的每个key对象保存了实际对象的弱引用,当回收了该key所对应的实际对象后,WeakHashMap会自动删除该key所对应的键值对。
public static void main(String[] args) {
// TODO Auto-generated method stub
WeakHashMap w1=new WeakHashMap();
//添加三个键值对,三个key都是匿名字符串,没有其他引用
w1 .put("语文", "良好");
w1 .put("数学", "及格");
w1 .put("英语", "中等");
w1 .put("java", "good");//该key是一个系统缓存的字符串对象
System.out.println(w1 );//输出{java=good, 数学=及格, 英语=中等, 语文=良好}
//通知系统进行垃圾回收
System.gc();
System.runFinalization();
System.out.println(w1 );//输出{java=good}
}
IdentityHashMap类:
IdentityHashMap与HashMap基本相似,只是当两个key严格相等时,即key1==key2时,它才认为两个key是相等的 。IdentityHashMap也允许使用null,但不保证键值对之间的顺序。
EnumMap类:
1、EnumMap中所有key都必须是单个枚举类的枚举值,创建EnumMap时必须显示或隐式指定它对应的枚举类。
2、EnumMap根据key的自然顺序,即枚举值在枚举类中定义的顺序,来维护键值对的次序。
3、EnumMap不允许使用null作为key值,但value可以。