`

Java编程之数组转换列表的陷阱

    博客分类:
  • Java
阅读更多

一、基本数据类型数组不能作为asList的入参,否则会引起程序的逻辑混乱。

       这段时间在熟悉新的项目,在看项目代码的时候,发现以前的同事很喜欢用Arrays和Collections这两个工具类,实现数组与列表之间的转换。应该说,很多开发的童鞋在实现数组与列表之前转换的时候,都钟情于这两个工具类。鄙人为了理解这两个工具类,特意写了一个demo学习一下。

import java.util.Arrays;
import java.util.List;
public class BadWithAsList {
	@SuppressWarnings("rawtypes")
	public static void main(String[] args) {
       int[] data = {1,2,3,4,5};
       List dataList = Arrays.asList(data);
       System.out.println("列表中元素的数量是:" + dataList.size());
	}
}

       也许,你会说这个很简单,最后输出是5。那么,你就掉进了asList方法的陷阱里面了。事实上,这段程序最后输出的结果是1。

       为什么最后的结果会是1?打开asList方法查询源码便知:

public static <T> List<T> asList(T... a){
	return new ArrayList<T>(a);
}

       从asList的源码可以知道,入参是变长的,而返回值是一个定长的List。再看看这个demo,定义的数组是基本类型的数组,我们知道基本类型是不能泛型化的,要想作为泛型化就必须使用其包装类型。运行这个demo之所以不报错,那是因为数组本身也是一个对象,是可以泛型化的。也就是说这个例子把int类型的数组作为了T的类型,所以转换后在List中只有一个类型为int数组的元素了,可以通过打印结果来验证:

import java.util.Arrays;
import java.util.List;
public class BadWithAsList {
	@SuppressWarnings("rawtypes")
	public static void main(String[] args) {
       int[] data = {1,2,3,4,5};
       List dataList = Arrays.asList(data);
       System.out.println("元素类型:" + dataList.get(0).getClass());
       System.out.println("前后元素是否相等:" + data.equals(dataList.get(0)));
	}
}

运行结果:

元素类型:class [I

前后元素是否相等:true

       看这两行输出信息,很明显列表中的元素是一个int数组,但是元素类型为什么会是“[I”?我们并没有指明是Array类型,这是因为JVM不可能输出Array类型。Array类型是java.lang.reflect包中的,是通过反射访问数组元素的工具类。在Java中任何一个数组的类都是“[I”,根源就是Java并没有定义数组这个类,而是在编译的时候,通过编译器生成的这个类,这是一个特殊的类,在JDK的API中没有任何数组类的信息。

现在将第一个demo的数组修改成对应的包装类即可,运行输出结果:列表中元素的个数:5。

 

二、asList方法产生的List对象不可更改

       为什么这么说呢?先通过一个例子说明一下:

public enum Week {
   SUN,
   MON,
   TUE,
   WED,
   THU,
   FRI,
   SAT
}
import java.util.Arrays;
import java.util.List;
public class NoWriteAfterAsList {
   public static void main(String args[]){
	   Week[] workDays = {Week.MON,Week.TUE,Week.WED,Week.THU,Week.FRI};
	   List<Week> workDayList = Arrays.asList(workDays);
	   workDayList.add(Week.SAT);
	   System.out.println("周六加班:" + workDayList.get(5));
   }
}

运行结果:

Exception in thread "main" java.lang.UnsupportedOperationException

at java.util.AbstractList.add(AbstractList.java:148)

at java.util.AbstractList.add(AbstractList.java:108)

at com.zh.arrays.transfer.list.NoWriteAfterAsList.main(NoWriteAfterAsList.java:10)

       UnsupportedOperationException,不支持List调用add的方法,这和我们以往所掌握的知识矛盾了,咱还是看看asList的源码,直接new 一个ArrayList返回了,要注意这里的ArrayList不是java.util包中的,而是Arrays工具类的内置类,其源码如下:

private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;
        ArrayList(E[] var1) {
            this.a = (Object[])Objects.requireNonNull(var1);
        }
        public int size() {
            return this.a.length;
        }
        public Object[] toArray() {
            return (Object[])this.a.clone();
        }
        public <T> T[] toArray(T[] var1) {
            int var2 = this.size();
            if(var1.length < var2) {
                return Arrays.copyOf(this.a, var2, var1.getClass());
            } else {
                System.arraycopy(this.a, 0, var1, 0, var2);
                if(var1.length > var2) {
                    var1[var2] = null;
                }
                return var1;
            }
        }

        public E get(int var1) {
            return this.a[var1];
        }
        public E set(int var1, E var2) {
            Object var3 = this.a[var1];
            this.a[var1] = var2;
            return var3;
        }
        public int indexOf(Object var1) {
            Object[] var2 = this.a;
            int var3;
            if(var1 == null) {
                for(var3 = 0; var3 < var2.length; ++var3) {
                    if(var2[var3] == null) {
                        return var3;
                    }
                }
            } else {
                for(var3 = 0; var3 < var2.length; ++var3) {
                    if(var1.equals(var2[var3])) {
                        return var3;
                    }
                }
            }
            return -1;
        }

       这里的ArrayList是一个私有的内部静态类,除了Arrays能访问以外,别的类不能访问。从源码中可以看出,这个类并没有提供add方法,查看其父类的源码如下所示:

public boolean add(E e) {
     add(size(), e);
     return true;
}
public void add(int index, E element) {
     throw new UnsupportedOperationException();
}

       父类确实提供了,但是没有具体的实现,所以每个子类需要自己覆写add方法,而Arrays中的ArrayList类型没有覆写,所以在调用的时候会报错。

       最后,建议在初始化List的时候,不要用Arrays.asList方法。除非你敢保证你的List在整个程序中只能用于读操作。

分享到:
评论

相关推荐

    Java各种类型转换md,学习代码

    在Java编程中,类型转换是必不可少的一部分,特别是在处理不同数据类型的交互时。Java提供了两种主要的类型转换方式:自动类型转换(隐式转换)和强制类型转换(显式转换)。以下将详细介绍这两种转换方法以及相关的...

    详解JAVA高质量代码之数组与集合

    6. **基本类型数组转换陷阱** 当尝试将基本类型数组转换为List时,Arrays.asList方法只会将整个数组作为一个对象放入List,而不是将每个元素分别添加。要将数组元素分开,需要使用Integer等包装类或者使用专门的...

    java代码-数组 40 李启强

    - 可以通过ArrayList或ArrayUtils等工具类将数组转换为集合,反之亦然。 13. **数组的遍历优化**: - 使用while循环结合数组索引也可以遍历数组,尤其在需要控制循环条件时。 14. **数组的遍历陷阱**: - 遍历...

    java面试中的陷阱java面试中的陷阱

    在Java面试过程中,面试官往往会通过一些问题来考察应聘者对Java基础知识的理解深度以及在实际编程中解决问题的能力。本文将根据提供的标题、描述及部分内容,整理并归纳出一系列常见的Java面试陷阱,帮助求职者更好...

    Java 编程 :常见问题汇总

    可以通过使用循环边界检查或使用`Arrays.asList()`将数组转换为列表,利用列表的`get()`方法自动检查边界。 ```java int[] arr = {1, 2, 3}; List&lt;Integer&gt; list = Arrays.asList(arr); int element = list.get...

    JAVA2 精要 语言详解与编程指南

    《JAVA2 精要 语言详解与编程指南》是一本深入探讨JAVA编程技术的书籍,旨在帮助读者全面理解和熟练掌握...通过阅读这本书,你不仅可以提升JAVA编程技能,还能了解到最佳实践和常见陷阱,从而提高编程效率和代码质量。

    Java Scjp 陷阱大全

    "Java SCJP 陷阱大全"显然是一份集合了Java编程中常见错误、陷阱和难点的资料,旨在帮助考生或者开发者避免在实际工作中遇到这些问题。以下将详细介绍Java SCJP考试中的一些关键知识点和常见陷阱: 1. **基本语法**...

    java 中文字符串,utf-8编码为byte数组的计算过程

    在Java编程语言中,处理中文字符串时,理解字符与字节之间的转换是非常关键的。本文将深入探讨如何将中文字符串转换成UTF-8编码的字节数组,并解析这一过程中的计算步骤。UTF-8是一种广泛使用的Unicode字符编码,它...

    Java中常见的陷阱题及答案

    Java编程语言虽然强大且广泛应用,但在实践中也存在一些容易让人踩坑的地方。下面我们就来深入探讨一下Java中常见的陷阱及其解决方案。 1. **找奇数的陷阱** ```java public static boolean isOdd(int i){ ...

    Java编程中常见的坑

    使用`Arrays.asList()`方法将数组转换为集合时需要注意,这种方法返回的是一个固定大小的列表,并且不允许修改集合(比如添加或删除元素)。如果尝试修改该集合,将会抛出`UnsupportedOperationException`。 将集合...

    Think in JAVA 编程思想 第四版 清晰扫描版

    《Think in JAVA 编程思想》是Java编程领域的一本经典著作,由Bruce Eckel撰写。第四版作为该书的最新版本,包含了作者对Java语言深入的理解和丰富的编程经验,旨在帮助读者理解面向对象编程的核心理念,并提升编程...

    More Java Pitfalls中文版

    《More Java Pitfalls》是一本深入探讨Java编程中常见错误和陷阱的专业书籍,旨在帮助开发者避免这些潜在的问题,提升代码质量。这篇博文是该书内容的中文版,提供了丰富的实例和解析,帮助读者理解并解决Java编程中...

    java puzzler (java谜题)

    标题中的“Java Puzzler”指的是这些谜题是针对Java编程语言设计的,目的是暴露和解释Java中可能引起混淆或不预期行为的特性。这些谜题通常涉及类、对象、方法、变量、数据类型、控制流等基本概念,也涵盖了更高级的...

    java解惑(java谜题)中文版的

    《Java解惑》是一本专为Java程序员设计的书籍,旨在揭示编程中常见的陷阱、误解和易犯的错误。这本书的中文版使得更多的中国开发者能够深入理解这些“谜题”,提高编程技能。Java Puzzlers是由Java之父James Gosling...

    Java 面试中的陷阱

    在Java面试中,面试官常常会设置一些陷阱,考察候选人的基础知识、理解深度以及问题解决能力。以下是对这些常见面试问题的详细解释: 1. **final, finally, finalize的区别**: - `final`:用于声明类、方法或变量...

    JAVA编程

    ### JAVA编程常见问题详解 ...综上所述,理解“==”与equals的区别、掌握不同类型间的转换方法以及注意常见的编程陷阱对于编写高效、可靠的Java程序至关重要。通过不断的实践和学习,这些问题将会逐渐变得简单易懂。

    V20-Java笔记整理-重要概念和常见陷阱梳理.docx

    ### Java重要概念与常见陷阱梳理 #### 一、Java简介 **1.1 Java体系分类** - **JavaSE(Java Platform Standard Edition)*...通过学习这些内容,可以帮助开发者更好地理解和掌握Java编程语言的核心原理和实践技巧。

    Java+Puzzlers(中英文并且带源码)

    《Java+Puzzlers》是一本深受欢迎的Java编程书籍,它主要探讨了在Java编程中容易被忽视或误解的一些小细节。这本书分为中文和英文两个版本,同时附带源代码,帮助读者深入理解问题并进行实践。"Puzzlers"在这里指的...

    The Java Language Specification (Third Edition)

    《Java语言规范》(第三版)是Java编程领域的一部权威著作,它是Java开发者不可或缺的参考文献。这一版本详尽地定义了Java编程语言的各个方面,涵盖了语法、语义、类型系统、异常处理、内存模型等多个核心概念。下面...

Global site tag (gtag.js) - Google Analytics