【问题标题】:How to implement a cache with binary array as key and binary arrays as values in Java如何在Java中实现以二进制数组作为键、二进制数组作为值的缓存
【发布时间】:2009-12-03 22:58:26
【问题描述】:

我需要创建一个包含所有城市和机场的 Java 缓存。所以,如果我在缓存中查询一个位置,比如说一个城市,它应该返回那个城市的所有机场,如果我查询一个机场的位置,我应该返回那个机场。 此外,每个位置都必须作为字节数组存储在缓存中。(因为用于查询缓存的公开接口具有 byte[] 作为位置参数) 其他注意事项是:

  1. 检索必须非常快,尽可能快
  2. 缓存只在系统启动时加载一次,获取后不改变 加载。
  3. 由于它只加载一次,如果这样可以加快检索速度,我们可以对其进行排序。

到目前为止我得到了什么:

方法 1

在 byte[] 数组上创建一个瘦包装器,比如说 ByteWrapper。将每个位置(机场和城市)作为地图(TreeMap?)中的键。使用 ByteWrapper 列表(包含适用的机场)作为值。

方法 2

创建按位置排序的多维 byte[] 数组。它本质上是一张地图。然后使用二分查找来定位key并返回结果。

你会建议什么方法?如果您有更好的想法,请告诉我 谢谢

【问题讨论】:

  • 放纵我:你为什么用byte[]s 代表城市和机场?
  • :) 嗯。我们有另一个缓存,它使用 bytes[](编码的机场)作为有关机场的其他信息的键。这样做是为了节省空间和更快的访问。该缓存的问题在于它基于机场。我们现在想支持城市。但是,我们不想在该缓存中再创建一个级别(城市-> 机场-> 其他信息-> 更多信息),因为它已经有 3-4 个级别。因此,我们正在创建这个新的缓存,它将用于获取给定城市/机场的机场,并使用结果来查询现有的基于机场的缓存。嗯,我是不是含糊其辞? :)
  • 嗯没有人回答?我正在研究解决方案。 tomm会让你知道结果。如果您有任何想法,请提出一些更好的想法。

标签: java arrays caching hashmap


【解决方案1】:

公开的 API 是基于 byte[] 的事实不应该必然决定缓存的内部细节。

第二个观察是这不是一个广义的数据结构问题。所有机场的空间和所有城市的空间都是有限的,并且是众所周知的。 (你甚至知道大小)。

哈希图、树等都是保证某些性能特征在既定范围内的算法一般用途

既然数据完整性不是问题(“数据不会改变”),而且如果空间考虑不重要(“尽可能快”),那么为什么不呢:

[编辑:这一点不知何故在剪切和粘贴中丢失了:您索引(编号)您的城市和机场,因为您知道这些集合并且它们实际上是静态的。]

// these need to get initialized on startup
// this initialization can be optimized.

Map<byte[], Long> airportIndexes = new HashMap<byte[], Long>(NUMBER_OF_AIRPORTS);
Map<byte[], Long> citiesIndexes = new HashMap<byte[], Long>(NUMBER_OF_CITIES);

Map<Long, byte[]> airports = new HashMap<Long, byte[]>(NUMBER_OF_AIRPORTS);
Map<Long, byte[]> cities = new HashMap<Long, byte[]>(NUMBER_OF_CITIES);

long[][] airportToCitiesMappings = new byte[NUMBER_OF_AIRPORTS][];
long[][] citiesToAirportMappings = new byte[NUMBER_OF_CITIES][];


public List<byte[]> getCitiesNearAirport(byte[] airportName) {
   Long[] cityIndexes = getCitiesByIdxNearAirport(airportName);
   List<byte[]> cities = new ArrayList<byte[]>(cityIndexes.length);
   for(Long cityIdx : cityIndexes) {
       cities.add(cities.get(cityIdx));
   }
   return cities;
}
public long[] getCitiesByIdxNearAirport(Long airportIdx) {
   return airportToCitiesMappings[airportIdx];
}
public long[] getCitiesNearAirport(byte[] airportName) {
   return getCitiesNearAirport(airportIndexes.get(airportName));
}
public long[] getCitiesNearAirport(Long airportIdx) {
   return airportToCitiesMappings[airportIdx];
}
// .. repeat above pattern for airports.

这应该会给你 O(1) 的时间性能特征。在空间方面存在相当大的冗余。

【讨论】:

  • 谢谢。方法很少出现问题 1) airportIndexes Map 将始终返回 null,因为 hashMap 不会认为 2 字节数组具有相同的值。 2) Long 和 long 等之间的转换。我在想我可以只创建 1 个多维数组,其中城市+机场作为维度 1。我们不在乎输入是机场还是城市......我们只需要返回对应的映射。因此,如果输入是城市,则返回该城市的所有机场,如果输入是机场,则返回该机场。在这种情况下,我们可以避免单独搜索城市和机场。有什么想法吗?
【解决方案2】:

你不需要字节数组,字符串就可以了。

您多久将项目添加到此缓存?我猜这完全是静态的,因为他们不是每天都在建造新的城市或机场。

所以,你可以做的是使用两个 MultiHashMap,一个键控城市,另一个键控机场。结帐 Google Multimap http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Multimap.html

如果您有机会使用 mySQL,您实际上可以使用基于 Memory Storage Engine 的表。

许多数据库可以将表固定在内存中,Oracle 肯定可以,所以这是另一种方法。

【讨论】:

  • 感谢您的回复。正如我所说,我必须使用字节数组作为查询缓存的方式。界面无法更改。是的,我可以将它存储为字符串,但这会涉及字符串和字节之间的转换开销。不,由于性能超出预期,我不能使用 DB。
【解决方案3】:

尝试接近 1,因为 byte[] 是一个 Object 类型,你可以使用类似的东西:

Map<byte[], List<byte[]>> cache = ... 

这可能是最简单的方法,您只需要为您的地图选择实现。可能您应该使用 HashMap,因为它是最简单的...

正如 gustavc 所说,使用 HashMap 是行不通的,所以您可以使用带有给定比较器的 TreeMap:

Map<byte[], List<byte[]>> m = new TreeMap<byte[], List<byte[]>>(new Comparator<byte[]>() {
    public int compare(byte[] o1, byte[] o2) {
        int result = (o1.length < o2.length ? -1 : (o1.length == o2.length ? 0 : 1));
        int index = 0;
        while (result == 0 && index < o1.length) {
            result = (o1[index] < o2[index] ? -1 : (o1[index] == o2[index] ? 0 : 1));
            index++;
        }
        return result;
    }
});

【讨论】:

  • 数组的哈希码是基于数组对象的标识,而不是数组内容。以下方法不起作用:byte[] a = {1}, b = {1}; map.put(a, someValue); assert map.get(b) == map.get(a);
【解决方案4】:

这就是我到目前为止所做的:

private static byte[][][] cache = null; // this is the actual cache
// this map has ByteArrayWrapper(a wrapper over byte[]) as key which
//  can be an airport or city and index of corresponding 
// airport/airports in byte[][][]cache as value
Map<ByteArrayWrapper, Integer> byteLocationIndexes = null;
/**
* This is how cache is queried. You can pass an airport or city as a location parameter
* It will fetch the corresponding airport/airports
*/
private byte[][] getAllAirportsForLocation(ByteArrayWrapper location) {
    byte[][] airports = null;
    airports = byteLocationIndexes.get(location)== null ? null : cache[byteLocationIndexes.get(location).intValue()];
    return airports;
}

我使用 String 作为 indexMap 中的键(并使用 String[][] 缓存)和 ByteArrayWrapper 作为键(以及 byte[] 作为缓存)对性能进行基准测试。如果我使用 ByteArrayWrapper 和 byte[][][] 缓存,会有 15-20% 的改进。

还可以做些什么来提高性能?如果我使用其他地图实现会有帮助吗?由于缓存只加载一次并且从不更改,因此可以对其进行排序。大部分时间都花在了 byteLocationIndexes 中的键查找上,这就是瓶颈。我已经在创建对象时计算 hashCode 并将其作为局部变量存储在 ByteArrayWrapper 中。

有什么建议吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-16
    • 2014-09-09
    • 2018-03-31
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-12
    相关资源
    最近更新 更多