一、Java中数组

数组用来存放固定数量的同类元素,声明方法:

T[] ref,T ref[],如int[] intAry; int intAry[]。推荐用T[]的方式,后一种方式为兼容C++习惯写法。

初始化方法:

new设置数组长度,或者直接列出数组元素,如下:

 // 创建数组,如果在创建的同时不初始化数组则必须指定其大小
intArray0 = new int[3]; // 创建数组时,不指定数组大小则必须在创建的同时初始化数组
intArray1 = new int[]{0,1,2}; // 静态初始化简化方式
int intArray3 [] = {30,31,32};

二、Java 数组和集合之间的关系

世间上本来没有集合,(只有数组参考C语言)但有人想要,所以有了集合
有人想有可以自动扩展的数组,所以有了List
有的人想有没有重复的数组,所以有了set
有人想有自动排序的组数,所以有了TreeSet,TreeList,Tree** 而几乎有有的集合都是基于数组来实现的.
因为集合是对数组做的封装,所以,数组永远比任何一个集合要快 但任何一个集合,比数组提供的功能要多 一:数组声明了它容纳的元素的类型,而集合不声明。这是由于集合以object形式来存储它们的元素。 二:一个数组实例具有固定的大小,不能伸缩。集合则可根据需要动态改变大小。 三:数组是一种可读/可写数据结构---没有办法创建一个只读数组。然而可以使用集合提供的ReadOnly方法,以只读方式来使用集合。该方法将返回一个集合的只读版本。

注:java中可创建只读集合和同步集合,方法如下:

// 只读集合
List<String> readOnlyList = Collections.unmodifiableList(list);
System.out.println(readOnlyList.getClass());
// 同步集合
List<String> synchronizedList = Collections.synchronizedList(list);
System.out.println(synchronizedList.getClass());

如果试图对只读集合进行添加或修改,将会抛出UnsupportedOperationException异常:
如果不希望方法返回的集合被修改,就可以使用只读集合;如果想要获取线程安全的集合,就可以使用同步集合。

三、数组和集合的差异

数组array是固定大小不能动态改变,只能存同一种类型,可传递继承类型,即Number是Integer的父类,则Number[]是Integer[]的父类型,即可将Integer[]当做实参传入形参要求为Number[]的函数中。

集合是对数组能力的扩展,基于数据或链表结构实现,只能存放引用类型,不能存放基本类型;不能进行类型传递,即Listt<Number>不能看作List<Integer>的父类型。

四、数组集合的使用建议

建议65:避开基本类型数组转换列表陷阱

  我们在开发中经常会使用Arrays和Collections这两个工具类和列表之间转换,非常方便,但也有时候会出现一些奇怪的问题,来看如下代码: 

1 public class Client65 {
2 public static void main(String[] args) {
3 int data [] = {1,2,3,4,5};
4 List list= Arrays.asList(data);
5 System.out.println("列表中的元素数量是:"+list.size());
6 }
7 }

  也许你会说,这很简单,list变量的元素数量当然是5了。但是运行后打印出来的列表数量为1。

  事实上data确实是一个有5个元素的int类型数组,只是通过asList转换成列表后就只有一个元素了,这是为什么呢?其他4个元素到什么地方去了呢?

  我们仔细看一下Arrays.asList的方法说明:输入一个变长参数,返回一个固定长度的列表。注意这里是一个变长参数,看源码:

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

  asList方法输入的是一个泛型变长参数,我们知道基本类型是不能泛型化的,也就是说8个基本类型不能作为泛型参数,要想作为泛型参数就必须使用其所对应的包装类型,那前面的例子传递了一个int类型的数组,为何程序没有报编译错误呢?

  在Java中,数组是一个对象,它是可以泛型化的,也就是说我们的例子是把一个int类型的数组作为了T的类型,所以在转换后在List中就只有一个类型为int数组的元素了,我们打印出来看看,代码如下: 

1 public class Client65 {
2 public static void main(String[] args) {
3 int data [] = {1,2,3,4,5};
4 List list= Arrays.asList(data);
5 System.out.println("元素类型是:"+list.get(0).getClass());
6 System.out.println("前后是否相等:"+data.equals(list.get(0)));
7 }
8 }

  输出的结果是: 元素类型是:class [I   前后是否相等:true

  很明显,放在列表中的元素时一个int数组,可能有人要问了,为什么"元素类型:"后的class是"[I"?我们并没有指明是数组(Array)类型呀!这是因为JVM不可能输出Array类型,因为Array是属于java.lang.reflect包的,它是通过反射访问数组元素的工具类。在Java中任何一个一维数组的类型都是 " [I " ,究其原因就是Java并没有定义数组这一个类,它是在编译器编译的时候生成的,是一个特殊的类,在JDK的帮助中也没有任何数组类的信息。

  弄清楚了问题,修改也很easy,直接使用包装类即可,部分代码如下: 

Integer data [] = {1,2,3,4,5};

  把int替换为Integer即可让输出元素数量为5.需要说明的是,不仅仅是int类型的数组有这个问题,其它7个基本类型的数组也存在相似的问题,这就需要大家注意了,在把基本类型数组转换为列表时,要特别小心asList方法的陷阱,避免出现程序逻辑混乱的情况。

  注意:原始类型数组不能作为asList的输入参数,否则会引起程序逻辑混乱。

建议66:asList方法产生的List的对象不可更改

  上一个建议指出了asList方法在转换基本类型数组时存在的问题,接着我们看一下asList方法返回的列表有何特殊的地方,代码如下:  

 1 public class Client66 {
2 public static void main(String[] args) {
3 // 五天工作制
4 Week days[] = { Week.Mon, Week.Tue, Week.Wed, Week.Thu, Week.Fri };
5 // 转换为列表
6 List<Week> list = Arrays.asList(days);
7 // 增加周六为工作日
8 list.add(Week.Sat);
9 /* do something */
10 }
11 }
12 enum Week {
13 Sun, Mon, Tue, Wed, Thu, Fri, Sat
14 }

  很简单的程序呀,默认声明的工作日(workDays)是从周一到周五,偶尔周六也会算作工作日加入到工作日列表中,不过,这段程序执行时会不会有什么问题呢?编译没有任何问题,但是一运行,却出现了如下结果:

  

UnsupportedOperationException,不支持的操作,居然不支持list的add方法,这是什么原因呢?我们看看asList方法的源代码:

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

  直接new了一个ArrayList对象返回,难道ArrayList不支持add方法,不可能呀!可能,问题就出现在这个ArrayList类上,此ArrayList非java.util.ArrayList,而是Arrays工具类的一个内部类,其构造函数如下所示: 

 1 private static class ArrayList<E> extends AbstractList<E>
2 implements RandomAccess, java.io.Serializable
3 {
4 private static final long serialVersionUID = -2764017481108945198L;
5 private final E[] a;
6
7 ArrayList(E[] array) {
8 if (array==null)
9 throw new NullPointerException();
10 a = array;
11 }
12 /*其它方法略*/
13 }

  这个ArrayList是一个静态私有内部类,除了Arrays能访问外,其它类都不能访问,仔细看这个类,它没有提供add方法,那肯定是父类AbstractList提供了,来看代码:  

1  public boolean add(E e) {
2 add(size(), e);
3 return true;
4 }
5
6 public void add(int index, E element) {
7 throw new UnsupportedOperationException();
8 }

  父类确实提供了,但没有提供具体的实现,所以每个子类都需要自己覆写add方法,而Arrays的内部类ArrayList没有覆写,因此add一个元素就报错了。

  我们深入地看看这个ArrayList静态内部类,它仅仅实现了5个方法:

  1. size:元素数量
  2. get:获得制定元素
  3. set:重置某一元素值
  4. contains:是否包含某元素
  5. toArray:转化为数组,实现了数组的浅拷贝

  把这几个方法的源代码展示一下: 

 1  //元素数量
2 public int size() {
3 return a.length;
4 }
5
6 public Object[] toArray() {
7 return a.clone();
8 }
9 //转化为数组,实现了数组的浅拷贝
10 public <T> T[] toArray(T[] a) {
11 int size = size();
12 if (a.length < size)
13 return Arrays.copyOf(this.a, size,
14 (Class<? extends T[]>) a.getClass());
15 System.arraycopy(this.a, 0, a, 0, size);
16 if (a.length > size)
17 a[size] = null;
18 return a;
19 }
20 //获得指定元素
21 public E get(int index) {
22 return a[index];
23 }
24 //重置某一元素
25 public E set(int index, E element) {
26 E oldValue = a[index];
27 a[index] = element;
28 return oldValue;
29 }
30
31 public int indexOf(Object o) {
32 if (o==null) {
33 for (int i=0; i<a.length; i++)
34 if (a[i]==null)
35 return i;
36 } else {
37 for (int i=0; i<a.length; i++)
38 if (o.equals(a[i]))
39 return i;
40 }
41 return -1;
42 }
43 //是否包含元素
44 public boolean contains(Object o) {
45 return indexOf(o) != -1;
46 }

  对于我们经常使用list.add和list.remove方法它都没有实现,也就是说asList返回的是一个长度不可变的列表,数组是多长,转换成的列表也就是多长,换句话说此处的列表只是数组的一个外壳,不再保持列表的动态变长的特性,这才是我们关注的重点。有些开发人员喜欢这样定义个初始化列表: 

    List<String> names= Arrays.asList("张三","李四","王五");

  一句话完成了列表的定义和初始化,看似很便捷,却隐藏着重大隐患---列表长度无法修改。想想看,如果这样一个List传递到一个允许添加的add操作的方法中,那将会产生何种结果,如果有这种习惯的javaer,请慎之戒之,除非非常自信该List只用于只读操作。

针对ArrayList,使用高级for循环实质是通过Iterator来循环效率比基本for循环遍历元素稍差,在数据较多要用到性能时就不能为了简单用高级for循环。

频繁插入和删除元素时用LinkList,修改元素操作多时用ArrayList。

编写高质量代码:改善Java程序的151个建议(第5章:数组和集合___建议65~69)

Java中Array、List、Set、Map的更多相关文章

  1. JAVA 中的 Collection 和 Map 以及相关派生类的概念

    JAVA中Collection接口和Map接口的主要实现类   Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的 ...

  2. Java中集合List,Map和Set的区别

    Java中集合List,Map和Set的区别 1.List和Set的父接口是Collection,而Map不是 2.List中的元素是有序的,可以重复的 3.Map是Key-Value映射关系,且Ke ...

  3. Java中的Set,List,Map的区别

    1. 对JAVA的集合的理解是想对于数组 数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型) JAVA集合可以存储和操作数目不固定的一组数据. 所有的JAVA集合都位于 ja ...

  4. Java中集合List,Map和Set的差别

    Java中集合List,Map和Set的差别 1.List和Set的父接口是Collection.而Map不是 2.List中的元素是有序的,能够反复的 3.Map是Key-Value映射关系,且Ke ...

  5. Java-杂项: Java中Array和ArrayList区别

    ylbtech-Java-杂项: Java中Array和ArrayList区别 1.返回顶部 1. 1)精辟阐述:可以将 ArrayList想象成一种“会自动扩增容量的Array”. 2)Array( ...

  6. JAVA中Collection接口和Map接口的主要实现类

    Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).一些Collection允许相同的元素 ...

  7. Java中的Set, List, Map漫谈

    在编程语言中,集合是指代表一组对象的对象.Java平台专门有一个集合框架(Collections Framework).集合框架是指表示和操作集合的统一架构,隔离了集合的操作和实现细节. 集合框架中的 ...

  8. Java中Array的常用方法

    0.创建/声明一个数组 1 2 3 String[] aArray = new String[5]; String[] bArray = {"a","b",&q ...

  9. 【Java集合的详细研究5】Java中Array与ArrayList的主要区别

    1)精辟阐述:可以将 ArrayList想象成一种“会自动扩增容量的Array”. 2)Array([]):最高效:但是其容量固定且无法动态改变:     ArrayList:  容量可动态增长:但牺 ...

随机推荐

  1. 文档控件NTKO OFFICE 详细使用说明之预览PDF文件(禁止打印、下载、另存为、防抓包下载)

    1.在线预览PDF文件(禁止打印.下载.复制.另存为) (1) 运行环境 ① 浏览器:支持IE7-IE11(平台版本还支持Chrome和Firefox) ② IE工具栏-Internet 选项:将ww ...

  2. 洛谷P2607 [ZJOI2008]骑士(树形dp)

    题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里, ...

  3. (转)RabbitMQ学习之exchange总结

    http://blog.csdn.net/zhu_tianwei/article/details/53969674 前面介绍了几类exchange的作用,这个总结一下: direct:消息会被推送至绑 ...

  4. (转)RabbitMQ学习之安装

    http://blog.csdn.net/zhu_tianwei/article/details/40832185 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客 ...

  5. C# 递归产生树

    //递归产生树 private void CreateTwo(TreeNode node, int id) { #region --根据指定ID查找数据到 dt string strSql = &qu ...

  6. JavaScript 创建对象之单例、工厂、构造函数模式

    01单例模式 首先看一个问题,我们要在程序中描述两个人,这两个人都有姓名和年龄,可能刚刚开始学习js的时候会写成这样: var name1 = 'iceman'; var age1 = 25; var ...

  7. 洛谷 P1328 生活大爆炸版石头剪刀布 模拟

    很简单 Code: #include<cstdio> #include<queue> using namespace std; queue<int>A; queue ...

  8. 洛谷P2038 无线网络发射器选址 水题 枚举

    刚开始边界写错了(将128写成127). 注意n <= 20,所以可以每读入一个点就将其周边更新,这样最多也只会有 40 * 40 * 20 种位置需要被枚举. Code: #include&l ...

  9. Python编码显示中文乱码

    爬虫时出现问题: import requests data=requests.get('http://roll.news.sina.com.cn/')print(data.text) 输出结果中文显示 ...

  10. POJ 3281 Dining[网络流]

    Cows are such finicky eaters. Each cow has a preference for certain foods and drinks, and she will c ...