union
传入:set1, set2
传出:内部实现的 setView 其继承了 AbstractSet
public static <E> SetView<E> union(final Set<? extends E> set1, final Set<? extends E> set2) {
checkNotNull(set1, "set1");
checkNotNull(set2, "set2");
return new SetView<E>() {
@Override
public int size() {
int size = set1.size();
for (E e : set2) {
if (!set1.contains(e)) {
size++;
}
}
return size;
}
@Override
public boolean isEmpty() {
return set1.isEmpty() && set2.isEmpty();
}
@Override
public UnmodifiableIterator<E> iterator() {
return new AbstractIterator<E>() {
final Iterator<? extends E> itr1 = set1.iterator();
final Iterator<? extends E> itr2 = set2.iterator();
@Override
protected E computeNext() {
if (itr1.hasNext()) {
return itr1.next();
}
while (itr2.hasNext()) {
E e = itr2.next();
if (!set1.contains(e)) {
return e;
}
}
return endOfData();
}
};
}
@Override
public Stream<E> stream() {
return Stream.concat(set1.stream(), set2.stream().filter(e -> !set1.contains(e)));
}
@Override
public Stream<E> parallelStream() {
return stream().parallel();
}
@Override
public boolean contains(Object object) {
return set1.contains(object) || set2.contains(object);
}
@Override
public <S extends Set<E>> S copyInto(S set) {
set.addAll(set1);
set.addAll(set2);
return set;
}
@Override
public ImmutableSet<E> immutableCopy() {
return new ImmutableSet.Builder<E>().addAll(set1).addAll(set2).build();
}
};
}
- JAVA所有的传参都是引用,除了基本类型,final字段不允许引用被改变
- 静态方法的生命周期随着类的消亡而消亡
由这两点就可以知道,在union方法进行方法调用时,set1和set2会一直存在于SetView方法中,并且不会随着new的申请空间而拷贝,因此set1,set2会一直存在并且随着外部的改变而改变。
在union内部重载中,依然是进行了懒操作,即只在需要时才进行iterator的遍历
filter
filter方法与Lists实现partition的方法需要注意的地方一样,即传入的时候有什么,传出的时候什么没了。
public static <E> Set<E> filter(Set<E> unfiltered, Predicate<? super E> predicate) {
if (unfiltered instanceof SortedSet) {
return filter((SortedSet<E>) unfiltered, predicate);
}
if (unfiltered instanceof FilteredSet) {
// Support clear(), removeAll(), and retainAll() when filtering a filtered
// collection.
FilteredSet<E> filtered = (FilteredSet<E>) unfiltered;
Predicate<E> combinedPredicate = Predicates.<E>and(filtered.predicate, predicate);
return new FilteredSet<E>((Set<E>) filtered.unfiltered, combinedPredicate);
}
return new FilteredSet<E>(checkNotNull(unfiltered), checkNotNull(predicate));
}
观察这段源码,可以发现方法中涉及到了两个类:
- SortedSet
- FilteredSet
这两个类都是内部类,并且都没有真正实现filter的功能,而只是进行一大堆的该有的重载和继承
其关键点在于FilteredSet类中,google在这里做了一个判断:如果是FilteredSet类,那么在进行filter时直接进行条件的再次叠加,不得不说,google在这一点上费劲心思
而SortedSet仅仅是为了继承SortedSet这个标签罢了
这样做达到了达到了两个效果:
- 实现懒操作,只有在整个流结束时才进行运算
- 对逻辑上判断做出交集,大大减少运算时的判断消耗
那么问题就来了,什么时候真正进行filter操作呢?哈哈哈,那些就直接交给已经写好的JDK collect父类吧。
从以上两个类可以发现google在设计开发时的几个原则:
- 尽可能的‘’懒’,即调用已有的类在更高层的类中进行操作,在底层仅做底层抽象和封装
- 尽可能的抽象,即在内部尽可能的调用高层的抽象类,再次进行抽象,一直到完成功能
- 尽可能的精细,即在该涉及到的问题上比如标签和多次filter上进行操作,尽可能的考虑到情况
- 大量使用泛型,静态内部类