【问题标题】:Ambiguities while converting List<Object[]> to List<Integer>将 List<Object[]> 转换为 List<Integer> 时的歧义
【发布时间】:2018-03-06 07:08:09
【问题描述】:

我们试图通过SQL 查询从数据库中获取结果,使用hibernate,如果记录存在,它将返回List&lt;Object[]&gt;。但后来我们想把它转换成List&lt;Integer&gt;。因此,我们没有遍历Object[] 列表并继续添加到整数列表,而是尝试了一些东西,示例代码就像

public class ListObjectArrayToListInteger {
    public static void main(String[] args) {
        Object[] ob1 = {1};
        Object[] ob2 = {5};
        Object[] ob3 = {9};
        List objList = new ArrayList();
        objList.add(ob1);
        objList.add(ob2);
        objList.add(ob3);

        //Case - 1
        List<Integer> intList = objList;
        System.out.println(intList);

        //Case - 2          
        List<Integer> intList = new ArrayList<Integer>();
        intList.addAll(objList);
        System.out.println(intList);
    }
}

我们只尝试从查询中获取一列,这就是为什么所有Arrays 都只有一个元素。 Case-1Case-2 都是分别执行的,两种情况下的输出都是一样的,是这样的

[[Ljava.lang.Object;@3e25a5, [Ljava.lang.Object;@19821f, [Ljava.lang.Object;@addbf1]

Case-1中,intList 类型为List&lt;Integer&gt;,指向ListObject[]s 的对象。由于数据类型完全不同,我们期待在运行时出现异常。

Case-2 中,我们明确地将 Object[]s 添加到 List&lt;Integer&gt; 中,并且无一例外地给出了据我所知的输出。

  1. 那么任何人都可以解释为什么我们会得到这样的输出吗?
  2. List&lt;Integer&gt;(在其所有位置都只期望 Integers)如何保存类似 [[Ljava.lang.Object;@3e25a5, [Ljava.lang.Object;@19821f] 的数据?
  3. 为什么 Java 允许用户将数据从原始数据列表添加到通用列表。在我的代码中,如果我将Object[] ob1 = {1, 2, "3", "abc"}; 设为intList,则可以将其添加到intList.addAll(objList);,这是允许的并且可以顺利运行。

【问题讨论】:

  • 袋熊有点兴奋,但你的问题确实是重复的,看看@JBNizet 链接了什么。
  • @tevemadar JBNizet 的链接有助于澄清我对例外情况的担忧,但我的第二个问题呢?
  • 您使用了原始类型,因此允许将任何类型的对象添加到列表中。您将对象数组添加到该列表中。因此,在打印时,它会打印每个对象数组。并且数组的 toString() 方法返回数组的类型(此处为[L),后跟其哈希码(3e25a5)。
  • ArrayList 等通用对象在运行时不能/不能对其元素执行类型检查。泛型只能防止编译时错误。通过使用原始类型,您可以绕过编译时检查。这就是我们不使用原始类型的原因。另见例如docs.oracle.com/javase/tutorial/java/generics/erasure.htmlstackoverflow.com/q/339699/2891664。您还可以查看ArrayList 的代码,并注意它在内部使用了Object[] 数组。

标签: java list data-conversion


【解决方案1】:

@Arun Sudhakaran,你问的三个问题可以这样回答:

问题 1:

案例 1:

你说:"intListList&lt;Integer&gt;的一个类型,用Object[]s "指向List的一个对象。

这是不正确的,因为您没有使用参数&lt;Object[]&gt; 初始化objList;您正在像这样初始化objListList objList = new ArrayList();

通过这样做,objList 将被创建为具有 原始类型List,而不是类型为 Object[]List

(更多关于 原始类型: https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

由于intListList&lt;Integer&gt; 的一种类型,这意味着intList 使用原始类型指向List 的对象,而不是List 的对象类型为Object[]s。

JDK 1.5 以来,泛型已成为 Java 的一部分。为了保持向后兼容性,Java 没有删除 raw-type 的集合初始化。

//Case - 1
List<Integer> intList = objList;

在这种情况下,objList 使用 raw-type 进行初始化。通过将intList 指向objList,编译器不知道objList 集合变量所持有的值的类型。这就是编译器允许您将Object 的数组添加到objList 的原因。

案例 2:

//Case - 2          
List<Integer> intList = new ArrayList<Integer>();
intList.addAll(objList);
System.out.println(intList);

您说:"我们明确地将Object[]s 添加到List&lt;Integer&gt; 并且无一例外地给出输出"

再次,这是不正确的。您正在将 Object[] 类型的 List 添加到 List&lt;Integer&gt; 中,而不是将 Object[]s 添加到 List&lt;Integer&gt; 中。你没有做intList.add(ob1); intList.add(ob2);。如果你这样做了,那么你可以说我们正在将Object[]s 添加到List&lt;Integer&gt;

JDK 1.5 起,您可以使用泛型来初始化集合变量,如下所示:

List<Integer> list = new ArrayList<>(); //or
List<?> list = new ArrayList<>();

之后,当尝试向list 添加元素时,编译器将检查该元素是否具有与list 相同的数据类型或list 的相同子类型。当您将鼠标悬停在 Eclipse 或 intelliJ 等 IDE 中的集合变量上时,您可能会看到类似 List&lt;? extends Integer&gt; 的内容。

但在这里,objList 被初始化为 raw-type 而不是泛型类型。这意味着编译器不知道objList 不是Integer 类型或Integer 子类型。如果是这种情况,编译器不会抛出异常。相反,编译器会遍历列表并给出ob1ob2和`ob3的引用值。

问题 2:

当尝试将objList 的所有元素添加到List&lt;Integer&gt; 中时,编译器将不知道objList 的数据类型,因为objList原始类型。结果,编译器不会抛出异常。相反,编译器将允许用户将此类元素添加到List&lt;Integer&gt;。在尝试遍历List&lt;Integer&gt; 时,您会发现objList 中的元素的参考值是ob1ob2ob3

问题 3:

Java 只允许用户将原始数据列表中的数据添加到通用列表中。这样做是为了支持兼容性,这也是您的代码可以在 JDK 1.4JDK 1.8 中顺利运行而无需更改任何代码的原因。

【讨论】:

    【解决方案2】:

    在休眠状态下,数组中的每个对象都代表一个选定的实体(表)。

    最好将查询限制为所需的结果:

      String hql = "select count(me.grp) from  MyEntity me group by grp";
      TypedQuery<Integer> query = entityManager.createQuery(hql, Integer.class);
      List<Integer> result = query.getResult();
    

    假设您也需要扩展数据用于其他目的:

      List<Integer> result = objList.stream()
              .map(oarr -> oarr[0])
              .map(Integer.class::cast)
              .collect(Collectors.toList());
    

    对于不是 int 而是 A 类的休眠查询 select A a, B b, C c

    List<Object[]> objList = ...
    
    List<Integer> result = objList.stream()
              .map(oarr -> oarr[0])           // Object[] to Object
              .map(A.class::cast)             // Object to A
              .map(A::getId)                  // A to int A.getId()
              .collect(Collectors.toList());
    

    关于代码

    如果 objList 被键入,它不会编译。

        List<Object[]> objList = new ArrayList<>();
    

    没有类型参数,整个泛型类型对于objList 不成立,因此对于intList 也是如此。类型擦除 intList 只是一个对象列表,一切正常。

    【讨论】:

    • 让我担心的不是休眠部分。我想知道的是这种转换是如何可能的,为什么会被允许。期望 int 类型的位置将如何保存十六进制类型的数据。
    • 现在我明白了;这是关于泛型类型的问题,而不是休眠查询。添加说明。
    猜你喜欢
    • 2010-09-06
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多