【问题标题】:How to generate a Map backed by a List如何生成由列表支持的地图
【发布时间】:2017-05-05 11:21:00
【问题描述】:

我有一个如下所示的数据模型:

class CustomField {
    String key;
    String value;
}

从我可以获取List<CustomField> 实例的API。键在列表中是唯一的,这意味着该集合确实应该是 Map<String, String>。在这个列表上操作很痛苦,因为每个操作都需要迭代来检查现有的键(CustomField 也没有实现 equals)

如何创建由该列表支持的Map<String, String>“视图”,以便使用地图界面对其进行操作?

我想要一个通用方法,例如:<T, K, V> Map<K, V> createMapBackedByList(List<T> list, BiFunction<K, V, T> elementMapper, Function<T, K> keyMapper, Function<T, V> valueMapper) 或类似方法。

它将使用函数在列表元素和映射键和值之间进行映射。

这里重要的是,我希望对地图的更改能够反映在底层列表中,这就是 Streams API 在这里不起作用的原因......

编辑:我无法修改 API 或 CustomField 类。

【问题讨论】:

  • 为什么不直接从List<CustomField> 创建一个Map<String,String>,然后在完成后将其转回List?你真的需要直接改变底层结构吗?
  • 大概你可以通过map.values()得到一个map的值列表。
  • 您可以编写一个新的Map 实现,可能扩展AbstractMap
  • 创建比较器以按自定义字段的键进行排序。然后使用集合的 binarySearch 来查找是否存在特定的 customField。
  • @Jack 这是可能的,但我想要一些可重复使用且不需要我再次写回更改的东西。

标签: java collections wrapper


【解决方案1】:

简单:

自己写

 public class ListBackedMap<K, V> implements Map<K, V> {

在创建时需要某种List&lt;Pair&lt;K,V&gt;&gt;;并“推迟”到那个。当然,这要求您的 CustomField 类实现 Pair 接口。 (你可能也需要发明)

(或者:您的新课程extends AbstractMap&lt;K,V&gt; 以保护您的大部分工作)。

现在你的方法只是返回一个这样的 Map 的实例。

换句话说:我不知道符合您要求的内置包装器。但是自己实现一个应该非常简单。

编辑:鉴于 OP 无法更改 CustomField 类,一个简单的帮助器,例如

 interface <K, V> MapEntryAdapter {
   K getKey();
   V getValue();
 }

将是必需的;以及知道如何从CustomField 的实例中检索键/值的特定实现。在这种情况下,地图将由 List&lt;MapEntryAdapter&lt;K, V&gt;&gt; 支持。

【讨论】:

  • 当然,这是完全可能的,但不是微不足道的,我希望在任何流行的集合框架(例如 Google Guava 或 Apache Commons)中都能找到类似的东西。我无法更改 CustomField,所以 Pair 解决方案是不可能的......
  • 我可以帮助解决 Pair 的问题——因为这种结构并不是真正需要的;我们只是添加了另一个抽象层(这总是有助于解决问题)。查看我的更新。
  • 我听取了您关于扩展 AbstractMap 的建议,最终它实际上非常简单。见stackoverflow.com/a/43805008/940731
【解决方案2】:

我最终尝试自己实现它并基于 AbstractList。这实际上比我第一次更容易......

public class ListBackedMap<T, K, V> extends AbstractMap<K, V> {
    private final List<T> list;
    private final BiFunction<K, V, T> keyValueToElement;
    private final Function<T, K> elementToKey;
    private final Function<T, V> elementToValue;

    public ListBackedMap(List<T> list, BiFunction<K, V, T> keyValueToElement, Function<T, K> elementToKey, Function<T, V> elementToValue) {
        this.list = list;
        this.keyValueToElement = keyValueToElement;
        this.elementToKey = elementToKey;
        this.elementToValue = elementToValue;
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return list.stream()
                .collect(toMap(elementToKey, elementToValue))
                .entrySet();
    }

    @Override
    public V put(K key, V value) {
        V previousValue = remove(key);
        list.add(keyValueToElement.apply(key, value));
        return previousValue;
    }

    public List<T> getList() {
        return list;
    }
}

它的性能不是很好(或线程安全),但它似乎可以很好地完成这项工作。

例子:

List<CustomField> list = getList();
ListBackedMap<CustomField, String, String> map = new ListBackedMap<>(
        list,
        (key, value) -> new CustomField(key, value),
        CustomField::getKey,
        CustomField::getValue);

【讨论】:

  • 很高兴能为您指明一个好的方向。为了“完成”您的示例,您可能希望展示如何实例化该类(假设您正在使用各种 lambdas 到从字段到键/值的“映射”)。至少我;我很好奇你是怎么做到的 ;-)
  • 我想这也适用于 Set,前提是元素相等性基于键。
  • @GhostCat 更新了一个例子:)
猜你喜欢
  • 2013-02-12
  • 2012-11-11
  • 1970-01-01
  • 2017-06-21
  • 2022-12-04
  • 2014-09-09
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多