黑马程序员——集合Collection:体系详述

------<ahref="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

集合框架概述

集合体系结构图

集合框架内错综复杂的继承关系、以及各种不同的接口和类与其通过语言来描述,不如用图片来进行最为直观的展示。以下所展示的图片是集合框架的最新版本的体现。

接口和类的区分图


继承关系简图


继承关系详图


集合的基本特点

1,集合是存储对象最常用的一种方式
2,集合是一种容器,有了集合便可以十分方便地对多个对象进行操作
3,集合的长度是可变的,而数组的长度却是固定的
4,集合只能存对象,数组可以存基本数据类型
5,集合可以存不同类型的对象
6,集合中存储的都是对象的引用(地址)

Collection

Collection接口的通用方法

Collection定义了集合框架的共性功能。其下两个主要的子接口有:List(列表),Set(集)。

增加:

add方法的参数类型是Object。以便于接收任意类型对象。
add(e);
addAll(collection);
import java.util.ArrayList;

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();

		//1,添加元素。
			al.add("java01");//add(Object obj);容器内添加的对象是任意类型的,类型不确定,需要用父类,此处是多态
			al.add("java02");
			al.add("java03");
			al.add("java04");

		//打印原集合。
		sop("原集合:"+al);//输出:原集合:[java01, java02, java03, java04]
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

删除:

remove(e);
removeAll(collection);
clear();
import java.util.ArrayList;

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();

		//1,添加元素。
			al.add("java01");//add(Object obj);容器内添加的对象是任意类型的,类型不确定,需要用父类,此处是多态
			al.add("java02");
			al.add("java03");
			al.add("java04");
			
		//打印原集合。
			sop("原集合:"+al);//输出:原集合:[java01, java02, java03, java04]
		
		//2,删除元素。
			al.remove("java02");//输出:remove后集合:[java01, java03, java04]
			sop("remove后集合:"+al);
			al.clear();//清空集合。
			sop("clear后集合:"+al);//输出:clear后集合:[]
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

判断:

contains(e);
isEmpty();
</pre><pre name="code" class="java">import java.util.ArrayList;

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();

		//1,添加元素。
			al.add("java01");//add(Object obj);容器内添加的对象是任意类型的,类型不确定,需要用父类,此处是多态
			al.add("java02");
			al.add("java03");
			al.add("java04");
			sop("原集合:"+al);//输出:原集合:[java01, java02, java03, java04]
		
		//2,删除元素。
			al.remove("java02");//输出:remove后集合:[java01, java03, java04]
			sop("remove后集合:"+al);
			al.clear();//清空集合。
			sop("clear后集合:"+al);//输出:clear后集合:[]
		//3,判断元素。
			sop("java03是否存在:"+al.contains("java03"));//输出:java03是否存在:false
			sop("集合是否为空?"+al.isEmpty());//输出:集合是否为空?true(此时集合长度为零)
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

获取交集:

retainAll();
import java.util.ArrayList;

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al1 = new ArrayList();
		//al1添加元素
			al1.add("java01");
			al1.add("java02");
			al1.add("java03");
			al1.add("java04");
			
		ArrayList al2 = new ArrayList();
		//al2添加元素
			al2.add("java01");
			al2.add("java02");
			al2.add("java08");
			al2.add("java09");
			al2.add("java10");
		//al1.retainAll(al2)
			al1.retainAll(al2);
			sop("al1:" + al1);//输出:al1:[java01, java02]
			sop("al2:" + al2);//输出:al2:[java01, java02, java08, java09, java10]
		//al2.retainAll(al1)
			al2.retainAll(al1);
			sop("al2:" + al2);//输出:al2:[java01, java02]
	}
	

取出元素:

目的是要取出元素并操作元素,所以光靠打印出集合是远远不够的。
iterator();
size();

迭代器接口Iterator

对于集合的元素取出这个动作:用一个函数来描述不够,需要用多个功能来体现,所以就将取出这个动作封装成一个对象来描述。把取出方式定义在集合的内部,这样取出方式就可以直接访问集合内部的元素,那么取出方式就被定义成了内部类。
而每一个容器的数据结构不同,所以取出的动作细节也不一样。但是都具有共性内容: 判断和取出。那么就可以将这些共性抽取,这些内部类都符合一个规则(或者说都抽取出来一个规则),该规则就是Iterator。
通过一个对外提供的方法:iterator();,来获取集合的取出对象。

迭代的常见操作

boolean hasNext();//有下一个元素,返回真
E next();//取出下一个元素
void remove();//移除
注:在迭代时循环中next调用一次,就要hasNext判断一次。

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

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();
		//al添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
			al.add("java04");
		
		//利用迭代器获取元素的基本方法
			Iterator it = al.iterator();//获取迭代器,取出集合中的元素
			//接口型引用只能指向自己的子类对象,而且该对象并不是被new()出来的,是通过al这个ArrayList调用了iterator()方法获取出来的
			//sop(it.next());//java01
			//sop(it.next());//java02
			//sop(it.hasNext());//true
			
			while(it.hasNext()){//iterator中的hasNext方法,只要集合中还有下一个元素,就返回真
				sop(it.next());//iterator中的next方法,取出下一个元素,并通过sop打印
			}
			/*
			  	输出:
			  	java01
				java02
				java03
				java04
			 */
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
/*
while代码块如果用for来写,格式如下:
for(Iterator it = al.iterator(); it.hasNext() ; )
{
	System.out.println(it.next());
}
使用for来写,避免了循环结束后it还在内存中 		 
*/

迭代器使用注意事项

1,迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
2,迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
3,迭代器的next方法返回值类型是Object,所以要记得类型转换。

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

class  CollectionDemo
{
	public static void main(String[] args) 
	{
		//创建一个集合容器。使用Collection接口的子类。ArrayList
		ArrayList al = new ArrayList();
		//al添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
			al.add("java04");
		
		//利用迭代器获取元素的基本方法
			Iterator it = al.iterator();//获取迭代器,取出集合中的元素
			//接口型引用只能指向自己的子类对象,而且该对象并不是被new()出来的,是通过al这个ArrayList调用了iterator()方法获取出来的
			//sop(it.next());//java01
			//sop(it.next());//java02
			//sop(it.hasNext());//true
			
			while(it.hasNext()){//iterator中的hasNext方法,只要集合中还有下一个元素,就返回真
				sop(it.next());//iterator中的next方法,取出下一个元素,并通过sop打印
			}
			/*
			  	输出:
			  	java01
				java02
				java03
				java04
			 */
	}
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
/*
while代码块如果用for来写,格式如下:
for(Iterator it = al.iterator(); it.hasNext() ; )
{
	System.out.println(it.next());
}
使用for来写,避免了循环结束后it还在内存中 		 
*/

List

体系概述

Collection
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。
|--Set:元素是无序,元素不可以重复。

List特有方法

增加:

add(index,element);
addAll(index,Collection);
import java.util.*;
class ListDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		//添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
		
			sop("原集合是:"+al);//输出:原集合是:[java01, java02, java03]
		//在指定位置添加元素。
			al.add(1,"java09");//在脚标为1的位置插入java09这个元素
			sop("改变后的集合是:"+al);//输出:改变后的集合是:[java01, java09, java02, java03]
	}
}

删除:

remove(index);
import java.util.*;
class ListDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		//添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
		
			sop("原集合是:"+al);//输出:原集合是:[java01, java02, java03]
		//在指定位置添加元素。
			al.add(1,"java09");
			sop("改变后的集合是:"+al);//输出:改变后的集合是:[java01, java09, java02, java03]
		
		//删除指定位置的元素。
			al.remove(2);//移除脚标为2的位置的元素
			sop("remove后的集合是:"+al);//remove后的集合是:[java01, java09, java03]
	}
}

修改:

set(index,element);
import java.util.*;
class ListDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		//添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
		
			sop("原集合是:"+al);//输出:原集合是:[java01, java02, java03]
		//在指定位置添加元素。
			al.add(1,"java09");
			sop("改变后的集合是:"+al);//输出:改变后的集合是:[java01, java09, java02, java03]
		
		//删除指定位置的元素。
			al.remove(2);//移除脚标为2的位置的元素
			sop("remove后的集合是:"+al);//remove后的集合是:[java01, java09, java03]
		//修改元素。
			al.set(2,"java007");//在脚标为2的位置,修改原来的元素的值
			sop("set后的集合是:"+al);//输出:set后的集合是:[java01, java09, java007]
	}
}

获取:

get(index):
subList(from,to);
listIterator();
int indexOf(obj):获取指定元素的位置。
ListIterator listIterator();
import java.util.*;
class ListDemo 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) {
		ArrayList al = new ArrayList();
		//添加元素
			al.add("java01");
			al.add("java02");
			al.add("java03");
		
			sop("原集合是:"+al);//输出:原集合是:[java01, java02, java03]
		//添加
			al.add(1,"java09");
			sop("改变后的集合是:"+al);//输出:改变后的集合是:[java01, java09, java02, java03]
		
		//删除
			al.remove(2);//移除脚标为2的位置的元素
			sop("remove后的集合是:"+al);//remove后的集合是:[java01, java09, java03]
		//修改
			al.set(2,"java007");//在脚标为2的位置,修改原来的元素的值
			sop("set后的集合是:"+al);//输出:set后的集合是:[java01, java09, java007]
		//获取
			//get(index):位置->元素
				//如果al.get(1)写在此处,然后再直接打印al,输出的还是[java01, java09, java007],因为仅仅是在查看
				sop("get(1)获得的元素是:" + al.get(1));//输出:get(1)获得的元素是:java09
				//获取所有元素。
					for(int x=0; x<al.size(); x++)//和数组不同,集合所使用的是size
					{
						System.out.println("al("+x+")="+al.get(x));
					}
					/*
						al(0)=java01
						al(1)=java09
						al(2)=java007
					 */
			//int indexOf(obj)元素->位置
					sop("index="+al.indexOf("java09"));//输出:index=1
			//subList(from,to);
					List sub = al.subList(1,3);//取脚标为1,2的两个元素,包含头,不包含尾
					sop("sub="+sub);//输出:sub=[java09, java007]
	}
}

ListIterator

ListIterator的特点:
ListIterator是List集合特有的迭代器,是Iterator的子接口。
使用迭代器时的注意事项:
在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。所以在迭代器时,只能用迭代器的方法操作元素。
需要ListIterator的原因:
Iterator方法是有限的,只能对元素进行判断,取出,删除的操作。如果想要其他的操作,如添加、修改等,就需要使用其子接口:ListIterrator。该接口只能通过List集合的ListIterator方法获取。

ListIterator的特有方法

add(obj);//增加
set(obj);//修改为obj
hasPrevious();//判断前面有没有元素
previous();//取前一个元素
import java.util.*;

class ListIteratorDemo{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	
	public static void main(String[] args) 
	{

		//新建一个ArrayList,并添加3个元素
			ArrayList aL = new ArrayList();
			aL.add("java01");
			aL.add("java02");
			aL.add("java03");
			sop(aL);

		/*
		 //在迭代过程中,准备添加或者删除元素。
			Iterator it = al.iterator();//此时把元素的引用又取到迭代器中去了
			//操作元素既可以用add,也可以用迭代器,但是不能同时使用,会发生ConcurrentModificationException异常	
			while(it.hasNext())
			{
				Object obj = it.next();
				if(obj.equals("java02"))//调用Object类的equals方法
					//al.add("java008");
					it.remove();//将java02的引用从集合中删除了。使用了Iterator的remove功能,不过移除的仅仅是集合中元素的引用
					//但是Iterator中没有add功能,要用迭代器来添加元素,考虑用ListIterator
				sop("obj="+obj);//输出:obj1=java01 \n obj2=java02 \n obj3=java03,元素的引用移除了,元素还在内存中,并还在被obj使用,java02还能打出 
			}
			sop(al);//输出:[java01,java03],移除后al的结果
		*/		
		
		//新建一个ListIterator对象
		
		//使用ListIterator的add方法
			 for(ListIterator li = aL.listIterator(); li.hasNext() ; )
			 {
					Object obj = li.next();			
					if(obj.equals("java02"))
						li.add("java009");	
			 }
			 
			sop(aL);//输出:[java01, java02, java009, java03],在java02这个元素后面插入一个java009的新元素

		//使用ListIterator的set方法
			for(ListIterator li = aL.listIterator(); li.hasNext() ;)//如果使用while的方式,则hasnext()直接为flase,因为li还在内存中
			{
				Object obj = li.next();
				if(obj.equals("java02"))
					li.set("java006");//把java02的位置替换为Java006
			}
			sop(aL);//输出:[java01, java006, java009, java03]
			
			
		//使用ListIterator的hasPrevious、previous方法
			ListIterator li = aL.listIterator();
			while(li.hasNext())
			{
				sop("next::"+li.next());
			}
			while(li.hasPrevious())
			{
				sop("pres::" + li.previous());
				/*
				 	pres::java03
					pres::java009
					pres::java006
					pres::java01

				 */
			}
	}
}

Enumeration:Vector

枚举就是Vector特有的取出方式。发现枚举和迭代器很像,其实枚举和迭代是一样的。
因为枚举的名称以及方法的名称都过长,所以被迭代器取代了。
特有方法:
addElement(obj);//添加元素,相当于add(obj);
Enumerationelements();//Vector特有取出方式(枚举)
hasMoreElements();//相当于Iterator的hasNext()方法
nextElements();//相当于Iterator的next()方法

LinkedList特有方法

增加:

addFirst();
addLast();
import java.util.*;

class LinkedListDemo 
{
	public static void main(String[] args) 
	{
		LinkedList link = new LinkedList();
	//addLast方法:尾端插入
		link.addLast("java01");
		link.addLast("java02");
		link.addLast("java03");
		link.addLast("java04");
		sop(link);//输出:[java01, java02, java03, java04]
	//addFirst方法:首端插入
		link.addFirst("java11");
		link.addFirst("java22");
		link.addFirst("java33");
		link.addFirst("java44");
		sop(link);//输出:[java44, java33, java22, java11, java01, java02, java03, java04]
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

获取:

1,获取但不删除
getFirst();
getLast();
2,获取但删除
removeFirst();
removeLast();
package collection;

import java.util.*;

class LinkedListDemo 
{
	public static void main(String[] args) 
	{
		LinkedList link = new LinkedList();
		link.add("java01");
		link.add("java02");
		link.add("java03");
		link.add("java04");
		sop(link);//输出:[java01, java02, java03, java04]
	
	//getFisrt
		sop(link.getFirst());//输出:java01
	//getLast
		sop(link.getLast());//输出:java04
		
	//removeFirst
		while(!link.isEmpty()){
			sop(link.removeFirst());
			/*
			 	java01
				java02
				java03
				java04
			 */
		}
	
	//removeLast
		while(!link.isEmpty()){//仅为演示用,实际过程中,此处link已经是空的,不会输出
			sop(link.removeLast());
		}
		/*
		 	java04
			java03
			java02
			java01
		 */
	}

	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

JDK6更新方法:
1、增
offFirst();
offLast();
2、获取:获取元素,但是不删除。如果集合中没有元素,会返回null。
peekFirst();
peekLast();
3、删:获取元素,并删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
与老方法的区别:
老方法:如果集合中没有元素,会出现NoSuchElementException
新方法:如果集合中没有元素,会返回null。

ArrayList应用实例

关键点:
contains方法在判断是否存在该元素的时候,其实是在用对象的equals方法在和对象进行比较,看对象与对象是否相同
如果要自定义新的equals规则,需要复写对(shang)象(di)的equals方法。
package collection;

import java.util.*;

/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。

思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。

List集合判断元素是否相同,依据是元素的equals方法。
*/

class Person
{
	private String name;
	private int age;
	Person(String name,int age)//构造器
	{
		this.name = name;
		this.age = age;
	}
	//开发时使用setter和getter
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	
	//equals方法:判断对象是否相同
	public boolean equals(Object obj)//此处的equals对象的equals方法(上帝的方法),根据自定义的要求,复写该equals方法
	//在容器比较元素是否相同,使用的是equals方法,用对象的equals和另一个对象在比较
	{
		if(!(obj instanceof Person))
			return false;

		Person p = (Person)obj;//向下转型
		System.out.println(this.name+"....."+p.name);
		/*
		 程序输出:主动比较者(this)...被比较者(obj)
		 	lisi02.....lisi01
			lisi04.....lisi01
			lisi04.....lisi02
			lisi03.....lisi01
			lisi03.....lisi02
			lisi03.....lisi04
			lisi04.....lisi01
			lisi04.....lisi02
			lisi04.....lisi04
			lisi03.....lisi01
			lisi03.....lisi02
			lisi03.....lisi04
			lisi03.....lisi03
		 */
		return this.name.equals(p.name) && this.age == p.age;//返回真就说明相同,返回假就说明不同
		//此处的equals是调用字符串的equals方法
	}
	
}

class ArrayListTest
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();//ArrayList容器不知道元素相同的条件

		al.add(new Person("lisi01",30));
		//其实在存的时候存的是al.add(Object obj);,只有Object才能接收任意对象
		//把Person往里面传的时候,其实是这样:Object obj = new Person("lisi01",30);类型被提升了
		//使用next时,返回的是Object,Object中没有Person特有的方法,如果还想要再得到Person类中的方法,需要向下转型
		al.add(new Person("lisi02",32));
		al.add(new Person("lisi04",35));
		al.add(new Person("lisi03",33));
		al.add(new Person("lisi04",35));
		
		//sop(al);
	
	//使用singleElement(al)方法后,去掉了重复元素
		al = singleElement(al);

		sop("remove 03 :"+al.remove(new Person("lisi03",33)));//remove方法底层也是依赖于元素的equals方法。

		Iterator it = al.iterator();
		while(it.hasNext())
		{
			Person p = (Person)it.next();//next()返回的是已经被提升的类型,需要子类对象特有的方法(此处如getName)时,需要先向下转型
			/*
			 其实是两句话的简化
			 Object obj = it.next();
			 Person p =(Person)obj; 意思就是把obj变成Person类型,然后赋给p
			 */
			sop(p.getName()+"::"+p.getAge());
		}
	}


	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器。
		ArrayList newAl = new ArrayList();

		Iterator it = al.iterator();

		while(it.hasNext())
		{
			Object obj = it.next();

			if(!newAl.contains(obj))
			//contains方法在判断是否存在该元素的时候,其实是在用对象的equals方法在和对象进行比较,看对象与对象是否相同
			//如果要自定义新的equals规则,需要复写对象的equals方法
			//1.lisi01先传入到contains的obj中
			//2.lisi01被添加进newA1这个集合中
			//3.lisi02被传入到contains的obj中
			//4.程序跳转到public boolean equals(Object obj),开始进行equals的比较
			//5.因为第一个对象lisi01并没有其他对象与之比较,所以直接被传入到newAl中,而第二个对象lisi02则需要调用equals方法与第一个传入的对象lisi01进行比较
			//6.equals在Object类的源码中的语句是:public boolean equals(Object obj) {return (this == obj)},第一次比较时,obj是lisi01,this是lisi02
			//7.因为newAl.contains(obj)第一次比较时,相当于lisi02.equals(lisi01),因为lisi01是先存在的,用后来的和先来的比较
			//8.那么lisi01就传入public boolean equals(lisi01)
				newAl.add(obj);
		}

		return newAl;
	}
}

Set

Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。     
|--HashSet:底层数据结构是哈希表。线程不同步。 保证元素唯一性的原理:判断元素的hashCode值是否相同。如果相同,还会继续判断元素的equals方法,是否为true。
|--TreeSet:可以对Set集合中的元素进行排序。默认按照字母的自然排序。底层数据结构是二叉树。保证元素唯一性的依据:compareTo方法return 0。
Set集合的功能和Collection是一致的。

HashSet

底层实现及概念

HashSet的底层是hashtable

基本功能


碰撞的解决方法之1:Open Addressing

碰撞解决方法之2:Separate Chaining


HashSet中保证元素唯一性的方法:

通过元素的hashcode加equals方法,如果hashcode相同,再用Object的equals方法来判断元素是否相同,要注意先判断的是hashcode

注意:
1,直接打印对象时,调用对象的toString方法,其底层实现是getClass().getName() + '@' + Integer.toHexString(hashCode())
2,我们所看到的@后面的值是对象的hashCode,和真实物理地址并不相同

在HashSet中存入自定义对象

package collection;

import java.util.*;

/*
往hashSet集合中存入自定对象
姓名和年龄相同为同一个人,重复元素。
*/
class HashSetDemo
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		//创建一个HashSet
			HashSet hs = new HashSet();

		//在HashSet中存入元素
			hs.add(new Person4("a1",11));
			hs.add(new Person4("a2",12));
			hs.add(new Person4("a3",13));
			hs.add(new Person4("a2",12));//重复的元素
	
			//sop("a1:"+hs.contains(new Person("a2",12)));
				
			//hs.remove(new Person("a4",13));
		
		//取出HashSet中的元素
			Iterator it = hs.iterator();
	
			while(it.hasNext())
			{
				Person4 p = (Person4)it.next();
				sop(p.getName()+"::"+p.getAge());
			}
	}
}
class Person4
{
	private String name;
	private int age;
	Person4(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	//复写Object类的hashCode方法
		public int hashCode()
		{
			System.out.println(this.name+"....?hashCode");
			return name.hashCode()+age*37;//如果return了固定值,如return 60,那么所有元素hashcode相同,会不断调用equals方法,效率较低
			//name.hashCode()调用了String复写Object类的hashCode方法
			//字符串的hashCode的计算公式为:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
			//age乘以一个数是为了防止这样的情况发生:Stringobj1.hashCode() + Stringobj2.age = Stringobj2.hashCode() + Stringobj1.age
		}
	//建立Person对象自己的hashCode
	
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	
	//复写Object类的equals方法
		public boolean equals(Object obj)
		{
	
			if(!(obj instanceof Person4))
				return false;
	
			Person4 p = (Person4)obj;
			System.out.println(this.name+"...equals?.."+p.name);
	
			return this.name.equals(p.name) && this.age == p.age;
		}

}

/*
输出结果:
	//System.out.println(this.name+"....?hashCode");
		a1....?hashCode
		a2....?hashCode
		a3....?hashCode
		a2....?hashCode
	//System.out.println(this.name+"...equals?.."+p.name);
		a2...equals?..a2
	//sop(p.getName()+"::"+p.getAge());
		a3::13
		a1::11
		a2::12
*/

TreeSet

底层实现及概念

与HashSet相比最大的特点:
可以对Set集合中的元素进行排序。

实现TreeSet排序的两种方式

1,让元素自身具备比较性

元素需要实现Comparable接口,覆盖compareTo方法。也种方式也成为元素的自然顺序,或者叫做默认顺序。

2,让集合自身具备比较性

当元素自身不具备比较性时,或、比较性并非所需。这时就要让集合自身具备比较性。在集合初始化时,就有了比较方式。

TreeSet底层数据结构:红黑树



TreeSet排序1:让元素自身具备比较性

package collection;

/*
 需求:
	往TreeSet集合中存储自定义对象学生。
	想按照学生的"年龄"进行排序。
 */
import java.util.*;

class TreeSetDemo 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet();

		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi08",19));
		//ts.add(new Student("lisi007",20));
			//如果比完此处的主要条件:年龄之后,不再比较一下次要条件:姓名,那么当年龄相同而姓名不同的人存进来以后,将不会被存入

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

//告诉TreeSet用哪种方式进行排
	//TreeSet本身需要进行排序,但是它并不知道以怎样的方式排,如果不让存入的对象具备比较性,会产生ClassCastException

//让元素自身具备比较性:1.实现Comparable接口
	class Student implements Comparable//该接口强制让学生具备比较性。
	{
		private String name;
		private int age;
	
		Student(String name,int age)
		{
			this.name = name;
			this.age = age;
		}
		
//让元素自身具备比较性:2.复写compareTo方法
	public int compareTo(Object obj)
	{
		//return 0;
		
		if(!(obj instanceof Student))
			throw new RuntimeException("不是学生对象");
		Student s = (Student)obj;//向下转型

		System.out.println(this.name+"....compareto....."+s.name);
		
		if(this.age>s.age)
			return 1;//返回负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 
		if(this.age==s.age)
		{
			return this.name.compareTo(s.name);//比较完主要条件之后再比较次要条件
			//用compareTo方法之后,当年龄相同时,还会按照姓名的顺序进行排序输出
			//String类中本身就包含compareTo方法
				/*
				 按字典顺序比较两个字符串。该比较基于字符串中各个字符的 Unicode 值。
				 按字典顺序将此 String 对象表示的字符序列与参数字符串所表示的字符序列进行比较。
					 如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
					 如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
					 如果这两个字符串相等,则结果为 0;compareTo 
				 只在方法 equals(Object) 返回 true 时才返回 0。
				 */
		}
		return -1;
	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}


正序和逆序输出的方式
class TreeSetOptDemo 
{
	public static void main(String[] args) 
	{
		TreeSet ts = new TreeSet();

		ts.add(new Student2("lisi02",17));
		ts.add(new Student2("lisi007",20));
		ts.add(new Student2("lisi09",19));
		ts.add(new Student2("lisi08",19));
		ts.add(new Student2("lisi06",19));
			//如果比完此处的主要条件:年龄之后,不再比较一下次要条件:姓名,那么当年龄相同而姓名不同的人存进来以后,将不会被存入

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student2 stu = (Student2)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

//告诉TreeSet用哪种方式进行排
	//TreeSet本身需要进行排序,但是它并不知道以怎样的方式排,如果不让存入的对象具备比较性,会产生ClassCastException

//让元素自身具备比较性:1.实现Comparable接口
	class Student2 implements Comparable//该接口强制让学生具备比较性。
	{
		private String name;
		private int age;
	
		Student2(String name,int age)
		{
			this.name = name;
			this.age = age;
		}
		
//让元素自身具备比较性:2.复写compareTo方法
	public int compareTo(Object obj)
	{
		return 1;//1代表后存入的对象总是比先存入的对象大,取的时候默认方式是从小到大取出
		//结果是按照存入的顺序输出
		/*
		 return1输出结果:
		 	lisi02...17
			lisi007...20
			lisi09...19
			lisi08...19
			lisi06...19
		 return-1输出结果:
		 	lisi06...19
			lisi08...19
			lisi09...19
			lisi007...20
			lisi02...17
		 */

	}

	public String getName()
	{
		return name;

	}
	public int getAge()
	{
		return age;
	}
}

TreeSet排序2:让集合自身具备比较性

TreeSet(Comparator<? super E> comparator) 
 构造一个新的空 TreeSet,它根据指定比较器进行排序。
注意:comparator接口里面需要复写compare方法,comparable接口里面需要复写的是compareTo方法
import java.util.*;

//假设原来按照年龄排序的代码已经固定,现在想在不改变原来代码的基础上,重新按照姓名进行排序

//原来已经固定的代码部分
		class Student implements Comparable//该接口强制让学生具备比较性。
		{
			private String name;
			private int age;
		
			Student(String name,int age)
			{
				this.name = name;
				this.age = age;
			}
		
			public int compareTo(Object obj)
			{	
				if(!(obj instanceof Student))
					throw new RuntimeException("不是学生对象");
				Student s = (Student)obj;
		
				//System.out.println(this.name+"....compareto....."+s.name);
				if(this.age>s.age)
					return 1;
				if(this.age==s.age)
				{
					return this.name.compareTo(s.name);
				}
				return -1;
			}
		
			public String getName()
			{
				return name;
			}
			public int getAge()
			{
				return age;
			}
		}


class TreeSetDemo2 
{
	public static void main(String[] args) 
	{
		/*
		 使用TreeSet默认构造函数,不传入MyCompare时的结果:
		 TreeSet ts = new TreeSet();
		 按照原来年龄的方式排序
		 	lisi06...18
			lisi09...19
			lisi007...20
			lisi02...21
			lisi02...22
			lisi007...29
		
		使用TreeSet超载构造函数,传入MyCompare类时的结果:
		TreeSet ts = new TreeSet(new MyCompare());
		按照新的姓名的方式排序
			lisi007...20
			lisi007...29
			lisi02...21
			lisi02...22
			lisi06...18
			lisi09...19
		 */
		
		//TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
		TreeSet ts = new TreeSet(new MyCompare());
		ts.add(new Student("lisi02",22));
		ts.add(new Student("lisi02",21));
		ts.add(new Student("lisi007",20));
		ts.add(new Student("lisi09",19));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi06",18));
		ts.add(new Student("lisi007",29));
		//ts.add(new Student("lisi007",20));
		//ts.add(new Student("lisi01",40));

		Iterator it = ts.iterator();
		while(it.hasNext())
		{
			Student stu = (Student)it.next();
			System.out.println(stu.getName()+"..."+stu.getAge());
		}
	}
}

//TreeSet排序方式二:让集合自身具备比较性
	class MyCompare implements Comparator
	{
		public int compare(Object o1,Object o2)
		{
			Student s1 = (Student)o1;
			Student s2 = (Student)o2;
	
			int num = s1.getName().compareTo(s2.getName());
			//此处的compareTo是调用String复写Object类的compareTo方法,该方法会根据比较结果返回值
	//主要条件相同时再比较次要条件
			if(num==0)
			{
				return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
				//数字也可以排序,此处调用Integer整数类复写的compareTo方法
					//把年龄先封装成整数对象再调用Integer的compareTo方法
					//将new Integer(s1.getAge())和new Integer(s2.getAge())进行比较
				/*
				if(s1.getAge()>s2.getAge())
					return 1;
				if(s1.getAge()==s2.getAge())
					return 0;
				return -1;
				*/
			}
			
			return num;
		}
	}

Map

基本概念

Map的分类:
|--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。JDK1.0,效率低。
|--HashMap:底层是哈希表数据结构。允许使用null键null值,该集合是不同步的。JDK1.2,效率高。
|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给Map集合中的键进行排序。
Map的特点:
1,该集合存储键值对,一对一对往里存,要保证键的唯一性。
2,Set集合的底层就是Map

常用操作方法

1,添加。
put(K key, V value) 
putAll(Map<? extends K,? extends V> m) 
2,删除。
clear() 
remove(Object key) 
3,判断。
containsValue(Object value) 
containsKey(Object key) 
isEmpty() 
4,获取。
get(Object key) 
size() //获取长度
values() //获取元素的value

entrySet() 
keySet() 

import java.util.*;
class  MapDemo
{
	public static void main(String[] args) 
	{
		Map<String,String> map = new HashMap<String,String>();

	//添加元素
		//Collection中的add方法返回的是boolean,而put方法返回的是该键被存入后原来的值
			//如果出现添加相同的键时,那么后添加的值会覆盖原有键对应值。
			//并put方法会返回被覆盖的值。
		System.out.println("put01:"+map.put("01","zhangsan1"));//输出:put01:null
		System.out.println("put01:"+map.put("01","wangwu"));//输出:put01:zhangsan1
		map.put("02","zhangsan2");
		map.put("03","zhangsan3");
	//判断元素
		System.out.println("containsKey:"+map.containsKey("022"));//输出:containsKey:false
	
	//删除元素
		//键-删-值
		System.out.println("remove:"+map.remove("02"));//输出:remove:zhangsan2
		
	//获取元素
		System.out.println("get023:"+map.get("023")); //输出:get023:null
		System.out.println("get01:"+map.get("01")); //输出:get01:wangwu
		
		map.put("04",null);//把key"04"存为null
		System.out.println("get04:"+map.get("04"));//输出:get04:null
			//hashMap集合中null是可以作为键存在的,hashTable不可存null
			//可以通过get方法的返回值来判断一个键对应的值是否存在。通过返回null来判断。

		//获取map集合中所有的值。
			Collection<String> coll = map.values();
			System.out.println(coll);//输出:[wangwu, zhangsan3, null] 返回的是Map中的值所组成的Collection
			System.out.println(map);//输出:{01=wangwu, 03=zhangsan3, 04=null}
	}
}

Map集合的两种取出方式

Map-keySet方式

基本思想:将map集合转成set集合。再通过迭代器取出。
Set<k> keySet方法具体步骤:
1,将map中所有的键存入到Set集合。因为set具备迭代器,所以可以用迭代方式取出所有的键
2,再根据get方法。获取每一个键对应的值。
import java.util.*;
class MapDemo2 
{
	public static void main(String[] args) 
	{
			Map<String,String> map = new HashMap<String,String>();
	
			map.put("02","zhangsan2");
			map.put("03","zhangsan3");
			map.put("01","zhangsan1");
			map.put("04","zhangsan4");

	
		//1,先获取map集合的所有键的Set集合,keySet();
			Set<String> keySet = map.keySet();//此处的String代表Key的类型在这里是字符串类型

		//2,有了Set集合,就可以获取其迭代器拿到Key
			Iterator<String> it = keySet.iterator();//Iterator的泛型和Set一致,因为是在操作Set中的元素
	
			while(it.hasNext())
			{
				String key = it.next();
		//3,有了Key就可以通过map集合的get方法获取其对应的值。
				String value  = map.get(key);//把get到的key存在名为value的字符串变量中
				System.out.println("key:"+key+",value:"+value);
			}

		/*
		 程序输出:
			key:01,value:zhangsan1
			key:02,value:zhangsan2
			key:03,value:zhangsan3
			key:04,value:zhangsan4
		 */
	}
}

Map-entrySet方式

Set<Map.Entry<k,v>> entrySet 基本思想:将map集合中的映射关系存入到了Set集合中,而这个关系的数据类型就是:Map.Entry
Entry其实就是Map中的一个static内部接口,为什么要定义在内部?
因为只有当有了Map集合,有了键值对,才会有键值的映射关系。
关系属于Map集合中的一个内部事物。而且该事物在直接访问Map集合中的元素。
import java.util.*;
class MapDemo2 
{
	public static void main(String[] args) 
	{
			Map<String,String> map = new HashMap<String,String>();
	
			map.put("02","zhangsan2");
			map.put("03","zhangsan3");
			map.put("01","zhangsan1");
			map.put("04","zhangsan4");

		//1,将Map集合中的映射关系取出。存入到Set集合中。
			Set<Map.Entry<String,String>> entrySet = map.entrySet();
			//Map.Entry是一个代表集合中的映射关系的数据类型
			//Set内"泛型"要返回的是Map.Entry类型,Map.Entry本身又泛型了<k,v>的类型,这里是<String,String>
		
		//2,有了Set集合,就可以获取其迭代器拿到映射关系
			Iterator<Map.Entry<String,String>> it = entrySet.iterator();//Iterator的泛型和Set一致,因为是在操作Set中的元素

			while(it.hasNext())
			{
				Map.Entry<String,String> me = it.next();
		//3,拿到了映射关系,就可以用其特有的getKey和getValue方法得到值
				String key = me.getKey();
				String value = me.getValue();

				System.out.println("key:"+key+",value:"+value);
			}
		/*
		 程序输出:
			key:01,value:zhangsan1
			key:02,value:zhangsan2
			key:03,value:zhangsan3
			key:04,value:zhangsan4
		 */
	}
}


/*
Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。
interface Map
{
	public static interface Entry
	{
		public abstract Object getKey();
		public abstract Object getValue();
	}
}

class HashMap implements Map
{
	class Haha implements Map.Entry
	{
		public  Object getKey(){}//把抽象方法实现
		public  Object getValue(){}
	}	
}
*/

Map一对多映射关系实例

/*  
"yureban"
	Student("01" "zhangsan"); 
	Student("02" "lisi"); 
 "jiuyeban" 
 	Student("01" "wangwu"); 
	Student("02" "zhaoliu"); 
就如同一个学校有多个教室。每一个教室都有名称。 
*/  
import java.util.*;  
  
class  MapDemo3 
{  
    public static void main(String[] args)   
    {  
    	//学校集合  
    		HashMap<String,HashMap<String,String>> czbk=new HashMap<String,HashMap<String,String>>();  
    	//预热班集合  
        	HashMap<String,String> yureban=new HashMap<String,String>();  
        //就业班集合  
        	HashMap<String,String> jiuyeban=new HashMap<String,String>();  
        
        //学校中班级集合和名称的映射  
	        czbk.put("yureban",yureban);  
	        czbk.put("jiuyueban",jiuyeban);           
	    //预热班级中学号与姓名的映射  
	        yureban.put("01","zhangsan");  
	        yureban.put("02","lisi");         
        //就业班级中学号与姓名的映射  
	        jiuyeban.put("01","wangwu");  
	        jiuyeban.put("02","zhouqi");  
              
        //直接显示全部学生信息  
	        getAllStudentInfo(czbk);  
  
    }  
    
    //定义一个方法获取全部学生信息,包括在哪个班级,叫什么名字,学号多少  
	    public static void getAllStudentInfo(HashMap<String ,HashMap<String,String>> hm)  
	    {  
	        for (Iterator<String> it=hm.keySet().iterator();it.hasNext() ; )//用keySet取出方式  
	        {  
	            String s= it.next();//班级名称  
	            System.out.println(s+":");  
	            HashMap<String,String> stu=hm.get(s);//班级集合  
	  
	            getStudentInfo(stu);  
	        }  
	    }  
      
    //获取班级中学生的信息,包括姓名和学号  
	    public static void getStudentInfo(HashMap<String,String> hm)  
	    {  
	        for (Iterator<String> it=hm.keySet().iterator();it.hasNext() ; )  
	        {  
	            String key=it.next();//学号  
	            String value=hm.get(key);//姓名  
	            System.out.println(key+"..."+value);  
	        }  
	    }  
}  
/* 
程序输出结果:
	jiuyueban:
	01...wangwu
	02...zhouqi
	yureban:
	01...zhangsan
	02...lisi
 */





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值