JCF 集合框架

<span style="font-family: Arial, Helvetica, sans-serif;">JCF = Java Collections Framework =>Java集合框架</span>

什么是集合: 存放数据的容器
为什么使用集合:
一个方法只有一个返回值,如何将一组数据返回?  使用集合操作

JCF 从1.2 开始 把集合成体系的提供给我们 1.0 时 Vector HashTable

JCF 两大分支:
Collection Map
[存放单值类型对象] [存放键值对对象]

List Set Queue Deque
[有序,不唯一] [无序,"唯一"] [队列 5.0] [1.6]

SortedSet 对Set接口进行了有序,"唯一"的排序

1st. List接口的实现类 ArrayList

import java.util.ArrayList;

/*首先理解包装类(Wrapper Class)的概念
 * 
 * Java当中的数据类型有两种: 基本数据类型,引用类型
 * Java当中的集合只允许存放引用类型的对象[换言之 基本数据类型是无法被添加进集合的 Java当中的集合存放的是引用类型的地址值,而非实际对象]
 * 
 * 
 * */
public class TestArrayList0 {
	public static void main(String[] args){
		//java 集合当中只允许存放引用类型对象
		ArrayList<Integer> list = new ArrayList<Integer>();
		//JDK 5.0之前 下面这三句添加值的语句会变异报错的,因为JDK5.0之前 Object类型的参数绝对不能匹配 int, boolean 等基本数据类型
		list.add(1);//自动打包  list.add(new Integer(1));
		list.add(2);
		//从JDk5.0开始基本数据类型和它所对应的包装类类型可以直接赋值  自动打包
		int num1 = 2;
		Integer num2 = num1;//Integer num2 = new Integer(num1);
		//从5.0开始 包装类类型和它对应的基本数据类型也可以直接赋值 自动解包
		Integer num3 = 7;
		int num4 = num3;//int num4 = num3.intValue();
		//SCJP
		Boolean data1 = null;
		boolean data2 = data1;// boolean data2 = data1.booleanValue();//data1 == null
		System.out.println(data2);//?  java.lang.NullPointerException
	}
}
/*class Integer{//引用类型
	private int value;//基本数据类型
	public int intValue(){
		return value;
	}
}*/
/*
 * 基本数据类型: boolean		char		byte		short		int		long		folat		double 
 * 包装类:		Boolean			Character	Byte		Short		Integer	Long		Float		Double
 * 理解:
 * 基本数据类型比作为水的话,那么他们对应的包装类类型就是装水的容器
 * 
 * */
ArrayList的最基本的用法

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

//ArrayList 的最基本用法
/**
 * 如何创建对象:new ArrayList<>(可以直接数组初始化元素的个数 也可不填);
 * 如何添加元素:add()
 * 如何得到第几个元素:List.get(int index);
 * 得到所有元素个数:size(); 
 * 如何遍历:
 * 		1.for循环		2.Iterator迭代器		3.foreach(5.0)
 * */
public class TestArrayList1 {
	public static void main(String[] args){
		//如何创建ArrayList对象
		//ArrayList<Integer> list = new ArrayList<Integer>();//创建ArrayList对象并指定当中只能存放的数据类型
		List<Integer> list = new ArrayList<Integer>(5);//多态 创建ArrayList对象并指定当中只能存放的数据类型 如果可以确定集合当中以公会存放多少个元素,可以直接指定 不写的话,默认10块空间
		//向ArrayList当中添加元素:add()
		list.add(77);//new Integet(77);
		list.add(new Integer(55));
		list.add(99);
		list.add(55);
		System.out.println(list.size());//得到集合当中元素个数:size()
		// 如何得到集合当中的第几个元素:get(int index) 
		Integer num = list.get(0);//data[0]
		System.out.println(num); 
		System.out.println(list);//[77, 55, 99, 55]  ArrayList对象已经覆盖过了toString()
		
		//如何遍历ArrayList
		//1st way:for()+[i]
		System.out.println("====================");
		for(int i = 0;i<list.size();i++){
			System.out.println(list.get(i));
		}
		System.out.println("====================");
		
		//2nd way:Iterator 迭代器 它也可以指定泛型  1.测试还有没有下一个元素:hasNext(); 2.得到集合中的下一个元素:next();3.删除当前的元素 remove()
		Iterator<Integer> car = list.iterator();//集合对象调用iterator() 返回该集合对象便利用的迭代器
		while(car.hasNext()){//测试是否还有下一个元素
			Integer data = car.next();//得到下一个元素
			System.out.println(data);
		}
		System.out.println("====================");
		//3rd way: foreach    (5.0)
		for(Integer i:list){
			System.out.println(i);
		}
		
		
		/*
		 * length		length()		length()		size()
		 * 数组			字符串			文件			集合
		 * 
		 * */
	}
}
ArrayList中常用的 但被我们容易忽略的一些方法

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
//ArrayList 还有很多经常用到的方法  addAll();把一个集合中的所有元素添加到另一个集合中(合二为一)   subList(0,3);==>从下标0开始取出到下标3的所有元素组成一个新的集合
//	indexOf(Object);返回指定对象的下标索引值 如果不存在返回-1  lastIndexOf(Object);返回指定对象最后出现的位置   contains(Object);//测试是否包含一个元素
public class TestArrayList2 {
	public static void main(String[] args) throws Exception{
		ArrayList<Integer> list = new ArrayList<Integer>(5);
//		Class c = Class.forName("TestArrayList2");//得到TestArrayList2.class文件的类 类型对象
//		Method[]  ms = c.getDeclaredMethods();//得到当前类中所有方法 getDeclaredMethods()  返回Method[]
//		for(Method m:ms){
//			System.out.println(m);
//		}	
		//向集合中添加元素
//		list.add(1);
//		list.add(2);
//		list.add(3);
//		list.add(4);
//		list.add(5);
		//向集合中添加元素 可以直接使用Collections.addAll();
		//addAll(list,1,2,3,4);
		Collections.addAll(list, 1,2,3,4,5);//Connection是一个接口 Collections是集合常用工具类
		System.out.println(list.size());
		//删除元素  ArrayList集合当中提供了两个remove(int index)明确的删除第几个元素 和 remove(Object obj)  
		list.remove(1);//优先被认定为 int index
		System.out.println(list);
		//remove(Object obj) 明确删除哪一个元素
		list.remove(new Integer(5));
		System.out.println(list);
		//清空整个集合 两种方法 while(list.size()!=0) list.remove(0);//集合内有元素 就将下标0删除    list.clear();//sun公司提供的清空整个集合的方法
		//list.clear();
		System.out.println(list.contains(new Integer(7)));
	}
	/*
	public static void addAll(List list,Integer ... data){//5.0 可变参
		//遍历
		for(Integer i:data){
			list.add(i);//添加元素
		}
	}*/
}

import java.lang.reflect.Field;
import java.util.ArrayList;

//ArrayList 那些应该知道 而经常被忽略掉的:trimToSize();		ensureCapacity(int size);
//ArrayList 不定义默认10块空间就只能放10个元素,当放第十一个元素的时候会触发扩容操作
//jdk7.0之前 扩容机制:*3/2+1			7.0和8.0 扩容机制: old+(old>>1) 1.5倍扩容
public class TestArrayList4 {
	public static void main(String[] args)throws Exception{
		//6.0:		10		16		25		38		...
		//7.0:		10		15		22		33		...
		//如果你已经确定最重要存放多少个元素,那么最好在构造的时候构造方法直接传入参数
		Object obj = new Object();//测试不使用ensureCapacity(int size);时 执行效率
		int size = 5000000;
		ArrayList<Object> list = new ArrayList<Object>();		
		//从1970年1月1日0点0分000毫秒 - 执行这行代码时总共经历了多少毫秒
		long time1 = System.currentTimeMillis();//System.nanoTime();随机返回一个长整型的纳秒数  System.currentTimeMillis();返回毫秒数
		//向list中不断地添加obj元素
		for(int i = 0;i<size;i++)
			list.add(obj);
		list.trimToSize();
		long time2 = System.currentTimeMillis();
		System.out.println(time2 - time1);
		//ArrayList 创建对象的时候 构造方法直接传参
		//ArrayList<Object> list2 = new ArrayList<Object>(size);
		//如果要是不能确定底层空间 我们需要在够早完了之后通过ensureCapacity(int size)来调整底层的个数
		ArrayList<Object> list2 = new ArrayList<Object>();
		list2.ensureCapacity(size);//将底层数组调整到我们最终需要的那个大小
		long time3 = System.currentTimeMillis();
		for(int i = 0;i < size;i++){
			list2.add(obj);
		}
		list2.trimToSize();
		long time4 = System.currentTimeMillis();
		System.out.println(time4 - time3);
		
		
		
		/*
		//如果不确定,在元素添加完成之后,底层的数组元素个数未必和实际的元素个数相同,所以要学会使用list.trimToSize();		
		//利用反射 测试ArrayList底层扩容机制
		for(int i = 0;i < 12; i++){
			list.add(i);//new Integer(i)
		}
		//在添加完元素以后 应该在手动调用一下trimToSize() 将数组大小修正到你实际用了多少 就要多少空间
		list.trimToSize();
		Class c = Class.forName("java.util.ArrayList");
		Field f = c.getDeclaredField("elementData");//参数elementData 是ArrayList类中的一个属性 Field 代表属性(用来获得属性的) Method代表方法 ( 用来获得方法名字的)  
		f.setAccessible(true);//反射面前无私有,反射可以破封装
		Object[] data = (Object[])(f.get(list));//通过list当中得到f的那个属性 然后再进行强转成Object[] 因为本身就是Object[] 当然f.get(list)方法为了保证万能实用性将返回类型定义成了Object 
		System.out.println(data.length);//打印list当中的那个私有属性elementData
		*/
	}
}


ArrayList中的remove(Object obj) 是如何找到元素 以及删除元素的
import java.util.ArrayList;
import java.util.Collections;

//ArrayList的remove(Object obj) 它如何找到元素的   如何删除你的元素
//添加到集合的是i1 而删除的是i2 为什么调用remove()的时候 却把i1给删掉了? 因为ArrayList的remove方法完全尊重equals的比较结果
public class TestArrayList3 {
	public static void main(String[] args){
//		ArrayList<String> list = new ArrayList<String>();
//		//Integer i1 = new Integer(7);
//		//Integer i2 = new Integer(7);
//		String str1 = new String("okay");
//		String str2 = new String("okay");
//		//System.out.println(i1 == i2);//false  创建了两块空间 怎么可能是同一个对象  (new)  == 比较两个引用对象的内存地址是否相同
//		list.add(new String(str1));
//		System.out.println(list.size());//1
//		list.remove(str2);
//		System.out.println(list.size());//0  因为ArrayList的remove() 完全遵守equals比较机制
		/*
		 *在某些情况下 我们需要将两个不同的对象根据其特定的属性值将其认定为是相同的对象进行操作,这时 我们需要覆盖Object类提供的equals方法 
		 * 
		 * */
		ArrayList<Student> stu = new ArrayList<Student>();
		Student stu1 = new Student("tom",12);
		Student stu2 = new Student("tom",20);
		stu.add(stu1);
		System.out.println(stu.size());
		stu.remove(stu2);//==>stu1.equals(stu2)  Object 中equals方法底层是连等比较(比较的是引用地址) 那么如何将属性相同的视为同一个对象呢?需要覆盖equals方法
		System.out.println(stu.size());
	}
}
class Student{
	String name;
	int age;
	public Student(String name,int age){
		this.name = name;
		this.age = age;
	}
	//如果两个对象的内容相等,我们就将其认定为同一个对象  换言之 其实就是将什么样的两个Student当做同一个对象
	@Override
	public boolean equals(Object obj){
		if(obj == null) return false;
		if(!(obj instanceof Student)) return false;//instanceof 用来判断左边的是不是右边的一个实力 换言之 Obj是不是一个Student类型的对象
		if(obj == this) return true;
		Student s1 = this;//参与比较的第一个对象
		Student s2 = (Student)obj;//需要的是Student类型,而这里是Object类型,所以需要进行强转
		//return s1.name.equals(s2.name) && (s1.age == s2.age);//String 用equals比较内容是否相等,基本数据类型只能用连等比较
		return s1.name.equals(s2.name);//只要名字相同 即认定为同一个对象
	}
	
}
使用迭代器遍历ArrayList时需要注意的地方

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
//在使用迭代器遍历ArrayList的过程当中,绝对不允许对集合整体进行任何添加删除操作(除了未遂的  list.remove(new Integer(999));//因为list这个集合当中根本没有999这个元素)
//因为在迭代器当中 会对集合的当前的元素甚至当前的操作进行一个统计,如果他发现在他遍历集合的过程当中 他的每一次next()都回去检查我剩余的元素是否和应当剩余的元素一致的
//在遍历过程当中,只要不是通过迭代器去删除或添加的话,就会跑出修改并发异常 应该使用car.remove();//删除那个隐形的光标所指向的元素 不可以指定要删除的元素
//ConcurrentModificationException => 并发修改异常  
public class TestArrayList5 {
	public static void main(String[] args){
		List<Integer> list = new ArrayList<Integer>(5);
		Collections.addAll(list, 1,2,3,4,5);
		//删除小于3的元素
		Iterator car = list.iterator();
		while(car.hasNext()){
			Integer num = (Integer) car.next();
			if(num<3){
				//list.remove(num);//ConcurrentModificationException => 并发修改异常 
				car.remove();//删除那个隐形的光标所指向的元素 不可以指定要删除的元素 否则会报错
			}			
		}
		System.out.println(list);
		testCopyOnWriteArrayList();
	}
	/*
	 * 迭代器在得到任务开始遍历集合 在没有结束之前 它的每一次next()都会校验剩余的元素和它已经记录着的那个元素是否是匹配的 只要发现不一样就会跑出修改并发异常
	 * 如何解决:
	 * 5.0之前:必须使用迭代器的remove方法删除元素
	 * 5.0之后:可以使用util包中concurrent子包中提供的CopyOnWriteArrayList类
	 * 
	 * */
	//使用CopyOnWriteArrayList
	public static void testCopyOnWriteArrayList(){
		CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<Integer>();//不允许指定空间
		Collections.addAll(list, 1,2,3,4,5);
		//删除小于3的元素
		Iterator car = list.iterator();
		while(car.hasNext()){
			Integer num = (Integer)car.next();
			if(num<3)
			list.remove(num);
		}
		System.out.println(list);
	}
}

模仿ArrayList中 几个常用的核心实现

//模仿ArrayList底层实现(主要的方法)
public class TestArrayList6 {
	public static void main(String[] args){
		MyList list = new MyList(3);
		list.add(77);
		list.add(88);
		list.add(99);
		for(int i = 0;i<list.size();i++){
			System.out.println(list.get(i));
		}
	}
}
//自定义一个ArrayList
class MyList{
	private Object[] data;//null
	private int size;//用户究竟存放了多少个元素进来,就是一个计数器  add==>size++		remove==>size--
	
	//带参的构造方法  根据用户指定的容量初始化底层数组的构造方法
	public MyList(int length){
		data = new Object[length];
	}
	//默认的构造方法,它(this(10))共享了有参构造方法 默认10块空间
	public MyList(){
		//data = new Object[10];//默认分配10块空间  
		this(10);//构造方法中 首行this是使两个构造方法代码共享用的,相当于给上边的带参的构造方法穿了一个10进去
	}
	//用来得到已经存放了多少个元素的个数
	public int size(){
		return size;
	}
	//得到第几个元素的方法 get (int index)
	public Object get(int index){
		return data[index];
	}
	//添加元素的方法
	public void add(Object obj){
		if(data.length == size){//如果底层的数组存储空间已经满了
			Object[] temp = new Object[data.length*3/2+1];//那么 需要一个更大的新数组空间
			System.arraycopy(data, 0, temp, 0, size);//将老数组中的元素复制到新数组中1.要复制的原数组,2.从元素组的第几个元素开始复制,3.复制到那个数组中,4.复制到新数组的第几个位置(按下标),5.总共复制多少个
			data = temp;//返回新数组,将老数组交给垃圾回收器 这个方法一结束 temp就会被回收,所以要再重新将值保存给Object []data
			
		}
		data[size++] = obj;//size 上边我们定义的那个下标  那个计数器  size++;//统计一空存了多少个元素	
	}
	//删除第几个元素的方法 采用复制的方式实现的
	public void remove(int index){ 
		System.arraycopy(data, index+1,data ,index, size-index-1);
		size--;
		/*删除下标2指向的那个元素
		 * 0	1	2	3	4		//要删除的是下标2 那么其实就是从下标3开始向下标2复制元素 总共复制2个元素(元素的总个数 - 我要删除的下标 - 1  即 5-2-1)
		 * 0	1	3	4	
		 * 
		 * */
	}
	//删除指定的那个元素的方法
	public void remove(Object obj){
		for(int i = 0;i<size;i++){//size 是本类中的属性 即便是私有的 在本来中也可以得到
			//拿着用户传进来的每一个obj 去调用equals方法和我data[i]个元素进行比较,如果哪一个返回true就remove掉
			if(obj.equals(data[i])){
				remove(i);//调用上边的那个remove方法  remove(int index)
				return ;//将操作的结果返回回去,直接结束for循环,另外一个remove只能删除一个元素,要想删除多个 需要调用removeAll方法
			}
			
		}
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章