我正在阅读Joshua Bloch撰写的有效java书。在“赞成组合而不是继承”的第16项中,他给出了一个使用HashSet并查询自创建以来添加了多少元素的示例(不要与当前大小相混淆,当删除元素时该大小会下降)。他提供了以下代码,这里是getAddCount返回6,我可以理解。这实际上应该返回3。 (这是因为HashSet的addAll方法是在其add方法的基础上实现的)
import java.util.HashSet;
public class InstrumentedHashSet<E> extends HashSet<E> {
// The number of attempted element insertions
private int addCount = 0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
现在他解释了使用包装类(组合和转发)解决这个问题的方法。这是我很难理解的地方。他提供了以下两个类
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) {
this.s = s;
}
public void clear() {
s.clear();
}
public boolean contains(Object o) {
return s.contains(o);
}
public boolean isEmpty() {
return s.isEmpty();
}
public int size() {
return s.size();
}
public Iterator<E> iterator() {
return s.iterator();
}
public boolean add(E e) {
return s.add(e);
}
public boolean remove(Object o) {
return s.remove(o);
}
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return s.addAll(c);
}
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
public boolean retainAll(Collection<?> c) {
return s.retainAll(c);
}
public Object[] toArray() {
return s.toArray();
}
public <T> T[] toArray(T[] a) {
return s.toArray(a);
}
@Override
public boolean equals(Object o) {
return s.equals(o);
}
@Override
public int hashCode() {
return s.hashCode();
}
@Override
public String toString() {
return s.toString();
}
}
AND
import java.util.*;
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
public static void main(String[] args) {
InstrumentedSet<String> s = new InstrumentedSet<String>(
new HashSet<String>());
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
这是如何运作的?在main方法中,我创建了一个HashSet实例并使用addAll方法,我添加了list的所有元素。但是HashSet调用它的addAll方法(后者又使用它的add方法),它应该与正确示例中的第一个相同,我应该得到6的值,但是这给了我3个。
答案 0 :(得分:4)
在
public class InstrumentedHashSet<E> extends HashSet<E> {
您直接添加到HashSet
,因为addAll()
委托给超级实施
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
addAll()
内部调用add()
,因为多态性而推迟@Override
add()
的实施
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
增加计数并打印6
(3 + 1 + 1 + 1)。
在
public class InstrumentedSet<E> extends ForwardingSet<E> {
你要加入
private final Set<E> s;
因为addAll()
委托给它,所以
public static void main(String[] args) {
InstrumentedSet<String> s = new InstrumentedSet<String>(
new HashSet<String>());
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
并打印3
。这里add()
正在调用Set<E> s
,而不是您的实例。
结论是,如果你继承,你需要了解副作用。 super
方法调用是否在内部调用任何其他方法调用?如果是这样,您需要采取适当的行动。
继承(从下面开始)
s.add() // s is your InstrumentedHashSet instance, because of polymorphism (inheritance), this adds to the count
this.add() // this is the internal call inside the HashSet#addAll()
super.addAll(...) // this calls the HashSet implementation of addAll which calls add() internally
s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedHashSet instance
组合物
this.add() // this is the internal call to add() inside the Set implementation
s.addAll() // s is the Set<E> instance
super.addAll(...) // this calls the ForwardingSet implementation of addAll()
s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedSet instance
答案 1 :(得分:1)
InstrumentedSet#getAddCount()
返回6,因为数组(3)的大小加了两次!
//InstrumentedSet
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); //here
return super.addAll(c); //and here!
}
super.addAll(c);
调用add()
方法。
更详细:
InstrumentedSet#addAll - &gt; ForwardingSet#addAll(因为super.addAll) - &gt; HashSet#addAll()(因为这是您在主要内容中提供的内容) - &gt; InstrumentedSet #add(因为多态)
如果您想要修复:删除addCount += c.size();
InstrumentedSet#addAll
会返回3,因为它会调用:
InstrumentedSet #addAll()(添加3) - &gt; ForwardingSet#addAll(因为超级) - &gt; HashSet#addAll(因为forwardingset有一个HashSet类型的字段) - &gt;的HashSet#添加