在对某些状态进行更新以使用ImmutableJS之前,我正在对个人项目进行完整性测试。我进行了一次小测试,以确保Immutable.List.equals
的运行效果符合我的期望-O(1)。
https://github.com/naddeoa/immutablejs-slow-equals.git
重要的部分在下面
function compareImmutableMany(l1, l2) {
let result = false;
for (let i = 0; i < iterations; i++) {
result = l1.equals(l2);
}
return result;
}
i1 = createImmutableList();
i2 = createImmutableList();
console.log("immutable lists are equal", compareImmutableMany(i1, i2));
测试是比较两个大小为100,000的本机js列表,然后比较两个大小为100,000的Immutable.List,每个1000次。我肯定错过了什么。我看到Immutable.List示例相对于本机列表示例而言表现很差。
starting
immutable lists are equal true
Seconds: 11.423
starting
normal lists are equal: true
Seconds: 0.109
您可以使用以下命令在本地运行它。
git clone https://github.com/naddeoa/immutablejs-slow-equals.git
cd immutablejs-slow-equals
npm i
node main.js
如果我犯了一个简单的错误,那么我会感谢一些眼睛让我知道它在哪里。我绝对希望这会非常快。尤其是因为用调用l1.equals(l2)
代替l1.hashCode()
确实非常快。
答案 0 :(得分:1)
根据docs:
如果两个不可变集合表示相同的值集合,则它们被视为值相等(通过.equals()或is())。这不同于JavaScript的对象和数组的相等引用(通过===或==),后者仅确定两个变量是否表示对同一对象实例的引用。
这意味着它不能为O(1),因为它需要检查列表中所有值的相等性。正如您所发现的,这将大大降低速度。
性能折衷也记录在案:
比较两个集合时,值相等性可能需要考虑每个集合中的每个项目,时间复杂度为O(N)。对于大量的价值收集,这可能会成为一项昂贵的操作。尽管如果两者不相等且几乎不相似,则可以很快确定不平等。相反,在比较具有引用相等性的两个集合时,仅需要比较对内存的初始引用,这不是基于集合的大小,而是具有O(1)时间复杂度。检查引用相等性总是非常快,但是,仅仅因为两个集合不是引用相等的,并不排除它们可能是值相等的可能性。
答案 1 :(得分:0)
我认为这可以归为用户错误。我习惯了语言的不变性,因为它是一等公民,并且没有将ImmutableJS映射到JS。我的错误(至少)如下。
首先,.equals()
在设计上是一个很深的平等,这不是在React中像shouldComponentUpdate
调用之类的实现性能相等检查的依据。
第二,要充分利用ImmutableJS,您需要正确使用其API。这是一个例子。
const Foo = Immutable.Record({a:1})
const foo1 = Foo({a:2})
const foo2 = Foo({a:2})
const cache = Immutable.Map({'key': foo1})
// Doing it this way will force you to either use .equals() or .hashCode()
// to check for changes later
const wrongWay = cache.set('key', foo2)
console.log('This is false', wrongWay === cache)
console.log('This is true', wrongWay.hashCode() === cache.hashCode())
console.log('This is true and slow', wrongWay.equals(cache))
// Doing it this way will let you rely in reference equality
const rightWay = cache.mergeDeep({'key': foo2})
console.log('This is true', rightWay === cache)
console.log('This is true, but not needed', rightWay.hashCode() === cache.hashCode())
console.log('This is true and slow, but not needed', rightWay.equals(cache))
第三,当将它与React一起使用时,为了使用PureComponent进行浅的引用相等性检查,您需要确保最终将获得引用相等性(如上例所示)。
最后,有时您需要在React的应用程序状态上执行昂贵的功能,才能获取组件的支持。为了无缘无故地避免这样做,可以将Lodash的备忘录与不可变对象上的引用相等性结合使用,如下面的示例所示。
const myFunction = _.memoize((immutableAppState) => {
return immutableAppState.immutableList.map(/* really expensive stuff returning new immutable objects based on other selectors */)
}, (immutableAppState) => immutableAppState.specificRecordThatChanges )
如果有人在这里发现一些错误(或遗漏了错误),请指出来,我会更新说明。