------<ahref="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
集合框架概述
集合体系结构图
集合框架内错综复杂的继承关系、以及各种不同的接口和类与其通过语言来描述,不如用图片来进行最为直观的展示。以下所展示的图片是集合框架的最新版本的体现。
接口和类的区分图
继承关系简图
继承关系详图
集合的基本特点
1,集合是存储对象最常用的一种方式2,集合是一种容器,有了集合便可以十分方便地对多个对象进行操作3,集合的长度是可变的,而数组的长度却是固定的4,集合只能存对象,数组可以存基本数据类型5,集合可以存不同类型的对象6,集合中存储的都是对象的引用(地址)
Collection
Collection接口的通用方法
Collection定义了集合框架的共性功能。其下两个主要的子接口有:List(列表),Set(集)。
增加:
add(e);add方法的参数类型是Object。以便于接收任意类型对象。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);//修改为objhasPrevious();//判断前面有没有元素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)
注意:comparator接口里面需要复写compare方法,comparable接口里面需要复写的是compareTo方法构造一个新的空 TreeSet,它根据指定比较器进行排序。
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方法具体步骤:
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.EntryEntry其实就是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
*/