简体   繁体   中英

How to use computeIfAbsent() for caching data in a HashMap?

I have separate directories containing files with properties for classes and directories with properties for their embedded object types. First properties for the class are loaded, then loadEmbeddedTypeProps() method walks through fields of a class and if a field of a certain type (I used Enumerated in the example below) is found then properties are loaded for the type as well from another directory. And finally properties for the class and for its embedded types are merged mergeProperties() method is called.

I decided to cache properties for embedded types in a static cachedProperties HashMap. If it already contains properties for a certain key then we get them from cachedProperties and merge with a locaProperties HashMap. If not, we're downloading the properties from a file ( loadPropsForType() is called).

The logic works as expected however it looks like the code in the loadEmbeddedTypeProps() method may be improved by using computeIfAbsent method. I've recently started to experiment with lambdas and not sure how this may be implemented here. Also is it a suitable spot to use computeIfAbsent ?

private static Map<String, Map<String, Properties>> cachedProperties = new HashMap<>();

            private Map<String, Properties> loadEmbeddedTypeProps(Class myClass) {
                Map<String, Properties> localProperties = new HashMap<>();
                Arrays.stream(myClass.getFields())
                        .filter(field -> field.getType().isAssignableFrom(Enumerated.class))
                        .forEach(field -> {
                            String fieldName = field.getType().getSimpleName();
                            String enumTypeName = StringUtils.uncapitalize(fieldName);
                            try {
                                if (cachedProperties.containsKey(enumTypeName)) {
                                 // properties for enumerated type are already in cache
                                    Map<String, Properties> propertiesFromCache = cachedProperties.get(enumTypeName);
                                    mergeProperties(propertiesFromCache, localProperties);
                                } else {
                                // properties for enumerated type are not cached yet
                                    Map<String, Properties> loadEnumProperties = loadPropsForType(enumTypeName);
                                    cachedProperties.put(enumTypeName, loadEnumProperties);
                                    mergeProperties(loadEnumProperties, localProperties);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        });
                return localProperties;
            }

Yes, computeIfAbsent() is appropriate here. You could use it as follows:

Map<String, Properties> properties =
      cachedProperties.computeIfAbsent(enumTypeName, this::loadPropsForType);
mergeProperties(properties, localProperties);

Assuming loadPropsForType is an instance method of the enclosing class. Whatever it is, replace the method reference appropriately. The type of the method reference here is a sub-type of Function<? super String, ? extends Map<String, Properties>> Function<? super String, ? extends Map<String, Properties>> Function<? super String, ? extends Map<String, Properties>> , as per the documentation of computeIfAbsent .

You can use computeIfAbsent , but since you can't throw a checked exception from a Function<...> you'd have to put the try catch inside the lambda you pass to computeIfAbsent .

.forEach(field -> {
    String fieldName = field.getType().getSimpleName();
    String enumTypeName = StringUtils.uncapitalize(fieldName);
    Map<String, Properties> enumProperties 
        = cachedProperties.computeIfAbsent(enumTypeName, key -> {
                try {
                    return loadPropsForType(key);
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                }
            });
    mergeProperties(enumProperties, localProperties);
});

Returning null tells computeIfAbsent that no mapping should be made.

If the mapping function returns null, no mapping is recorded.

So if an exception is thrown that field is basically just ignored. Just be aware that the returned enumProperties will also be null , so you will have to deal with that in mergeProperties .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM