【问题标题】:Get a paramaterized array from a Collection/Map从 Collection/Map 中获取参数化数组
【发布时间】:2014-03-17 16:05:32
【问题描述】:

我用来将重复对象的列表/数组存储为映射,其中键是对象本身,值是它的多重性。

['d', 'a', 'd', 'd', 'y'] --> {d=3, a=1, y=1}(订单被保留,FWIW)

我这样做既是为了节省字节,也是因为操纵这张计数图让我的生活更轻松

有时我确实需要恢复为列表/数组形式,

{d=3, a=1, y=1} --> ['d', 'd', 'd', 'a', 'y']

显然无法重新创建原始订单,该信息已丢失,但这无关紧要,因为我认为等效的两个列表/数组 如果它们具有相同的大小并共享完全相同的元素(包括相对多重性)

为了从地图到列表,我编写了以下方法:

<T> ArrayList<T> counted2Lst(final LinkedHashMap<T, Integer> map){
    final ArrayList<T> grp=new ArrayList<T>(map.size());  // larger most of the times

    for (final Entry<T, Integer> entry : map.entrySet()) {
        final T obj=entry.getKey();

        for(int i=entry.getValue(); i>0; i--){
            grp.add(obj);
        }
    }

    return grp;
}

预期会做什么:从 Map 中构建一个 ArrayList

当我决定做同样的事情时遇到了问题,但得到的是 T[](不是 Object[])而不是 ArrayList

这是我的第一次尝试:

<T> T[] counted2Arry(final Map<T, Integer> map){
    final ArrayList<T> tmpLst=counted2Lst(map);     // method defined above

    @SuppressWarnings("unchecked")
    T[] arryT=(T[]) java.lang.reflect.Array.newInstance(tmpLst.get(0).getClass(), tmpLst.size());

    return tmpLst.toArray(arryT);
}

据我所知,由于类型擦除,在运行时无法获取 Collection/Map 的参数化类型, 所以要实际创建一个 T 数组,我必须首先获取存储在 tmpLst 中的第一个对象的类(被授予 T 类型) 然后通过 java.lang.reflect.Array.newInstance() 制作一个 T 数组

问题是如果输入映射是空的(这是完全合理和合法的情况),那么就没有对象可以检测到 T 的实际类。

换句话说,我用Java怎么说?

if(map.size()==0)  {    // or  (tmpLst.size()==0)
    // return a zero-sized T[0] (as opposed to a zero-sized Object[])
}

【问题讨论】:

  • 顺便看看toArray方法在java集合中是如何实现的。大多数情况下,它只是将Object[] 转换为T[],因为类型擦除以及除了Ts 之外什么都不会放在数组中的事实。
  • 出于某种原因需要数组吗?数组不能与泛型很好地混合。返回列表总是更方便。客户可以根据需要拨打toArray
  • counted2Lst() 确实返回了一个 List,但我想允许作为选项返回一个 T[](这就是 counted2Arry() 试图做的)。
  • ... continue ... List.toArry() 确实返回 Object[] - 而不是 T[] - List.toArry(T[]) 需要指定数组类型,其中是我试图解决的信息(客户端程序员可能不知道该信息)。事实上,经过几天的调查,我得出的结论是,我的尝试真的很天真,目前 Java 不允许做我想做的事,主要是因为类型擦除;我不知道 Java 8(可能会破坏兼容性)是否允许这样做。

标签: java arrays generics reflection


【解决方案1】:

您真的应该查看 Guava 的 Multiset(或 SortedMultiset)。它基本上完全按照您在代码中所做的工作等等,但在经过生产验证的库中。

这是一个例子:

char[] chars = new char[]{'d', 'a', 'd', 'd', 'y'};

// unsorted version
ImmutableMultiset.Builder<Character> builder = 
  ImmutableMultiset.builder();
for (char c : chars) builder.add(c);
ImmutableMultiset<Character> mset = builder.build();
System.out.println(new ArrayList<>(mset));

// sorted version
ImmutableSortedMultiset.Builder<Character> sortedBuilder =
  ImmutableSortedMultiset.naturalOrder();
for (char c : chars) sortedBuilder.add(c);
ImmutableSortedMultiset<Character> sortedMset = sortedBuilder.build();
System.out.println(new ArrayList<>(sortedMset));

// how to get an array
Object[] array = mset.toArray();
System.out.println(Arrays.toString(array));

编辑:我不建议自己编写方法,但这就是ArrayList.toArray 在 Oracle JDK 7 中的实现方式:

public <T> T[] toArray(T[] a) {
  if (a.length < size)
    // Make a new array of a's runtime type, but my contents:
    return (T[]) Arrays.copyOf(elementData, size, a.getClass());
  System.arraycopy(elementData, 0, a, 0, size);
  if (a.length > size)
    a[size] = null;
  return a;
}

EDIT 2:在上面添加了 Guava 代码示例。我意识到没有办法做 OP 试图做的事情,除非地图至少有一个键,或者你传入一个(可能是空的)T[] 并使用如上所述在整个 JDK 中使用的方法,或者通过至少有Class&lt;T&gt; 可用(请参阅this SO):

public <T> T[] toArray(Class<T> type) {
  int size = // define size of returned array
  T[] ret = (T[]) Array.newInstance(type, size)
  // fill the array
  return ret;
}

【讨论】:

  • (要做这个特别的事情,你只需写new ArrayList&lt;T&gt;(multiset)。)
  • 不确定 toArray(T[]) 提供的代码示例是否有帮助:数组 T[] 的类信息确实保留了有关其参数化类型的信息,而这不会发生,AFAIK,与Map 和 ArrayList。关于 Guava 的 MultiSet(特别是 HashMultiset)我没有看到我要求的方法。确实有 java.util.AbstractCollection.toArray() --> Object[] 和 java.util.AbstractCollection.toArray(T[] a) --> T[],但这不会像我需要的那样工作:a返回 E[] 的方法,但不传递有关参数化类型的任何信息
  • 我认为JDK中使用的方法是唯一可能的。这就是为什么收藏库甚至不尝试做你想做的事情。有关如何使用 MultisetSortedMultiset 的示例,请参阅我的更新答案。
【解决方案2】:

可以获取条目集的大小。

if (map.entrySet().size() == 0) { /* do things */ }

【讨论】:

  • 有问题的部分不是获取地图的大小、它的 entrySet(相同)或派生列表的大小,而是 /* 做的事情 */ 部分
猜你喜欢
  • 2020-06-21
  • 2020-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多