RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1431454
Accepted
supervitas
supervitas
Asked:2022-07-20 18:56:38 +0000 UTC2022-07-20 18:56:38 +0000 UTC 2022-07-20 18:56:38 +0000 UTC

默认 <T> T[] toArray(T[] array) 方法的实现

  • 772

我正在尝试构建自己的 JCF(玩弄以更好地理解集合)。

我创建了一个MyList<E>实现接口的类,Collection<E>因此有必要实现默认方法retainAll(Collection<?> c)和<T> T[] toArray(T[] array).

好像发生了,但是想请教高手,在方法中是否可以通过<T> T[] toArray(T[] array)其他方式创建一个新的更大的数组?

我试图创建一个新类型Object[],然后将其强制转换为T[],但它崩溃了ClassCastException。

我的决定:

    @Override
    default boolean retainAll(Collection<?> c) {
        removeIf(element -> !c.contains(element));
        return true;
    }

    @Override
    default <T> T[] toArray(T[] array) {
        if (array.length != size()) {
            array = Arrays.copyOf(array, array.length + (size() - array.length));
        }
        for (int i = 0; i < size(); i++)
            array[i] = (T)get(i);
        return array;
    }
java коллекции
  • 1 1 个回答
  • 76 Views

1 个回答

  • Voted
  1. Best Answer
    Михаил Ребров
    2022-07-20T19:50:20Z2022-07-20T19:50:20Z

    以一般形式创建参数化类型(泛型)的数组

    您可以尝试使用反射创建一个数组

    我们有object一个参数化类型(通用)T:

    T object;
    

    使用这些输入,我们可以创建一个给定类型的元素数组,如下所示:

    T[] arr = (T[]) java.lang.reflect.Array.newInstance(
                        object.getClass().getComponentType(), // получаем тип из класса объекта
                        size // указываем размер массива
                    );
    

    toArray 方法的目的

    创建该方法toArray是为了将集合转换为相同类型的数组。
    在这个方法中,我们传递一个对我们将要写入集合数据的数组的引用。
    如果数组中没有足够的空间,那么我们创建一个新的并写入它来代替旧的。
    如果数组中的空间多于所需空间,则我们将空元素 ( null) 作为数据结束标记。

    考虑 toArray 方法的可用实现

    为了正确实现此方法,最好看看他们自己如何建议这样做,使用来自ArrayList和的实现示例LinkedList。

    ArrayList 类中 toArray(T[] a) 的实现

    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;
    }
    

    我提醒您,我们正在考虑 ArrayList 的实现,顾名思义,这是一个List<E>基于数组的接口的实现,这在进一步考虑的上下文中很重要。

    ArrayListelementData 字段用于在类中存储列表的元素。

    首先,我们检查传递的数组是否足够大以将现有元素放入其中。

    if (a.length < size)
    

    如果数组的大小不够,那么使用实用程序类,我们制作一个具有相同大小和传递数组类型的Arrays数组的副本( )elementDataa.getClass()

    return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    

    否则,我们将数组的所有元素复制elementData到数组中а(按计划),从头开始(在这种情况下,使用的方法允许您指定开始复制的位置和复制到的位置,以及这些元素的数量)。我们使用类中
    的实用方法进行复制arraycopySystem

    System.arraycopy(elementData, 0, a, 0, size);
    

    接下来,我们检查传递的数组是否大于我们的列表。

    if (a.length > size)
    

    如果传过来的数组比需要的大,那么我们把null放在复制元素的后面,这样在迭代的时候,可以找到数据的结尾,而不是抓脸的Exception

    a[size] = null;
    

    LinkedList 类中 toArray(T[] a) 的实现

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            a = (T[])java.lang.reflect.Array.newInstance(
                    a.getClass().getComponentType(), size);
        int i = 0;
        Object[] result = a;
        for (LinkedList.Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
    
        if (a.length > size)
            a[size] = null;
    
        return a;
    }
    

    和前面的例子一样,我们首先检查传递的数组的大小是否足够大。

    if (a.length < size)
    

    如果数组的大小不足,则使用反射创建一个具有指定数据类型和所需大小的新数组,并将对它的引用保存在变量中a

    a = (T[])java.lang.reflect.Array.newInstance(
               a.getClass().getComponentType(), size);
    

    我注意到这个实现完全使用了我最初提出的方法。

    与第一个实现不同,在这个阶段我们只有一个足够大小的空数组,而在前一个实现中,我们已经为一个处理过的案例返回了一个新数组。

    接下来,我们需要将所有元素转移到这个数组中。

    首先,我们定义一个变量来存储可迭代元素的索引,因为 在链表中,迭代时,我们只有前一个元素和下一个元素的链接。

    int i = 0;
    

    接下来,我们创建一个变量result并在其中放置对数组的引用。a

    Object[] result = a;
    

    这就是需要解释团队的地方。
    这样做显然是为了不打扰类型转换。
    因此,为了避免问题和麻烦,我们将简单地创建另一个链接,仅已声明为Object[].
    并且我们已经被允许在没有任何类型转换的情况下放置列表的元素。
    事实上,我们将使用相同的数组。
    只是入坑的条件会比较软 :)
    这种带耳朵的佯攻可以在源码中找到。java.lang
    实际上,开发者JDK只是把你列在清单上的责任推给你(他们还应该怎么做?) .

    接下来,我们使用变量将链表的元素复制到一个数组中result

    for (LinkedList.Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
    

    也值得在这里停下来更详细地考虑一切。

    首先,我们将链表节点作为迭代器,并使用对第一个元素的引用来初始化迭代器。

    LinkedList.Node<E> x = first;
    

    作为退出循环的条件,我们使用通常的空值检查。

    x != null;
    

    迟早,它会出现在那里,因为列表中的最后一个元素将具有指向列表中下一个元素的链接,并且等于null,我们将停在那里。

    好吧,作为转换表达式,我们使用获取到链表下一个元素的链接

    x = x.next
    

    因此,反过来,逐个元素地获取,我们将绕过整个列表。

    并获取列表的每个元素,我们将其值放入数组中

    result[i++] = x.item;
    
    • 如前所述,使用了结果变量,但实际上我们使用的是相同的а
    • 我们在一行中获取元素索引的值并将其加一(它允许这样做,为什么不使用它?)

    此外,与第一个变体一样,我们用空元素标记数据的结尾

    if (a.length > size)
        a[size] = null;
    

    并返回一个数组

    您的实施

    首先,我将指出您实施的一些缺点。

    这是复制数组时使用的内容:

    array.length + (size() - array.length)
    

    为什么这么难?
    如果我们省略括号(在这里它们是可选的),那么我们只得到size()( array.length - array.length = 0)

    因为 我没有类的整个实现的代码,我不能为你完全写出你方法的某种终极实现toArray,也不能检查它的性能,但我可以看例子,注意在上述实现中使用的想法和技术,并提出了一些实现建议。

    选项

    您可以用耳朵使用相同的技巧来更改数组的类型并编写如下内容:

    @Override
    default <T> T[] toArray(T[] array) {
        if (array.length != size()) {
            array = Arrays.copyOf(array, size());
        }
        // сохраняем ссылку на массив в переменную с менее строгим типом
        Object[] result = array; 
        for (int i = 0; i < size(); i++) {
            //работаем с подменой без приведения
            result[i] = get(i); 
        }
        // но возвращаем переданную ссылку
        return array;
    }
    

    我对它是否有效感兴趣。
    你怎么试 - 退订!

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5