图析:Java集合之ArrayList精炼详解

一、前期基础知识储备

Java中的集合类包含的内容很多而且很重要,很多数据的存储处理(排序,去重,筛选等)都需要通过集合类来完成。集合在Java开发和Android开发中都很常见,可能在实际开发中对某个或者某两个集合类比较熟悉,今天本节文章的目的就是分析Android开发中最为常见的集合类—ArrayList,同时普及下Java集合的类型,相互之间的区别及简单说明下集合和数组的区别

(1)在Java中,集合的分类有两大分支:Collection;Map

Collection和Map中的关系和分支如上图所示,对于集合类的学习,要抓住学习的关键点,要不然类一多,就容易记混了。以下是个人总结的学习集合类的四个要点:

1)是否允许空

2)是否允许重复数据

3)是否有序,有序的意思是读取数据的顺序和存放数据的顺序是否一致

4)是否线程安全

Collection和Map的各自常用到的具体类和两者的简单区别如下图所示:

List、Set、Map是这个集合体系中最主要的三个接口。 List和Set继承自Collection接口。Map也属于集合系统,但和Collection接口不同。

Set不允许元素重复。HashSet和TreeSet是两个主要的实现类。Set 只能通过游标来取值,并且值是不能重复的。

List有序且允许元素重复。ArrayList、LinkedListVector是三个主要的实现类。 ArrayList 是线程不安全的, Vector 是线程安全的,这两个类底层都是由数组实现的; LinkedList 是线程不安全的,底层是由链表实现的

Map 是键值对集合。其中key列就是一个集合,key不能重复,但是value可以重复。HashMap、TreeMap和Hashtable是Map的三个主要的实现类。 HashTable 是线程安全的,不能存储 null 值 HashMap 不是线程安全的,可以存储 null 值。

(2)数组和集合的区别

1)数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型);

2)JAVA集合可以存储和操作数目不固定的一组数据,容量是动态变化的;

3)若程序时不知道究竟需要多少对象,需要在空间不足时自动扩增容量,则需要使用容器类库,array不适用。

数组和集合之间的转换:使用相应的toArray()和Arrays.asList()方法可以回想转换

二、上代码,具体分析ArrayList

前面简单分析了常见的集合类和各自之间的关系,那么接下来我们具体看一下ArrayList,笔者做安卓开发的,所以接触比较多的是ArrayList,比如常见的和ListView、RecyclerView搭配使用,这些时候经常需要使用到ArrayList进行数据的填充。

(1)ArrayList定义即特点

ArrayList是一个其容量能够动态增长的动态数组。它继承了AbstractList,实现了List、RandomAccess, Cloneable,java.io.Serializable。 基本的ArrayList,①可以非常方便的做到随机访问元素;②但是在List中间插入和移除元素时较慢。③ArrayList的操作不是线程安全的!一般在单线程中才使用ArrayList,而在多线程中一般使用Vector(和ArrayList90%相似度,而且是线程安全的)或者同步锁操作。

(2)ArrayList构造方法

   

①默认构造函数,该构造方法构造了一个空的链表。

  ArrayList()

ArrayList list = new ArrayList(); //创建一个空数组链表

②capacity是ArrayList的初始容量大小,当增加数据导致链表溢出时,链表容量会自动添加上一次容量大小的一半,该方法构造了一个指定大小但内容为空的链表。

  ArrayList(int capacity)

ArrayList<String> list = newArrayList<String>();//创建一个空的数组链表,用来存放String类型的对象。

③创建包含collection的ArrayList,该构造方法构造了一个包含指定元素集合的链表,注意,这里的字符E是一个标记,用来表示集合中元素的类型。

   ArrayList(Collection<? extendsE>collection)

ArrayList<Integer> list = newArrayList<Integer>(7);//创建一个指定初始容量的数组链表。

(3)ArrayList常用方法

ArrayList常用方法

对应作用

add(Element e)

增加方法:增加指定元素到链表尾部

add(int index, Element e)

增加方法:增加指定元素到链表指定位置

remove(Element e)

删除方法:删除链表中的指定元素

remove(int index)

删除方法:删除链表中指定位置的元素

removeRange(int start, int end)

删除链表中从某一个位置开始到某一个位置结束的元素

clear()

清空ArrayList

set(int index, E element)

修改方法:将链表中指定位置上的元素替换成新元素

get(int index)

查询方法:获取链表中指定位置处的元素.

size()

返回链表长度(链表包含元素的个数)

ArrayList是用来存储和处理数据的,所以常用的操作方法和数据库的类似,包括增、删、改、查等方法,使用起来都比较简单。

(4)ArrayList遍历方式

①索引值遍历方法

for(int i = 0; i < arrayList.size();i++){

  System.out.print(arrayList.get(i) + " ");

}

②加强for循环遍历方法

for(Integer number : arrayList){

  System.out.print(number + " ");

}

需要说明的是,遍历ArrayList时,通过索引值遍历效率最高,加强for循环遍历次之。

PS:加强for循环是Java5之后引入的一种主要用于数据的增强型for循环,语法格式如下:

for(声明语句 : 表达式){

   //代码句子

}

声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。

表达式:表达式是要访问的数组名,或者是返回值为数组的方法。

(5)ArrayList转换为数组—toArray()的使用

有时候,当我们调用ArrayList中的 toArray(),可能遇到过抛出java.lang.ClassCastException异常的情况,这是由于toArray() 返回的是 Object[] 数组,将 Object[] 转换为其它类型(如,将Object[]转换为的Integer[])则会抛出java.lang.ClassCastException异常,因为Java不支持向下转型。

所以一般更常用的是使用另外的方法进行使用:

①第一种方式(最常用)

 Integer[] integer = arrayList.toArray(new Integer[0]);

②第二种方式(容易理解)

 Integer[] integer1 = newInteger[arrayList.size()];

 arrayList.toArray(integer1);

(6)数组转化为集合—asList()的使用

复杂一点,自己写循环进行遍历:List<String> list=new ArrayList<String>();

                  for(int i=0;i<array.length;i++){ list.add(array[i]);  }

简单一点,调用转化方法:List<String> list=Arrays.asList(array); 

小结:

从上面的几个过程总结一下ArrayList的优缺点。ArrayList的优点如下:

1)ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快;

2)ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素;

不过ArrayList的缺点也十分明显:

1)删除元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能;

2)插入元素的时候,涉及到一次元素复制,如果要复制的元素很多,那么就会比较耗费性能;

因此,ArrayList比较适合顺序添加、随机访问的场景

三、ArrayList线程非安全的解决办法

ArrayList是线程非安全的,这很明显,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。那么我们想要使用ArrayList并且让它线程安全怎么办?一个方法是用Collections.synchronizedList方法把你的ArrayList变成一个线程安全的List,比如:

ArrayList<String> synchronizedList = Collections.synchronizedList(list);

synchronizedList.add("aaa");

synchronizedList.add("bbb");

for (int i = 0; i <synchronizedList.size(); i++){

   System.out.println(synchronizedList.get(i));

}

第二种方法是使用线程安全的和ArrayList有90%相似度的Vector(速度慢,线程安全),常用的方法和ArrayList类似,代码如下:

public static void main(String[] args) {  
    Vector v = new Vector();  
    v.add("xxxx1");  
    v.add("xxxx2");  
    v.add("xxxx3");  
    v.add("xxxx4");  
    v.add("xxxx5");  
    v.set(2, "qqq");  
    v.remove(2);  
  
    System.out.println(v);  
    System.out.println(v.size());  
    System.out.println(v.isEmpty());  
    System.out.println(v.indexOf("xxxx1"));  
    System.out.println(v.get(3));  
    System.out.println(v.contains("xxxx51"));  
    v.clear();  
    System.out.println(v);  
  
}  

文章最后提一下,ArrayList、Vector、LinkedList三者之间的区别,这个也比较弄混:

1)ArrayList和Vector相似度90%,如前文所述,前者访问速度更快,后者线程安全;

2)ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;

    对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处。

    对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间。

猜你喜欢

转载自blog.csdn.net/weixin_41101173/article/details/80155068