我在 React Native 应用程序中将 Immutable.js 与 Redux 结合使用。
元数据(例如查找表)是从服务器获取的,并作为 Immutable.Map 保存在应用程序本地。查找值的键是整数(数据库中的主键)。
当我获取数据时,所有整数键都被强制转换为字符串。这是 js 对象的正常行为。因此,当创建查找映射时,键将是字符串。
Example:
let colors = Immutable.Map({
'10': 'yellow',
'20': 'pink',
..
})
引用查找值的对象将引用存储为数字,如下所示:
let michael = Immutable.Map({
name: 'Michael',
colorId: 10
})
由于 Immutable.js 不会将数字键强制转换为字符串,因此我无法执行此操作:
let color = colors.get(michael.get('colorId'))
上面的内容等同于:
let color = colors.get(10)
这是行不通的,因为 id 是一个字符串。这会起作用:
let color = colors.get('10')
我通常使用 Immutable.List 来存储从服务器获取的数据集。为了获取一个项目,我使用 find()。但是小型查找表(本质上是应用程序元数据)的访问非常频繁,并且需要快速。
你如何解决这个问题?以下是一些可供考虑的替代方案:
1.搜索时手动将key转换为字符串
let color = colors.get(michael.get('colorId') + '')
cons:
- 这不是一个有吸引力的解决方案。对我来说似乎是错误修剪。
2.使用普通的js对象来查找列表
pros:
- js 中对象键查找速度很快
- 自动将键强制转换为字符串,双向(写入和读取)
cons:
- 理想情况下,我想在任何地方都使用 Immutable.js。
3.使用List.find()查找表
pros:
cons:
- 我不知道使用 List.find() 而不是 Map.get() 对性能的影响
4. 精心设计 Immutable.Map 以使用数字键
let colors = new Immutable.Map()
.set(10, 'yellow')
.set(20, 'pink')
pros:
cons:
- 麻烦的
- 键已经被强制转换为通过线路发送的 json 对象中的字符串,因此我本质上是将它们转换回数字(我认为)
编辑..做了一些性能测试
来自简单性能测试的有趣数据。
- 10 项的查找列表
- 求一个值一百万次
- Mac 迷你酷睿 i7 2.6
带有数字键的 Immutable.Map:185 ms
使用 find() 的 Immutable.List:972 ms
带有强制键的普通 JS 对象:8 ms
使用 find() 的普通 JS 数组:127 ms
由于我使用的是 React Native,如果我想达到 60 fps,我总是必须注意 16 毫秒的限制。基准值似乎不是线性的。仅使用 100 次查找来运行测试,使用 Map 需要 1 毫秒,使用列表需要 2 毫秒。那是相当昂贵的。
我在基准测试中做错了什么吗?否则我想我现在必须把 Immutable.js 排除在外:(
测试代码
let Immutable = require('immutable');
let mapTest = Immutable.Map()
.set(1, Immutable.Map({value: 'one'}))
.set(2, Immutable.Map({value: 'two'}))
.set(3, Immutable.Map({value: 'three'}))
.set(4, Immutable.Map({value: 'four'}))
.set(5, Immutable.Map({value: 'five'}))
.set(6, Immutable.Map({value: 'six'}))
.set(7, Immutable.Map({value: 'seven'}))
.set(8, Immutable.Map({value: 'eight'}))
.set(9, Immutable.Map({value: 'nine'}))
.set(10, Immutable.Map({value: 'ten'}));
let listTest = Immutable.fromJS([
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
])
let objTest = {
1: {value: 'one'},
2: {value: 'two'},
3: {value: 'three'},
4: {value: 'four'},
5: {value: 'five'},
6: {value: 'six'},
7: {value: 'seven'},
8: {value: 'eight'},
9: {value: 'nine'},
10: {value: 'ten'}
};
let arrayTest = [
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
];
const runs = 1e6;
let i;
let key;
let hrStart;
console.log(' ')
console.log('mapTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = mapTest.getIn([key, 'value'] )
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('listTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = listTest
.find(item => item.get('key') === key)
.get('value');
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('arrayTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = arrayTest
.find(item => item.key === key)
.value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('objTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = objTest[key].value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);