写点什么

在 ArrayList 使用冒泡法

用户头像
sinsy
关注
发布于: 2021 年 01 月 25 日
在 ArrayList 使用冒泡法

前言

前几天接到一个类似版本控制的需求,其中某个元素需要排在最后面。遇到问题有点意思,在实现的过程中出现了元素的重复。

实现过程

由于我需要将某个元素排到最后面,第一想法使用冒泡法,将需要的元素依次与后面的元素做一个交换,完成冒泡排序。


假设 en 你需要转移的元素,完成与 2 、3 这两元素的交换。

正常来说,我们是这样使用的冒泡法的。

    String[] str = new String[]{"1", "en", "2", "3"};
String temp;
for (int i = 0; i < str.length; i++) { if ("en".equals(str[i])) { for (int j = i; j < str.length - 1; j++) { temp = str[j]; str[j] = str[j + 1]; str[j + 1] = temp; } break; } }
复制代码

但是现在容器使用的不是数组,而是 list。使用类似这样的方式就出了点差错。

这个是实现的第一版

    List<String> list = new ArrayList<>();
list.add("1"); list.add("en"); list.add("2"); list.add("3");
int size = list.size();
String temp1;
String temp2;
for (int i = 0; i < size; i++) { if ("en".equals(list.get(i))) { for (int j = i; j < size - 1; j++) { temp1 = list.get(j); temp2 = list.get(j + 1); list.add(j, temp2); list.add(j + 1, temp1);
} break; } }

复制代码

出乎意料的是,输出结果是

12enenenenenen23
复制代码

和理想差距很大,正常需要结果应该是 1 2 3 en 。但是看第一版输出结果,复制很多相同元素,感觉是 ArrayLst 添加的时候有问题。所以点进去看看,验证自己的猜测。

    // jdk 11 版本    /**     * Inserts the specified element at the specified position in this     * list. Shifts the element currently at that position (if any) and     * any subsequent elements to the right (adds one to their indices).     *     * @param index index at which the specified element is to be inserted     * @param element element to be inserted     * @throws IndexOutOfBoundsException {@inheritDoc}     */    public void add(int index, E element) {        // 检查是否越界        rangeCheckForAdd(index);        // 记录修改次数        modCount++;        final int s;        Object[] elementData;        // 初始化容器        if ((s = size) == (elementData = this.elementData).length)            elementData = grow();        // 数组复制        System.arraycopy(elementData, index,                         elementData, index + 1,                         s - index);        elementData[index] = element;        // 容器容量加一        size = s + 1;    }
复制代码

add 源码里有一个 System.arraycopy 以及 全局的 size,前者是数组复制的时候,index 到 index + 1 复制,这也是为什么,当 index = 2 的时候 val = "en" ,复制的时候复制了这个到 index = 3 元素相同。后者进行一次 add 已使用的容器大小就会加一。

根据这两个我们可以模拟出第一次实现的时候出现相同元素、以及 size 变大的问题。

    String[] str = new String[16];
str[0] = "1"; str[1] = "en"; str[2] = "2"; str[3] = "3";
System.arraycopy(str, 1, str, 2, 14); System.arraycopy(str, 2, str, 3, 12); for (String s : str) { System.err.println(s); }
复制代码

输出

1enenen23nullnullnullnullnullnullnullnullnullnull
复制代码

解决

既然知道问题是由 System.arraycopy 以及 size 引起的数组复制、容器变大的问题,那就好解决了。

  • 解决重复元素的出现,同时保证下次交换是正确的

  • 保证容器不在变大

所以,第一种解决方案。

    List<String> list = new ArrayList<>();
list.add("1"); list.add("en"); list.add("2"); list.add("3");
int size = list.size();
String temp1;
String temp2;
for (int i = 0; i < size; i++) { if ("en".equals(list.get(i))) { for (int j = i; j < size - 1; j++) { temp1 = list.get(j); temp2 = list.get(j + 1); list.remove(temp1); list.remove(temp2); list.add(j, temp2); list.add(j + 1, temp1);
} break; } }
复制代码

复制多的元素要及时删除,这样可以有效避免 System.arraycopy 复制的时候多出来的元素。

第二种,避免使用到 System.arraycopy 这样的方法,使用 list.set 直接进行交换。

    List<String> list = new ArrayList<>();
list.add("1"); list.add("en"); list.add("2"); list.add("3");
int size = list.size();
String temp1;
String temp2;
for (int i = 0; i < size; i++) { if ("en".equals(list.get(i))) { for (int j = i; j < size - 1; j++) { temp1 = list.get(j); temp2 = list.get(j + 1); list.set(j, temp2); list.set(j + 1, temp1);
} break; } }
复制代码

总结一下

感觉看源码还是非常有用,无论是在工作中寻找 BUG 的产生还是解决 BUG 等问题。

另外感觉自己对 JDK 代码还不是很熟悉,应该把常用容器的源码都读一下,在使用的时候,可以避免很多坑。

声明

作者: Sinsy 

本文链接:https://blog.sincehub.cn/2021/01/23/at-arraylist-bubble/

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版 权协议,转载请附上原文声明。

如您有任何商业合作或者授权方面的协商,请给我留言:550569627@qq.com


用户头像

sinsy

关注

还未添加个人签名 2019.10.18 加入

公众号:编程的那些年 个人博客网站:https://blog.sincehub.cn/

评论

发布
暂无评论
在 ArrayList 使用冒泡法