【问题标题】:HashMaps vs Reactive ProgrammingHashMaps 与反应式编程
【发布时间】:2015-05-12 14:40:59
【问题描述】:

我开始更多地接受响应式编程,并尝试将其应用于我的典型业务问题。我经常设计的一种模式是数据库驱动的类。我有一些已定义的单元类,例如ActionProfile,其实例由ActionProfileManager 管理,它从数据库表中创建实例并将它们存储在Map<Integer,ActionProfile> 中,其中IntegeractionProfileId 键。 ActionProfileManager 可能会定期清除并重新导入数据,并通知所有依赖项从其映射中重新拉取。

public final class ActionProfileManager {
    private volatile ImmutableMap<Integer,ActionProfile> actionProfiles;

    private ActionProfileManager() { 
        this.actionProfiles = importFromDb();
    }

    public void refresh() { 
        this.actionProfiles = importFromDb();
        notifyEventBus();
    }

    //called by clients on their construction or when notifyEventBus is called
    public ActionProfile forKey(int actionProfileId) { 
        return actionProfiles.get(actionProfiles);
    }

    private ImmutableMap<Integer,ActionProfile> importFromDb() { 
        return ImmutableMap.of(); //import data here
    }
    private void notifyEventBus() { 
        //notify event through EventBus here
    }
}

但是,如果我想让它更具反应性,那么创建地图会破坏 monad。我可以做的一种方法是让Map 本身成为一个 Observable,并返回一个为客户端查找特定密钥的 monad。然而,中间命令式操作可能并不理想,特别是如果我开始使用rxjava-jdbc 时。但是在密集的情况下,hashmap 可能有助于显着提高查找性能。

public final class ActionProfileManager {
    private final BehaviorSubject<ImmutableMap<Integer,ActionProfile>> actionProfiles;

    private ActionProfileManager() { 
        this.actionProfiles = BehaviorSubject.create(importFromDb());
    }

    public void refresh() { 
        actionProfiles.onNext(importFromDb());
    }

    public Observable<ActionProfile> forKey(int actionProfileId) { 
        return actionProfiles.map(m -> m.get(actionProfileId));
    } 
    private ImmutableMap<Integer,ActionProfile> importFromDb() { 
        return ImmutableMap.of(); //import data here
    }
}

因此,对我来说最被动的方法似乎只是在每次刷新时通过 Observable&lt;ActionProfile&gt; 推送数据库中的所有内容,并过滤客户端的最后一个匹配 ID。

public final class ActionProfileManager {
    private final ReplaySubject<ActionProfile> actionProfiles;

    private ActionProfileManager() { 
        this.actionProfiles = ReplaySubject.create();
        importFromDb();
    }

    public void refresh() { 
        importFromDb();
    }

    public Observable<ActionProfile> forKey(int actionProfileId) { 
        return actionProfiles.filter(m -> m.getActionProfileID() == actionProfileId).last();
    } 
    private void importFromDb() { 
        // call onNext() on actionProfiles and pass each new ActionProfile coming from database
    }
}

这是最佳方法吗?导致内存泄漏而不被 GC 处理的旧数据呢?维护地图并使其可观察是否更实用?

上述数据驱动类的最佳响应式方法是什么?或者有没有更好的方法我还没有发现?

【问题讨论】:

  • IMO 您可能要考虑为此使用Redis 数据库 - IMO 您可以同时改进您的mapping(使用其键值条目)和您的 observable(因为 Redis 实现了 JMS)
  • 一直在做一些其他的研究,也许我不应该使用主题,而是我自己的 Observable implementations github.com/ReactiveX/RxJava/issues/1794
  • 您当前方法的目标是为任何请求数据的消费者提供缓存吗?调用刷新的触发器是什么?
  • 触发器可能会从 UI 事件中触发,很可能来自 JavaFX 控件。我还可以定期创建一个计时器来刷新业务参数并重建地图。我确实想要某种类型的缓存(我认为),因为新订阅者可以随时在 UI 环境中锁定。但我也不希望保留陈旧的数据。
  • 我最终避免使用Subject,而是选择使用Obersvable.defer() 和/或Observable.cache()。我发现 Subject 的无限热特性在用于支持复杂的 UI 触发流程时会很麻烦,而且我总是不得不调用 take(1) 以使其对于该流程实例来说是冷的和有限的。

标签: java monads reactive-programming rx-java


【解决方案1】:

如果您不关心早期的值,则在这里使用BehaviorSubject 是正确的做法。

请注意,大多数令人沮丧的主题都是在 Rx.NET 的早期编写的,并且大多被一遍又一遍地引用而没有经过深思熟虑。我将此归因于这样的作者可能并不真正了解主题是如何工作的,或者遇到了一些问题,只是声明不应该使用它们。

我认为主题是多播事件(通常来自单个线程)的好方法,您可以在其中控制或者您是事件的来源,并且事件分派有点“全局”(例如侦听鼠标移动事件) .

【讨论】:

  • 您对有关使用哈希图的原始问题有任何意见吗?但是谢谢这确实阐明了我的第二个问题。
  • 我不太明白这个问题。
  • 对于最初的问题,我想我会继续在 BehaviorSubject 中添加一个 Map 并将 Observables 映射到它的查找上。虽然不是纯粹的反应式,但它似乎是最干净的,并且在大量数据时最不容易出现性能问题。
猜你喜欢
  • 2019-03-16
  • 2018-12-28
  • 2014-10-09
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 2018-11-28
  • 1970-01-01
相关资源
最近更新 更多