Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)
Posted CodeJiao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)相关的知识,希望对你有一定的参考价值。
文章目录
- 1. Arrays.asList()正确使用
- 3. 不要在 foreach 循环里进行元素的 remove / add 操作
1. Arrays.asList()正确使用
1.1 简介
Arrays.asList()
在平时开发中还是比较常见的,我们可以使用它将一个数组转换为一个List
集合。
示例:
String[] myArray = "Apple", "Banana", "Orange";
List<String> myList = Arrays.asList(myArray);
System.out.println(myList);
结果:
上面两个语句等价于下面一条语句
List<String> myList = Arrays.asList("Apple", "Banana", "Orange");
JDK
源码对于这个方法的说明:
/** *返回由指定数组支持的固定大小的列表。
此方法作为基于数组和基于集合的API之间的桥梁,
与 Collection.toArray()结合使用。返回的List是可序列化并实现
RandomAccess接口。 */
public static <T> List<T> asList(T... a)
return new ArrayList<>(a);
1.2 《阿里巴巴Java 开发手册》对其的描述
1.2.1 第一种情况
示例:
List<String> myList = Arrays.asList("Apple", "Banana", "Orange");
myList.add("Peach");
结果:
1.2.2 第二种情况
示例:
String[] myArray = "Apple", "Banana", "Orange";
List<String> myList = Arrays.asList(myArray);
myArray[0] = "Durian";
System.out.println(myList);
结果:
1.3 Arrays.asList()使用时的注意事项总结
1.3.1 传递的数组必须是对象数组,而不是基本类型。
Arrays.asList()
是泛型方法,传入的对象必须是对象数组。
示例:
int[] myArray = 1, 2, 3;
List myList = Arrays.asList(myArray);
System.out.println(myList.size());//1
System.out.println(myList.get(0));//数组地址值
System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException
int[] array = (int[]) myList.get(0);
System.out.println(array[2]);//3
当传入一个原生数据类型数组时,Arrays.asList()
的真正得到的参数就不是数组中的元素,而是数组对象本身!此时List
的唯一元素就是这个数组,这也就解释了上面的代码。
1.3.2 使用包装类型数组解决问题
Integer[] myArray = 1, 2, 3;
List myList = Arrays.asList(myArray);
System.out.println(myList.size());// 3
System.out.println(myList.get(0));// 1
System.out.println(myList.get(1));// 2
1.3.3 使用集合的修改方法:add()、remove()、clear()会抛出异常
1.3.4 Arrays.asList() 方法返回的并不是 java.util.ArrayList
Arrays.asList()
方法返回的并不是 java.util.ArrayList
,而是 java.util.Arrays
的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。
示例:
List myList = Arrays.asList(1, 2, 3);
System.out.println(myList.getClass());//class java.util.Arrays$ArrayList
结果:
下图是java.util.Arrays$ArrayList
的源码,我们可以看到这个类重写的方法有哪些。
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array)
a = Objects.requireNonNull(array);
@Override
public int size()
return a.length;
@Override
public Object[] toArray()
return a.clone();
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a)
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
@Override
public E get(int index)
return a[index];
@Override
public E set(int index, E element)
E oldValue = a[index];
a[index] = element;
return oldValue;
@Override
public int indexOf(Object o)
E[] a = this.a;
if (o == null)
for (int i = 0; i < a.length; i++)
if (a[i] == null)
return i;
else
for (int i = 0; i < a.length; i++)
if (o.equals(a[i]))
return i;
return -1;
@Override
public boolean contains(Object o)
return indexOf(o) != -1;
@Override
public Spliterator<E> spliterator()
return Spliterators.spliterator(a, Spliterator.ORDERED);
@Override
public void forEach(Consumer<? super E> action)
Objects.requireNonNull(action);
for (E e : a)
action.accept(e);
@Override
public void replaceAll(UnaryOperator<E> operator)
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = 0; i < a.length; i++)
a[i] = operator.apply(a[i]);
@Override
public void sort(Comparator<? super E> c)
Arrays.sort(a, c);
我们再看一下java.util.AbstractList
(java.util.Arrays$ArrayList
的父类)的remove()
方法,这样我们就明白为啥会抛出UnsupportedOperationException
异常。
1.4 正确的将数组转换为ArrayList
1.4.1 自己动手实现(学习阶段)
示例:自定义方法
static <T> List<T> arrayToList(final T[] array)
final List<T> arrayList = new ArrayList<T>(array.length);
for (final T s : array)
arrayList .add(s);
return (arrayList);
使用:
Integer[] myArray = 1, 2, 3;
List<Integer> integerList = arrayToList(myArray);
System.out.println(integerList);// [1, 2, 3]
System.out.println(integerList.getClass());//class java.util.ArrayList
结果:
1.4.2 最简便的方法(推荐)
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
1.4.3 使用 Java8 的Stream(推荐)
1.4.4 使用 Guava(推荐)
不可变集合:
对于不可变集合,你可以使用ImmutableList
类及其of()
与copyOf()
工厂方法:(参数不能为空)
可变集合:
对于可变集合,你可以使用Lists
类及其newArrayList()
工厂方法:
1.4.5 list自带的addAll方法
示例代码:
String[] strs = new String[]"H", "E", "L", "L", "O";
final List<String> arrayList = new ArrayList<>(strs.length);
arrayList.addAll(Arrays.asList(strs));
System.out.println(arrayList);
运行结果:
1.4.6 使用 Apache Commons Collections
先导入相关依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
测试代码:
String[] str = "a", "b", "c";
List<String> list = new ArrayList<>();
CollectionUtils.addAll(list, str);
System.out.println(list);
结果:
2. Collection.toArray()正确使用、反转数组
该方法是一个泛型方法:<T> T[] toArray(T[] a);
如果toArray
方法中没有传递任何参数的话返回的是Object
类型数组。
由于JVM
优化,new String[0]
作为Collection.toArray()
方法的参数现在使用更好,new String[0]
就是起一个模板的作用,指定了返回数组的类型,0
是为了节省空间,因为它只是为了说明返回的类型。
代码:
String[] s = new String[]"dog", "lazy", "a", "over", "jumps", "fox", "brown", "quick", "A";
List<String> list = Arrays.asList(s);
Collections.reverse(list);// 反转数组
s = list.toArray(new String[0]);//没有指定类型的话会报错 java.lang.Object[]无法转换为java.lang.String[]
System.out.println(Arrays.toString(s));
结果:
3. 不要在 foreach 循环里进行元素的 remove / add 操作
如果要进行remove
操作,可以调用迭代器的 remove
方法而不是集合类的 remove
方法。因为如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove / add
方法,迭代器都将抛出一个ConcurrentModificationException
,这就是单线程状态下产生的 fail-fast
机制。
fail-fast 机制 :多个线程对 fail-fast 集合进行修改的时,可能会抛出ConcurrentModificationException(并发修改异常),单线程下也会出现这种情况,上面已经提到过。
java.util
包下面的所有的集合类都是fail-fast
的,而java.util.concurrent
包下面的所有的类都是fail-safe
的。
3.1 正确示例
示例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext())
String item = iterator.next();
if (item.equals("2"))
// 删除当前的item元素
iterator.remove();
System.out.println(list);
运行结果:
3.2 错误示例
代码:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list)
if ("1".equals(item))
list.remove(item);
System.out.println(list);
运行结果:
以上是关于Java集合 -- 疑难点总结(Arrays.asList()正确使用Collection.toArray()正确使用反转数组 foreach 循环不要进行元素的 remove/add 操作)的主要内容,如果未能解决你的问题,请参考以下文章
java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定
Java基础 -- 疑难点总结(equls的正确姿势整型包装类值的比较BigDecimal的正确姿势基本数据类型与包装数据类型的使用标准)P3C插件(阿里规范插件)