一个发射简单的随机数的无限流可以非常有用,它通常与其他流结合使用。下面的Observable产生了从0到1000的伪随机整数:

Observable<Integer> randomInts = Observable.create(subscriber -> {
    Random random = new Random();
    while (!subscriber.isUnsubscribed()) {
        subscriber.onNext(random.nextInt(1000));
    }
});

显然,重复肯定会发生,执行(1001)次就可以保证至少有一个是重复的。但是,如果我们只想要看前几个(比如说前10个)事件,但是这些事件必须是唯一的,不能重复?内置的distinct()操作符会自动丢弃上游Observable已经发生的事件,确保只向下游传递唯一的事件:

Observable<Integer> uniqueRandomInts = randomInts
    .distinct()
    .take(10);

每当从上游Observable(本例中即randomInts)发出新值时,distinct()操作符会在内部确保这种值以前没有发生过。通过equals()和hashCode()方法进行比较,因此确保按照Java指南实现它们(两个相等的对象必须具有相同的散列代码)。有趣的是,取1001次的话,最终会以随机顺序发射0到999中的每一个值,而且永远不会完成,因为在0和999之间没有第1,001个唯一的值。

在第45页的“Use Case: From Callback API to Observable Stream”中,我们来看Observable<twitter4j.Status>,它在社交媒体网站Twitter上发布的状态更新。每当用户发布状态更新时,就会推送新的事件。Status对象包含几个属性,比如getText()、getUser(),等等。distinct()操作符对状态事件没有意义,因为几乎不可能有重复的操作。但是,如果我们希望只看到每个用户的第一次更新的文本(status.getUser(). getid()返回long)呢?显然,我们可以提取这种惟一的属性并在其上运行distinct():

Observable<Status> tweets = //...
Observable<Long> distinctUserIds = tweets
    .map(status -> status.getUser().getId())
    .distinct();

不幸的是,当我们执行distinct()时,原始的Status对象丢失了。我们真正需要的是提取用于确定惟一性的事件属性的方法。如果被提取的属性(称为key)已经见过了,那么两个事件被认为是相等的(而后者被丢弃了)。

Observable<Status> distinctUserIds = tweets
    .distinct(status -> status.getUser().getId());

无论我们返回的是哪个key,都将使用equals()和hashCode()与已经看到过的key进行比较。一定要记住,distinct()必须记住所有的events/keys,直到永远。(参阅第315页的“内存消耗和泄漏”。当我们只想处理一次唯一的事件时,distinct()是很有用的)。

在实践中,distinctUntilChanged()通常更合理。在distinctUntilChanged()这个用例中,任何给定的事件只有在前一个事件是相同的情况下才会被丢弃(默认情况下使用equals()进行比较)。当我们收到一些稳定的测量数据,我们希望在测量值实际发生变化时得到通知,这时distinctUntilChanged()就工作的很好。在79页的“Pairwise Composing Using zip() and zipWith()”中,哦们体验了Observable<Weather> ,其中Weather 有两个属性:Temperature和Wind。一个新的Weather事件可以每分钟出现一次,但是天气不会经常变化,所以我们想放弃重复的事件,只关注变化了的事件:

Observable<Weather> measurements = //...
Observable<Weather> tempChanges = measurements
    .distinctUntilChanged(Weather::getTemperature);

上面的代码片段只在温度变化时才会发出Weather事件(未考虑到Wind的变化)。显然,如果我们想要在每次温度或风向变化时发出一个事件,那么使用参数的distinctUntilChanged()会更好,假定Weather实现了equals()方法。 distinct()和 distinctUntilChanged()之间的重要区别是,后者可以产生重复,但前提是它们被不同的值分隔开。例如,同样的温度可能每天都会发生,被更冷更温暖的其他测量所隔开。另外,distinctUntilChanged()必须只记住最后看到的值,而不是distinct(),它必须跟踪自流开始以来的所有惟一值。这意味着distinctUntilChanged()有一个可预测的、常量的内存占用,而不像distinct()那样,内存占用可能是无限的。

results matching ""

    No results matching ""