【问题标题】:How to return List<E> from Collection<V> where E is contained inside V?如何从 Collection<V> 返回 List<E>,其中 E 包含在 V 中?
【发布时间】:2021-04-03 21:09:41
【问题描述】:

我有一张地图,它在调用Map.values() 时返回以下数据,它返回Collection&lt;V&gt;

[
    Cache.CachedObject(inserted=1617483447407, value=Record(id=10, type=5, timestamp=2021-04-03T08:37:51.312Z)), 
    Cache.CachedObject(inserted=1617483446133, value=Record(id=11, type=6, timestamp=2021-04-03T08:37:51.312Z)), 
    Cache.CachedObject(inserted=1617483445030, value=Record(id=8, type=4, timestamp=2021-04-03T08:37:51.312Z))
]

如何从Collection&lt;V&gt; 返回List&lt;Record&gt;

Cache类的代码如下

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.api.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

public class Cache<K, V> {
    private long timeToLive = 20000L;
    private HashMap<K, V> cacheMap = new HashMap();

    public Cache() {
        if (this.timeToLive > 0L) {
            Thread t = new Thread(() -> {
                while(true) {
                    this.cleanup();
                }
            });
            t.setDaemon(true);
            t.start();
        }

    }

    public void put(K key, V value) {
        synchronized(this.cacheMap) {
            this.cacheMap.put(key, new Cache.CachedObject(value));
        }
    }

    public V get(K key) {
        synchronized(this.cacheMap) {
            Cache<K, V>.CachedObject c = (Cache.CachedObject)this.cacheMap.get(key);
            return c == null ? null : c.value;
        }
    }

    public void remove(K key) {
        synchronized(this.cacheMap) {
            this.cacheMap.remove(key);
        }
    }

    public int size() {
        synchronized(this.cacheMap) {
            return this.cacheMap.size();
        }
    }

    public void clear() {
        synchronized(this.cacheMap) {
            this.cacheMap.clear();
        }
    }

    public Collection<V> values() {
        synchronized(this.cacheMap) {
            return this.cacheMap.values();
        }
    }

    public void cleanup() {
        long now = System.currentTimeMillis();
        ArrayList deleteKey;
        synchronized(this.cacheMap) {
            Iterator<Entry<K, V>> itr = this.cacheMap.entrySet().iterator();
            deleteKey = new ArrayList(this.cacheMap.size() / 2 + 1);

            while(itr.hasNext()) {
                Entry<K, V> entry = (Entry)itr.next();
                K key = entry.getKey();
                V cached = entry.getValue();
                if (cached != null && now > ((Cache.CachedObject)cached).inserted + this.timeToLive) {
                    deleteKey.add(key);
                }
            }
        }

        for(Iterator var4 = deleteKey.iterator(); var4.hasNext(); Thread.yield()) {
            K key = var4.next();
            synchronized(this.cacheMap) {
                this.cacheMap.remove(key);
            }
        }

    }

    public String toString() {
        return "Cache(timeToLive=" + this.timeToLive + ", cacheMap=" + this.cacheMap + ")";
    }

    private class CachedObject {
        public long inserted = System.currentTimeMillis();
        public V value;

        protected CachedObject(V value) {
            this.value = value;
        }

        public String toString() {
            return "Cache.CachedObject(inserted=" + this.getInserted() + ", value=" + this.getValue() + ")";
        }

        public long getInserted() {
            return this.inserted;
        }

        public V getValue() {
            return this.value;
        }
    }
}

【问题讨论】:

  • 你能分享Cache.CachedObject的来源吗?
  • Cache的代码无法编译,错误很多,这肯定是XY problem,因为CachedObject没有被声明为泛型,内部map没有V类型对于它的值,但CachedObject&lt;V&gt;,并且必须更改它以使代码编译。大量的synchronized 块应该替换为线程安全的映射,例如ConcurrentHashMap,或者至少通过应用Collections.synchronizedMap
  • @AlexRudenko 不确定您遇到了什么错误。你在设计问题上是对的。这是库类之一,我现在实现了自己的。然而,给我同步块而不是一些线程安全集合的原因是缓存检索应该是恒定时间。不过,我对此毫无疑问。无论如何,非常感谢您将我推向正确的方向。
  • 立即出现的错误:方法Cache::put:incompatible types: Cache.CachedObject cannot be converted to V this.cacheMap.put(key, new Cache.CachedObject(value));方法Cache::cleanUp:error: incompatible types: Object cannot be converted to K K key = var4.next();

标签: java collections java-8 java-stream


【解决方案1】:

更新

要解决Cache /CachedObject 发布的代码中的多个编译和设计问题,需要应用以下修复(但它们不是最终的,可能会进一步改进):

  • 使内部类CachedObject泛型
  • HashMap 替换为ConcurrentHashMap(这样synchronized 块可以被移除)并将此映射中的值类型固定为CachedObject&lt;V&gt;
  • 重构cleanUp方法

一个示例实现

public class Cache<K, V> {
    private long timeToLive = 20000L;
    private Map<K, CachedObject<V>> cacheMap = new ConcurrentHashMap<>();

    public Cache() {
        if (this.timeToLive > 0L) {
            Thread t = new Thread(() -> {
                while(true) {
                    this.cleanup();
                }
            });
            t.setDaemon(true);
            t.start();
        }
    }

    public void put(K key, V value) {
        this.cacheMap.put(key, new CachedObject(value));
    }

    public V get(K key) {
        CachedObject<V> c = this.cacheMap.get(key);
        return c == null ? null : c.value;
    }

    public void remove(K key) {
        this.cacheMap.remove(key);
    }

    public int size() {
        return this.cacheMap.size();
    }

    public void clear() {
        this.cacheMap.clear();
    }

    public Collection<V> values() {
        return this.cacheMap.values().stream()
            .map(CachedObject::getValue).collect(Collectors.toList());
    }

    public void cleanup() {
        if (!this.cacheMap.isEmpty()) {
            long now = System.currentTimeMillis();
            this.cacheMap.entrySet().removeIf(e -> null == e.getValue() || now > e.getValue().inserted + this.timeToLive);
        }
        Thread.yield();
    }

    public String toString() {
        return "Cache(timeToLive=" + this.timeToLive + ", cacheMap=" + this.cacheMap + ")";
    }

    private class CachedObject<V> {
        public long inserted = System.currentTimeMillis();
        public V value;

        protected CachedObject(V value) {
            this.value = value;
        }

        public String toString() {
            return "Cache.CachedObject(inserted=" + this.getInserted() + ", value=" + this.getValue() + ")";
        }

        public long getInserted() {
            return this.inserted;
        }

        public V getValue() {
            return this.value;
        }
    }
}

通过这个实现,Cache::values() 方法提供了一个适当的 V 类型元素集合,这些元素被复制到一个列表中,因此只需转换为 List 就足够了:

Cache<String, Record> cache = new Cache<>();
cache.put("#1", new Record(1));
cache.put("#2", new Record(2));
cache.put("#3", new Record(3));

System.out.println(cache);

List<Record> records = (List<Record>) cache.values();
System.out.println(records);

System.out.println(records);

Thread.sleep(2_100L);

List<Record> noRecords = (List) cache.values();
System.out.println(noRecords);

输出

Cache(timeToLive=2000, cacheMap={#3=Cache.CachedObject(inserted=1617530470001, value=Record{id=3}), #1=Cache.CachedObject(inserted=1617530470001, value=Record{id=1}), #2=Cache.CachedObject(inserted=1617530470001, value=Record{id=2})})
[Record{id=3}, Record{id=1}, Record{id=2}]
[]

【讨论】:

  • 试试这个,它会返回我提供的日志。实际上 CachedObject 中的“值”是一个泛型。 public V getValue() {return this.value;} 。尝试了多种方法来提取但失败
  • .map(CachedObject::getValue).map(Record.class::cast) 然后。
  • @AshishDeshpande 我没有看到任何日志,也没有看到CachedObject 的代码。是否用作CachedObject&lt;Record&gt;
  • @AlexRudenko 提供代码。 @JoopEggen 也试过得到reason: no instance(s) of type variable(s) exist so that V conforms to Cache&lt;K, V&gt;.CachedObject 得到编译时错误,需要Required type: Function Provided: &lt;method reference&gt;
  • @AshishDeshpande 整个类 Cache 在其当前状态下存在多个设计问题。
【解决方案2】:

如果您不介意使用Eclipse Collections(顺便说一句,这是一个很棒的库,但是一个额外的依赖项......),您可能想要使用以下内容

List<String> valuesList = Lists.mutable
                               .ofAll(values)
                               .collect(CachedObject::getValue);

【讨论】:

    【解决方案3】:

    我解决了。决定实现库 Cache 类,对 values 方法稍作改动。

    public Collection<V> values() {
            synchronized (cacheMap) {
                return cacheMap
                        .values()
                        .stream()
                        .map(cache -> ((CachedObject) cache).getValue())
                        .collect(Collectors.toList());
            }
        }
    

    如果它是正确的方法,请有人遵守

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-05
      相关资源
      最近更新 更多