Java中的AbstractMap
在Java中,Map是一种常用的数据结构,它表示了一组键值对的映射关系。Java提供了许多实现Map的类,其中之一就是AbstractMap。本文将介绍AbstractMap的用途、实现原理、以及一些示例代码。
AbstractMap概述
AbstractMap是一个抽象类,它是Map接口的一个实现。它提供了一些方法的默认实现,使得实现Map接口的类可以更加方便地实现自己的类。AbstractMap实现了Map接口中的大部分方法,并留出一些方法给子类去实现,例如entrySet()和keySet()。下面是AbstractMap中实现的方法:
public abstract class AbstractMap<K,V> implements Map<K,V> {
abstract class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public int size() {
return AbstractMap.this.size();
}
public void clear() {
AbstractMap.this.clear();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Entry<?,?> candidate = AbstractMap.this.getEntry(key);
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
return AbstractMap.this.remove(key, e.getValue());
}
}
public int size() {
return entrySet().size();
}
public boolean isEmpty() {
return size() == 0;
}
public boolean containsValue(Object value) {
for (Iterator<Map.Entry<K,V>> i = entrySet().iterator(); i.hasNext(); ) {
Map.Entry<K,V> e = i.next();
if (Objects.equals(value, e.getValue()))
return true;
}
return false;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<?,?> that = (Map<?,?>)o;
if (that.size() != size())
return false;
try {
for (Map.Entry<K,V> e : entrySet()) {
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(that.get(key)==null && that.containsKey(key)))
return false;
} else {
if (!value.equals(that.get(key)))
return false;
}
}
} catch (ClassCastException | NullPointerException unused) {
return false;
}
return true;
}
public int hashCode() {
return entrySet().hashCode();
}
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public V remove(Object key) {
Iterator<Map.Entry<K,V>> i = entrySet().iterator();
Entry<K,V> correctEntry = null;
while (correctEntry == null && i.hasNext()) {
Entry<K,V> e = i.next();
if (Objects.equals(key, e.getKey()))
correctEntry = e;
}
V oldValue = null;
if (correctEntry != null) {
oldValue = correctEntry.getValue();
i.remove();
}
return oldValue;
}
public void clear() {
entrySet().clear();
}
public Set<K> keySet() {
Set<K> ks = keySet;
return (ks != null ? ks : (keySet = new AbstractSet<K>() {
public Iterator<K> iterator() {
return new Iterator<K>() {
private Iterator<Map.Entry<K,V>> i = entrySet().iterator();
public boolean hasNext() {
return i.hasNext();
}
public K next() {
return i.next().getKey();
}
public void remove() {
i.remove();
}
};
}
public int size() {
return AbstractMap.this.size();
}
public boolean contains(Object o) {
return AbstractMap.this.containsKey(o);
}
}));
AbstractMap实现原理
AbstractMap的实现原理比较简单,它主要是提供了一些Map接口方法的默认实现。这些默认实现可以让实现Map接口的类更加方便地实现自己的类。例如,实现Map接口时需要提供一个entrySet()方法,它返回一个包含键值对的集合。由于AbstractMap已经实现了该方法,实现Map接口的类只需要继承AbstractMap,并根据自身情况重写entrySet()方法即可。
除了entrySet()方法,AbstractMap还提供了其他一些方法的默认实现,例如size()、isEmpty()、equals()、hashCode()等。这些方法都是在AbstractMap中实现的,具体实现方式也比较简单,大部分都是直接调用entrySet()方法的默认实现。例如,size()方法只需要返回entrySet()的size即可。
AbstractMap示例代码
下面我们来看一些示例代码。首先,我们需要实现一个自定义的Map类,这个类可以将一个字符串转换为Map对象:
public class StringMap extends AbstractMap<String, String> {
private final String[] pairs;
public StringMap(String... pairs) {
if (pairs.length % 2 != 0)
throw new IllegalArgumentException("Invalid number of key-value pairs");
this.pairs = pairs;
}
@Override
public Set<Map.Entry<String, String>> entrySet() {
Set<Map.Entry<String, String>> set = new HashSet<>();
for (int i = 0; i < pairs.length; i += 2) {
set.add(new SimpleEntry<>(pairs[i], pairs[i + 1]));
}
return set;
}
public static void main(String[] args) {
StringMap m = new StringMap("name", "Alice", "age", "20");
System.out.println(m.get("name")); // 输出Alice
System.out.println(m.get("age")); // 输出20
}
}
在上面的例子中,我们首先实现了一个继承自AbstractMap的StringMap类。StringMap类有一个构造方法,它接受一组字符串作为参数。这些字符串将被按照键值对的形式解析出来,并存储到一个HashMap中。我们可以使用get()方法来查找某个键对应的值。
下面是一个更加具体的案例,实现一个简单的词频统计程序:
public class WordCounter extends AbstractMap<String, Integer> {
private Map<String, Integer> map = new HashMap<>();
public WordCounter(String text) {
String[] words = text.split("\\s+");
for (String word : words) {
String w = word.toLowerCase();
int count = map.getOrDefault(w, 0);
map.put(w, count + 1);
}
}
@Override
public Set<Entry<String, Integer>> entrySet() {
return map.entrySet();
}
public static void main(String[] args) {
String text = "This is a test. This is another test.";
WordCounter wc = new WordCounter(text);
System.out.println(wc.get("this")); // 输出2
System.out.println(wc.get("test.")); // 输出1
}
}
在上面的例子中,我们实现了一个继承自AbstractMap的WordCounter类,它用来统计文本中每个单词出现的次数。我们可以使用get()方法查找某个单词出现的次数。
结论
AbstractMap是Java中实现Map接口的一个抽象类,它提供了一些方法的默认实现,使得实现Map接口的类可以更加方便地实现自己的类。AbstractMap中已经实现了大部分Map接口中的方法,例如size()、isEmpty()、equals()、hashCode()等,同时留出一些方法给子类去实现,例如entrySet()和keySet()。根据具体的需要,我们可以自定义继承自AbstractMap的子类,在该子类中实现自己的方法,从而达到更好的封装和代码复用。
虽然AbstractMap提供了一些Map接口方法的默认实现,但并不是所有的Map实现都需要继承AbstractMap。例如,HashMap就不是继承自AbstractMap的,而是直接实现了Map接口。因此,在实现自己的Map类时,我们需要根据实际情况去选择是否继承AbstractMap。
总的来说,AbstractMap是一个非常有用的工具类,它可以让我们更加方便地实现自己的Map类。如果你经常需要实现Map类,那么AbstractMap肯定是一个值得掌握的工具。