源码分析篇--Java集合操作(1)

一、集合框架
1、集合框架体系图
在这里插入图片描述
2、集合的概念
 Java集合是使程序能够存储和操纵元素不固定的一组数据。 所有Java集合类都位于java.uti包中。与Java数组不同,Java集合中不能存放基本数据类型,只能存放对象的引用。但是在JDK5.0以后的版本当中,JAVA增加了“自动装箱”和“自动拆箱”的机制,比如如果要存入一个INT类型的数据,JVM会把数据包装成Integer然后再存入集合,看起来集合能够存入基本数据类型,其实是不能的只是多了一个包装数据的过程。我们来看个例子,验证一下集合存放的是对象的引用(内存地址,什么是内存地址呢?内存地址就是计算机中使用16进制或其他进制来映射一个字符,这个进制数就是实际字符在内存中所映射的地址,内存地址是内存当中存储数据的一个标识,并不是数据本身,通过内存地址可以找到内存当中存储的数据。),而不是对象数据本身:

package com.yzh.maven.main;
import java.util.ArrayList;
import java.util.List;
import com.yzh.maven.entity.UserInfo;
public class CollectionTest{
	private static Integer[] arr = null;
	public static void main(String[] args) {
		///////////////////////list元素的添加/////////////////////////
		List<UserInfo> list = new ArrayList<UserInfo>();
		UserInfo u = null;
		for(int i = 0;i<5;i++){
			u = new UserInfo();
			u.setUserName("yzh"+i);
			u.setPassword("123"+i);
			list.add(u);
		}
		for(int i = 0;i<list.size();i++){
			//System.out.println(System.identityHashCode(list.get(i)));
			System.out.println(list.get(i));
		}
		List<Integer> list2 = new ArrayList<Integer>();
		for(int i = 0;i<5;i++){
			list2.add(i);
		}
		System.out.println(list2.get(0).getClass());
	}
}
output:
com.yzh.maven.entity.UserInfo@6084fa6a
com.yzh.maven.entity.UserInfo@3a5476a7
com.yzh.maven.entity.UserInfo@7f39ebdb
com.yzh.maven.entity.UserInfo@33abb81e
com.yzh.maven.entity.UserInfo@4ec4d412
class java.lang.Integer

注意:
① System.out.println(xx),括号里面的“xx”如果不是String类型的话,就自动调用xx的toString()方法。
② Java中打印对象内存地址
Object的hashCode()默认是返回内存地址的,但是hashCode()可以重写,所以hashCode()不能代表内存地址的不同。System.identityHashCode(Object)方法可以返回对象的内存地址,不管该对象的类是否重写了hashCode()方法。

3、集合与数组的区别
数组是最常见的链式存储结构,它是一段连续的内存空间,在内存中我们可以简单表示为下图样式
在这里插入图片描述
注意:链表与数组是两种不同的数据结构,数据结构可以分为线性结构和非线性结构,在线性结构中,存储方式又分为连续存储(数组)和离散存储(链表),例如栈和队列都是线性结构常见的应用。我们可以将数组简单地理解为一种线性表数据结构(线性表是动态的),因为数组一旦定义了,其长度就不可以更改了,也就是不可以做增删操作,但可以做修改和查询操作。不存在什么先进先出的含义。
用数组存放一堆相同类型对象也是一个不错的选择,但是有一个很大的缺陷,那就是数组大小只能是固定的,不能从数组里动态添加和删除一个对象,要扩容的时候,就只能新建一个数组然后把原来的对象全部复制到新的数组里,而且只能存放相同类型的对象,使用起来不够灵活。下面我们来使用数组实现扩容,如下所示。

package com.yzh.maven.main;
public class CollectionTest3 {
	//初始化元素数组
	private static Object[] src = new Object[1];
	private static int len = src.length; 
	//目标数组
	private static Object[] to = new Object[len];
	//记录添加元素的个数
	private static int countTemp = 0;
	//中间数组
	private static Object[] obj = new Object[len];
	public static void main(String[] args) {
		for(int i = 0;i<12;i++){
			add("AA"+i);
		}

		for(Object o:to){
			System.out.print(o+" ");
		}
		
		System.out.println("\n原素个数"+size());
	}
	
	public static void add(Object i){
		int temp = src.length - 1;
		//最后一个元素不为空,说明数组元素已经满了,就扩容
		if(src[temp] != null){
			//在扩展前需要保存to数组的数据
			System.arraycopy(to, 0, obj, 0,len);
			//扩容
			len = len+1;
			//动态扩展数组,增加数组容量
			to = new Object[len];
//将obj数组从0开始的元素拷贝to数组到从0-第len-1个元素的位置,从obj中间数组中获取数据到to数组
			System.arraycopy(obj, 0, to, 0,len-1);
			//添加元素
			to[len-1]=i;
			//克隆数组是为了每次使长度增1
			obj = to.clone();
		      //如果是第一个元素(数组元素未满),也就是最后一个元素为空
		}else{
			src[0] = i;
			System.arraycopy(src, 0, to, 0, countTemp+1);
		}
		countTemp++;
		return;
	}
	
	public static int size(){
		return countTemp;
	}
}
/**output:
AA0 AA1 AA2 AA3 AA4 AA5 AA6 AA7 AA8 AA9 AA10 AA11 
12
*/

实际上,集合的底层是一个动态数组,原理类似于上面的情形。可以判断的是,在List集合的底层方法中,一定使用了arraycopy()方法来实现动态数组扩容。数组扩容的大概思路是,先判断数组的最后一个元素是否有值,如果有值,说明数组溢满,数组需要扩容才能添加元素了;如果没值,说明数组没满,就可以继续添加元素。在扩容时,需要定义一个中间数组来存储未扩容的目标数组的数据,否则会丢失内存中原有的数据,而且需要将中间数组的长度也要进行扩容,例如obj = to.clone();只是为了扩展中间数组容量而已,还可以将其改写成obj = new Object[len];来达到扩展中间数组容量。使用数组进行容量扩展是一件很麻烦的事,但是,我们使用已经封装好的List的实现类就不一样了:

package com.yzh.maven.main;

import java.util.ArrayList;
import java.util.List;
public class CollectionTest5  {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for(int i = 0;i<12;i++){
			list.add("AA"+i);
		}

		for(Object o:list){
			System.out.print(o+" ");
		}
	}
}
/**output:
AA0 AA1 AA2 AA3 AA4 AA5 AA6 AA7 AA8 AA9 AA10 AA11 
*/

我们看到,实现的效果和上面的一样,但是相比数组的操作就简单多了。

猜你喜欢

转载自blog.csdn.net/yzh18373476791/article/details/85456597