如何在Java中返回有限数量的缓存实例?

时间:2016-07-19 06:58:10

标签: java enums singleton factory-pattern

我有一个“配置”类,它成为其他几个类的字段。它表示允许或禁止操作的其他类的某种配置或“能力”。现在的配置类包含一组四个独立的布尔值,可能会保持这样 - 或者与另一个bolean一起成长 - 。配置是不可变的:一旦创建了对象,配置就永远不会改变。

public class Configuration {
    private final boolean abilityOne;
    private final boolean abilityTwo;
    private final boolean abilityThree;
    private final boolean abilityFour;

    public Configuration (final boolean abilityOne, final boolean abilityTwo,
                          final boolean abilityThree, final boolean abilityFour) {
    this.configuration = ((1 * (abilityOne ? 1 : 0)) +
            (2 * (abilityTwo ? 1 : 0)) +
            (4 * (abilityThree ? 1 : 0)) +
            (8 * (abilityFour ? 1 : 0)));
 }

    public boolean isAbilityOne() {
        return((1 & this.configuration) > 0);
    }

    public boolean isAbilityTwo() {
        return((2 & this.configuration) > 0);
    }

    public boolean isAbilityThree() {
        return((4 & this.configuration) > 0);
    }

    public boolean isAbilityFour() {
        return((8 & this.configuration) > 0);
    }
}

由于C /有限硬件背景,我的下一个实现(尝试减少内存占用)是使用int作为位图:1 - >第一个布尔值,2->第二,4 - >第三,8->第四。这样我存储一个整数,我需要的布尔函数就像:

它工作正常,内存效率很高。但是我的 Java-all-my-life 同事对此感到不满。

不同配置的数量是有限的(布尔值的组合),但使用它们的对象数量非常大。为了减少内存消耗,我想到了某种“多单例”,枚举或缓存实例。这就是我现在所处的位置。什么是最好的?

4 个答案:

答案 0 :(得分:1)

我建议如下,因为你只需要为你的枚举添加另一种能力,所以很容易扩展。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion '24.0.0'
    defaultConfig {
        applicationId "com.agte.agtevivo"
        minSdkVersion 19
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
        flavor {
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
}

答案 1 :(得分:1)

我认为multiton模式是最有效的方法:

public class Configuration {

    private static Map<Long, Configuration> configurations = new HashMap<>();

    private long key;
    private long value;

    public static Configuration getInstanse(long key, boolean... configs) {
        if (configurations.containsKey(key)) {
            return configurations.get(key).setConfigs(configs);
        }
        Configuration configuration = new Configuration(key, configs);
        configurations.put(key, configuration);
        return configuration;
    }

    // Max number of configs.length is 64
    private Configuration(long key, boolean... configs) {
        this.key = key;
        setConfigs(configs);
    }

    private Configuration setConfigs(boolean[] configs) {
        this.value = 0L;
        boolean config;
        for (int i = 0; i < configs.length; i++) {
            config = configs[i];
            this.value = this.value | (config ? (1L << i) : 0L);
        }
    }

    public long getKey() {
        return key;
    }

    public boolean getConfig(int place) {
        return (value & (1L << place)) == (1L << place);
    }
}

答案 2 :(得分:0)

如果配置实现对象很小并且创建起来不是很昂贵,则无需缓存它们。因为每个怪物对象都必须保持对其每个配置的引用,并且在机器级别,引用是一个指针,并至少使用与int相同的内存。

@gamulf提出的EnumSet方式可以在没有任何缓存的情况下使用,因为根据EnumSet javadoc:

  

枚举集在内部表示为位向量。这种表现非常紧凑和高效。这个类的空间和时间性能应该足以使其成为传统的基于int的“位标志”的高质量,类型安全的替代品。

我没有对它进行基准测试,但是对于@gamulf的解决方案,缓存很可能没用,因为Configuration对象只包含一个只包含int的EnumSet。

如果您有一个繁重的配置类(在内存方面或创建起来很昂贵)并且只有少量可能的配置,您可以在类中使用静态HashSet成员,并使用静态工厂将返回缓存对象的方法:

public class Configuration {
    static Set<Configuration > confs = new HashSet<>();
    ...

    public Configuration (Ability ... abs) {
        ...
    }

    public boolean hasAbility(Ability ab) {
        ...
    }

    static Configuration getConfiguration(Ability ... abs) {
        for (ConfImpl2 conf: confs) {
            if (conf.isSame(abs)) { return conf; }
        }
        ConfImpl2 conf = new ConfImpl2(abs);
        confs.add(conf);
        return conf;
    }
    private boolean isSame(Ability ... abs) {
        // ensures that this configuration has all the required abilities and only them
        ...
    }
}

但正如我已经说过的那样,对于像@gamulf

提出的那些轻量级物体来说,这可能是无用的

答案 3 :(得分:0)

我想根据你的答案分享我所做的调查,所以我会用这些结果发布一个答案。这样,为什么我选择一个答案而不是其他答案可能会更清楚。

裸露的结果排名如下(用于600“怪物”物体的记忆,占所需物品的10%):

  1. 简单选项:里面有四个布尔值的类:22.200.040
  2. 初始选项:具有一个整数作为位图的类:22.200.040
  3. “multiton”选项:一个工厂类,它返回对普通选项的类的引用:4.440.040
  4. EnumSet(没有番石榴缓存):53.401.896(在这一个中我可能搞砸了,因为结果不符合预期......我可能会在以后进一步研究)
  5. 带有guava缓存的EnumSet:4.440.040
  6. 由于我的测试首先进行了一系列比较以确保所有实现都为所有配置提供完全相同的结果,因此很明显4.440.040数字是List&lt;&gt;的大小。我曾经拿过这些项目,因为在我测量内存之前我决定将其设置为null之前,这些数字始终为0

    请不要在我释放每个列表并将其设置为gc(); freeMemory(); 之前和之后测量内存消耗量( null),因为我使用了相同的内容所有方法,每次执行20次执行,执行次数不同。结果对我来说足够一致。

    这些结果指出multiton解决方案是表现最好的解决方案。这就是我将其设置为选定答案的原因。

    作为旁注/好奇心,请注意,此调查开始的项目选择了琐碎的选项作为解决方案,大部分调查是为了满足我自己的好奇心 - 并且有一些隐藏的愿望能够证明其他一些解决方案 soooo 比微不足道的解决方案更有效......但是没有 - 。这就是我花了这么长时间才得出结论的原因。