JavaSE学习笔记-Day12

一. 容器和泛型的引入

不管是什么语言,在开发程序的过程中和数据打交道是必不可少的,为了更好地容纳这些数据,我们在之前的学习中就引入了数组的概念,这确实是容纳数据的好办法,但是单单数组是完全不能满足我们的需求的,因为我们对数据远远不止只有容纳的要求,更重要的是要管理数据。诚然,对数组中的元素也可以进行管理,不过需要自己敲代码,如果功能仅限于此,那么这就不是Java了。作为更强大的语言,Java自然要帮你写好各种管理数据的代码,你只需要傻傻地调用就可以了,Java把这些容纳、管理数据的代码都封装成一些类,就成为了今天我们要学的——容器,也叫集合(Collection)。

容器有几点注意事项:

  1. Java的容器装的元素只能是一个对象。不过有包装类的存在,也不用担心基本数据类型装不进容器。
  2. 容器不止是像它字面上的意思——装东西,容器的强大之处是不单单要存储数据,还要帮你处理数据、管理数据。我们可以把容器理解成一个超级强化版的数据结构。

以下是Java容器类的主要框架
在这里插入图片描述
现在我们还是来探讨一下Java那些写源码的人是怎么将任何类型的对象都可以存到一个容器的呢?这个问题在包装类的时候就已经想过了,Object[]数组实现(多态性)。好,现在又有一个新问题:我把乱七八糟的不同类型的对象全部放到一个容器里,那当我拿的时候还得挨个判断是不是我要的类型的数据再拿,这不废劲吗?的确,按照我们的生活经验,我们用容器(瓶子、管子啥的)装东西的时候,也要按照种类的不同分开装,然后为了我们自己更方便的知道指定的容器里装的是啥,我们会在容器的最外面贴上标签来提示自己。哈哈,Java也不傻,也有常识,既然都封装好了容器,自然要做的完美,也认为一个容器装的对象类型不能太杂了,也定义了一个类似标签的东东——泛型,来限制一下。

用专业的话来阐述一遍:泛型可以帮助我们建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性。随便看看吧。泛型是在JDK1.5开始增加的。

二. 容器中泛型的使用

我们打开eclipse,不妨来看看Collection接口的部分源码,这些容器类都打包在了java.util包中:
在这里插入图片描述
好,这就是我们目前需要看的源码范围。咦,这接口名后面咋有个 '< E >'东西?这就是泛型!泛型定义的语句和语法全体现在上面了。这个泛型的定义呀,好比这个方法中的形参,这个<>中的这个字符E就好比形参的名字,这里的字符可以是任何标识符,不过一般采用T,E,V这3个字母,咱们也就别搞特殊了。那么怎么将一个确定的对象类型给传过去呢?具体语句是这样的:Collection<String> 对象名= new 具体实现类名<String>();,在JDK1.7开始后面<>里的内容可以省略。这样,这个接口中 < E >就相当于< String >了,也就是说,这个容器对象只能存储String类的对象。这个过程是不是和方法中形参和实参特别相似?!

这里还有几点要注意:第一,既然容器只能装对象,那<>里必须是引用数据类型,不能是基本数据类型。第二,我们只是强烈建议使用泛型。事实上,泛型的使用不是强制的,不使用编译器也不会报错! 这点可不能与形参对应了。

package cn.zjb.test;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 测试集合类使用
 * @author 张坚波
 *
 */
public class TestCollection {
	public static void main(String[] args) {
		
		Collection c= new ArrayList(); //没加泛型
		System.out.println(c.isEmpty()); //这个方法顾名思义,判断容器是否为空
		
	}
}

运行结果如下:
在这里插入图片描述
这个程序只是为了强调一下泛型不是必加不可的,没有其他任何意义。

三. Collection接口

现在,有了前面知识的铺垫,我们就开始从封装好的容器“类”继承树体系的根节点开始以深度优先为原则进行学习。

首先来看看Collection接口的常用方法:
在这里插入图片描述
最后一个先不管,来测试一下:

package cn.zjb.test;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 测试集合类使用
 * @author 张坚波
 *
 */
public class TestCollection {
	public static void main(String[] args) {
		
		Collection<String> c= new ArrayList<>();
		c.add("张");
		c.add("坚");
		c.add("波");
		System.out.println(c);//[张, 坚, 波]
		System.out.println(c.size()); //3
		System.out.println(c.isEmpty()); //false
		System.out.println(c.contains("zhang")); //false
		System.out.println(c.contains("张")); //true
		c.remove("张");
		Object[] obj=c.toArray(); //Object的toString()方法打印的是哈希地址值
		for(Object temp:obj)
			System.out.print(temp+" "); //坚 波
		System.out.println(""); //换行
		
		Collection<String> c2= new ArrayList<>();
		c2.add("张");
		c2.addAll(c); 
		System.out.println(c2); //[张, 坚, 波]
		System.out.println(c2.containsAll(c)); //true
		c2.retainAll(c);
		System.out.println(c2);// [坚,波]
		c2.removeAll(c);
		System.out.println(c2); //[]  ,元素为空
		
	}
}

四. List接口

java.util.List接口是有序、可重复的容器。它继承于Collection接口,所以Collection接口的方法它也有。 除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法,如图:
在这里插入图片描述
测试一下:

package cn.zjb.test;

import java.util.List;
import java.util.ArrayList;

/**
 * 测试List接口
 * @author 张坚波
 *
 */
public class TestList {
	public static void main(String[] args) {
		
		List<String> list=new ArrayList<>();
		list.add("张");
		list.add("坚");
//		list.add(3, "波"); //错误的,必须要在原有的数组长度内容中添加或紧接着添加
		list.add(2, "波");
		System.out.println(list); //[张, 坚, 波]
		list.set(1, "波");
		System.out.println(list); //[张, 波, 波]
		System.out.println(list.get(0)); //张
		System.out.println(list.indexOf("波")); //1
		System.out.println(list.lastIndexOf("波")); //2
		list.remove(1);
		System.out.println(list); //[张, 波]
		
	}
}

List接口常用的实现类有3个:ArrayList、LinkedList和Vector。咱们依次学习一下。

五. ArrayList容器

顾名思义,ArrayList底层用于存储数据的方式肯定是: 数组。ArrayList的特点:查询效率高,增删效率低,线程不安全。我们一般使用它。
在这里插入图片描述
我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 ArrayList的Object数组初始化长度为10,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组(容量是加上原来容量右移一位的结果,即一半),并将原数组内容和新的元素一起加入到新数组中,源码如下:
在这里插入图片描述
为了更好地掌握和使用ArrayList,我们来自己编写一下代码:

package cn.zjb.practice;

import java.util.Arrays;

/**
 *  MyArrayList类是用来对数据进行存储和管理的容器类,存储数据的结构为数组。
 *  <p>该类没有实现任何接口,并且是Object的直接子类。
 * @author 张坚波
 *
 */
public class MyArrayList <E> {
	/**
	 * MyArrayList容器用来储存数据元素的结构。
	 */
	private static Object[] container;
	
	/**
	 * MyArrayList容器默认的大小。
	 */
	private static final int DEFAULT_CAPACITY=5;
	
	/**
	 * 检查是否需要扩容的变量
	 */
	private static int currentLength;
	
	/**
	 * 当前container数组的长度
	 */
	private static int length;
	
	/**
	 * 用于创建一个容量为默认大小的数组作为数据存储结构。
	 */
	public MyArrayList() {
		container=new Object[DEFAULT_CAPACITY];
		currentLength=DEFAULT_CAPACITY;
	}
	
	/**
	 * 用于创建一个容量为自定义大小的数组作为数据存储结构。
	 * @参数 initialCapacity是自定义长度
	 * (目前不考虑参数传递是否会出问题,所有的方法都这样,简化点)
	 */
	public MyArrayList(int initialCapacity) {
		container=new Object[initialCapacity];
		currentLength=initialCapacity;
	}
	
	/**
	 * 该方法用于添加元素
	 * @参数 e是泛型E的具体对象
	 */
	public void add(E e) {
		if(length==currentLength-1) {
			expend();
		}
		container[length++]=e;
		
	}
	/**
	 * 该方法用于删除指定索引的元素
	 * @参数 index表示索引位置
	 */
	public void remove(int index) {
		if(index<=length-1) {
			System.arraycopy(container, index+1, container, index, length-index+1);
			length--;
		}else
			throw new RuntimeException();
	}
	
	/**
	 *该方法用于找出对应泛型元素的索引位置 
	 *@参数 e表示要找的泛型对象
	 *@返回值 对应泛型的索引位置,如果没有返回-1
	 */
	public int indexOf(E e) {
		for(int i=0;i<length;i++) 
			if(container[i].equals(e))
				return i;
		
		return -1;
	}
	
	/**
	 * 该方法用于找容器的大小
	 * @返回值 底层数组的长度
	 */
	public int size() {
		return length;
	}
	
	/**
	 * 该方法用于自动扩容
	 */
	public void expend() {
		container=Arrays.copyOf(container, currentLength*2);
		currentLength=currentLength*2;
	}
	
	
	/**
	 * 
	 * @参数 obj是container数组
	 * @返回值 将泛型E具体值以特殊格式返回
	 */
	public static String toString(Object[] obj) {
		if(length==0)
			return "[]";
		else {
			StringBuilder sb=new StringBuilder();
			sb.append('[');
			for(int i=0;i<length;i++)
				sb.append(container[i]+",");
			sb.setCharAt(sb.length()-1, ']');
			return sb.toString();
		}
	}
	
	/**
	 * 重写Object类的toString()方法,打印容器的内容
	 * @返回值  返回static String toString(Object[] obj)的返回值
	 */
	@Override
	public String toString() {
		return toString(container);
	}
	
}
package cn.zjb.practice;
/**
 * practice包中主方法所在的类
 * @author 张坚波
 *
 */
public class MainClass {
	public static void main(String[] args) {
		MyArrayList<String> a=new MyArrayList<>();
		for(int i=0;i<10;i++) //实现了自动扩容
			a.add("zhang");
		System.out.println(a); //[zhang,zhang,zhang,zhang,zhang,zhang,zhang,zhang,zhang,zhang]
		a.remove(2);
		System.out.println(a); //[zhang,zhang,zhang,zhang,zhang,zhang,zhang,zhang,zhang]
		System.out.println(a.indexOf("zhang")); //0
		System.out.println(a.size()); //9
	
	}
}

简化版的,不能再简化了,哈哈,就是找找感觉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值