集合类
思考:
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合框架的构成及分类
集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。
集合的特点:
1.长度可以发生改变。
2.只能存储对象。
3.可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。
集合和数组的区别:
1.长度
数组固定。
集合可变。
2.存储元素
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
3.储存是否是同一类型
数组元素类型一致。
集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
4.功能是否一致
数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。
集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。
如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。
集合框架中的常用接口
1.Collection接口有两个子接口:
List(列表) ,Set(集)
2.List和Set的区别?
List:可存放重复元素,元素存取是有序的。因为该集合体系有索引。
Set:不可以存放重复元素,元素存取是无序的(存入和取出顺序不一定一致)。
Collection定义了集合框架的共性功能。
1.添加
add(e);
addAll(collection);
2.删除
remove(e);
removeAll(collection);
clear();
3.判断。
contains(e);
isEmpty();
4.获取
iterator();
size();
5.获取交集。
retainAll();
6.集合变数组。
toArray();
Collection共性功能实例
1.直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2.集合对象中存储的其实都是元素对象的引用(地址)。
3.add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。
List接口中常用的类
List可存放重复元素,元素存取是有序的。因为该集合体系有索引。
1.ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
2.LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
3.Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。
取出List集合中元素的方式:
1.get(int index):通过脚标获取元素。
2.遍历取出:
Iterator():通过迭代方法获取迭代器对象。
ListIterator():Iterator()的子类,只能用于List集合。
注意:Iterator里面的方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。
迭代器(Iterator)
1.迭代是取出集合中元素的一种方式。如同抓娃娃游戏机中的夹子。
2.因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
3.用法:
(1)Iterator iter = l.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
(2)for(Iterator iter = iterator();iter.hasNext();){
System.out.println(iter.next());
}
迭代器的使用
1.使用步骤
(1)通过集合对象获取迭代器对象。
(2)通过迭代器对象判断。
(3)通过迭代器对象获取。
2.迭代器原理
由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
(1)创建集合对象。
(2)创建元素对象。
(3)把元素添加到集合中。
(4)遍历集合。
通过集合对象获取迭代器对象。
通过迭代器对象判断。
通过迭代器对象获取。
遍历自定义对象实例(Iterator)
Iterator迭代注意事项
1.迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
2.迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
3.迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
4.迭代器的next方法返回值类型是Object,所以要记得类型转换。
5.在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。
思考:为什么next方法的返回类型是Object的呢?
因为add方法的参数是Object类型可以接受所有类型的对象。会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。
列表迭代器(ListIterator)
1.由来:由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
2.特有方法:
(1)添加
add(E e):将指定的元素插入列表(可选操作)。
(2)修改
set(E e):用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
(3)逆向
hasPrevious():逆向遍历列表
previous():返回列表中的前一个元素。
遍历自定义对象及ListIterator特有方法实例(ListIterator)
面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
1.使用集合操作。
2.使用列表迭代器操作。
Vector-枚举(Enumeration)
1.特点:
特有的取出方式:枚举(Enumeration)
2.为什么枚举被迭代器替代了?
因为枚举的名称以及方法的名称都过长。
Vector实例:
LinkedList
1.LinkedList的特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
LinkedList实例:
常见的数据结构
加钱:数据结构+算法+UML+设计模式。
数据结构:
栈,队列,数组,链表。
栈:先进后出。 如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。
栈和队列实例(LinkedList模拟)
Set接口中常用的类
元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
1.HashSet:
(1)定义:底层数据结构是哈希表。线程是不同步的,会出现安全问题,存取速度快。
(2)HashSet的子类LinkedHashSet:有序。
内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。
2.TreeSet:底层的数据结构是二叉树,线程是不同步的,可以对Set集合中的元素进行排序。
(1)对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
2.集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
3.如果使用的集合涉及到了频繁的插入,建议使用LinkedList。
HashSet
1.HashSet是如何保证元素唯一性的呢?
是通过两个方法,hashCode和equals来完成。如果元素hashCode值相同,才会判断equals是否为true。如果元素的hashCode不同,不会调用equals方法。
2.怎么重写hashCode()和equals()方法呢?
(1)hashCode():把对象的所有成员变量值相加即可。如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
(2)equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student)) ;
Student stu = (Student)obj;
return this.name.equals(stu.name) && this.age == stu.age;
所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
2.HashSet在存对象时,自动调用了HashCode()生成哈希码值,如果生成的哈希码值和该HashSet里存在元素的哈希码值相同,再自动调用equals(),反之则不调用equals()。
保证元素唯一性实例:
TreeSet
思考:
思考:
1.为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
2.数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合框架的构成及分类
集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。
集合的特点:
1.长度可以发生改变。
2.只能存储对象。
3.可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。
集合和数组的区别:
1.长度
数组固定。
集合可变。
2.存储元素
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
3.储存是否是同一类型
数组元素类型一致。
集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
4.功能是否一致
数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。
集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。
如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。
集合框架中的常用接口
1.Collection接口有两个子接口:
List(列表) ,Set(集)
2.List和Set的区别?
List:可存放重复元素,元素存取是有序的。因为该集合体系有索引。
Set:不可以存放重复元素,元素存取是无序的(存入和取出顺序不一定一致)。
Collection定义了集合框架的共性功能。
1.添加
add(e);
addAll(collection);
2.删除
remove(e);
removeAll(collection);
clear();
3.判断。
contains(e);
isEmpty();
4.获取
iterator();
size();
5.获取交集。
retainAll();
6.集合变数组。
toArray();
Collection共性功能实例
import java.util.List;
import java.util.ArrayList;
public class CollectionDemo{
public static void main(String[] args){
ArrayList al = new ArrayList();
//添加一个元素
al.add("张三");
al.add("李四");
al.add("王五");
sop(al.get(1));//输出一个元素
al.remove("王五");//删除一个元素
sop(al.size());//打印下集合的长度 结果为2.因为删了一个
al.clear();//清空集合
sop(al.contains("王五"));//判断该列表中是否有这个元素
sop(al.isEmpty);//判断该列表是否为空
method();
}
//取交集和取交集
public static void method(){
List list1 = new ArrayList();
list1.add("张三");
list1.add("李四");
list1.add("王五");
List list2 = new ArrayList();
list2.add("张三");
list2.add("李四");
list2.add("赵六");
//list1.retainAll(list2);//取交集。拿list1元素跟list2的元素比,如果相同就保留。调用父类的方法
//sop(list1);
list1.removeAll(list2);//删除交集。拿list1的元素跟list2的元素比,如果相同就删除。调用父类的方法
sop(list1);
}
public static void sop(Object obj){
System.out.println(obj);
}
}
注意:
1.直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2.集合对象中存储的其实都是元素对象的引用(地址)。
3.add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。
List接口中常用的类
List可存放重复元素,元素存取是有序的。因为该集合体系有索引。
1.ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
2.LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
3.Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。
取出List集合中元素的方式:
1.get(int index):通过脚标获取元素。
2.遍历取出:
Iterator():通过迭代方法获取迭代器对象。
ListIterator():Iterator()的子类,只能用于List集合。
注意:Iterator里面的方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。
迭代器(Iterator)
1.迭代是取出集合中元素的一种方式。如同抓娃娃游戏机中的夹子。
2.因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
3.用法:
(1)Iterator iter = l.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
(2)for(Iterator iter = iterator();iter.hasNext();){
System.out.println(iter.next());
}
迭代器的使用
1.使用步骤
(1)通过集合对象获取迭代器对象。
(2)通过迭代器对象判断。
(3)通过迭代器对象获取。
2.迭代器原理
由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
(1)创建集合对象。
(2)创建元素对象。
(3)把元素添加到集合中。
(4)遍历集合。
通过集合对象获取迭代器对象。
通过迭代器对象判断。
通过迭代器对象获取。
遍历自定义对象实例(Iterator)
import java.util.ArrayList;
import java.util.Iterator;
/*
* 遍历自定义对象(Iterator)
*/
//学生类
class Student{
private String name;
private int age;
Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class IteratorDemo {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Student("张三", 20));
al.add(new Student("李四", 21));
al.add(new Student("王五", 22));
for(Iterator it = al.iterator();it.hasNext();){
Student stu = (Student)it.next();
System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
}
}
}
Iterator迭代注意事项
1.迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
2.迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
3.迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
4.迭代器的next方法返回值类型是Object,所以要记得类型转换。
5.在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。
思考:为什么next方法的返回类型是Object的呢?
因为add方法的参数是Object类型可以接受所有类型的对象。会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。
列表迭代器(ListIterator)
1.由来:由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
2.特有方法:
(1)添加
add(E e):将指定的元素插入列表(可选操作)。
(2)修改
set(E e):用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
(3)逆向
hasPrevious():逆向遍历列表
previous():返回列表中的前一个元素。
遍历自定义对象及ListIterator特有方法实例(ListIterator)
import java.util.ArrayList;
import java.util.ListIterator;
/*
* 遍历自定义对象,及ListIterator特有方法
*/
//学生类
class Student{
private String name;
private int age;
Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ListIteratorDemo {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Student("张三", 20));
al.add(new Student("李四", 21));
al.add(new Student("王五", 22));
ListIterator it = al.listIterator();
//遍历元素
sop(al);
//正向遍历列表
while(it.hasNext()){
Student stu = (Student)it.next();
if(stu.getName() == "李四"){
//删除元素
it.remove();
//添加元素
it.add(new Student("赵六", 24));
}
else if (stu.getName() == "王五") {
//修改元素
it.set(new Student("贺七", 23));
}
}
System.out.println("修改后:");
//遍历元素
sop(al);
System.out.println("逆向遍历列表:");
//逆向遍历列表
while(it.hasPrevious()){
Student stu = (Student)it.previous();
System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
}
}
//遍历元素,ArrayList的get方法
public static void sop(ArrayList al)
{
for(int x = 0; x<al.size(); x++){
Student stu = (Student)al.get(x);
System.out.println("name:" + stu.getName() + ",age:" + stu.getAge());
}
}
}
面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
1.使用集合操作。
2.使用列表迭代器操作。
Vector-枚举(Enumeration)
1.特点:
特有的取出方式:枚举(Enumeration)
2.为什么枚举被迭代器替代了?
因为枚举的名称以及方法的名称都过长。
Vector实例:
import java.util.*;
class VectorDemo
{
public static void main(String[] args) {
Vector v = new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
v.add("java04");
Enumeration en = v.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
}
}
LinkedList
1.LinkedList的特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
LinkedList实例:
import java.util.*;
class LinkedListDemo {
public static void main(String[] args) {
LinkedList link = new LinkedList();
link.addLast("java01");
link.addLast("java02");
link.addLast("java03");
link.addLast("java04");
//sop(link);
//sop(link.getFirst());
//sop(link.getFirst());
//sop(link.getLast());
//sop(link.removeFirst());
//sop(link.removeFirst());
//sop("size="+link.size());
while(!link.isEmpty()){
//移除并返回此列表的最后一个元素
sop(link.removeLast());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
常见的数据结构
加钱:数据结构+算法+UML+设计模式。
数据结构:
栈,队列,数组,链表。
栈:先进后出。 如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。
栈和队列实例(LinkedList模拟)
/*
使用LinkedList模拟一个堆栈或者队列数据结构。
*/
import java.util.*;
class DuiLie
{
private LinkedList link;
DuiLie(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addFirst(obj);
}
public Object myGet(){
return link.removeFirst();
}
public boolean isNull(){
return link.isEmpty();
}
}
class LinkedListTest{
public static void main(String[] args) {
DuiLie dl = new DuiLie();
dl.myAdd("java01");
dl.myAdd("java02");
dl.myAdd("java03");
dl.myAdd("java04");
while(!dl.isNull()){
System.out.println(dl.myGet());
}
}
}
Set接口中常用的类
元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
1.HashSet:
(1)定义:底层数据结构是哈希表。线程是不同步的,会出现安全问题,存取速度快。
(2)HashSet的子类LinkedHashSet:有序。
内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。
2.TreeSet:底层的数据结构是二叉树,线程是不同步的,可以对Set集合中的元素进行排序。
(1)对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
2.集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
3.如果使用的集合涉及到了频繁的插入,建议使用LinkedList。
HashSet
import java.util.HashSet;
import java.util.Iterator;
public class HashSetDemo {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add("java-01");
hs.add("java-02");
hs.add("java-03");
Iterator it = hs.iterator();
while(it.hasNext()){
sop(it.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
思考:
1.HashSet是如何保证元素唯一性的呢?
是通过两个方法,hashCode和equals来完成。如果元素hashCode值相同,才会判断equals是否为true。如果元素的hashCode不同,不会调用equals方法。
2.怎么重写hashCode()和equals()方法呢?
(1)hashCode():把对象的所有成员变量值相加即可。如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
(2)equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student)) ;
Student stu = (Student)obj;
return this.name.equals(stu.name) && this.age == stu.age;
所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:
1.对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
2.HashSet在存对象时,自动调用了HashCode()生成哈希码值,如果生成的哈希码值和该HashSet里存在元素的哈希码值相同,再自动调用equals(),反之则不调用equals()。
保证元素唯一性实例:
import java.util.HashSet;
import java.util.Iterator;
/*往hashSet集合中存入自定义对象,姓名和年龄相同为同一个人,重复元素。
*/
//人类
class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int hashCode(){
HashSetTest.sop(name + ":hashCode");
return name.hashCode()+age*50;
}
public boolean equals(Object obj){
//判断是不是同一个类型的对象
if(!(obj instanceof Person)){
return false;
}
Person p = (Person)obj;
HashSetTest.sop(name + " equals " + p.name);
return this.name.equals(p.name) && this.age == p.age;
}
}
public class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("张三", 20));
hs.add(new Person("李四", 20));
hs.add(new Person("张三", 20));
hs.add("张三");
Iterator it = hs.iterator();
while (it.hasNext()) {
Object obj = it.next();
if(obj instanceof Person){
Person p = (Person)obj;
sop("name:" + p.getName() + ",age:" + p.getAge());
}else {
sop(obj);
}
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
TreeSet
思考:
TreeSet的排序是如何进行的呢?
通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。
TreeSet集合两种实现排序方式:
1.自然排序(元素具备比较性)
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。也种方式也成为元素的自然顺序,或者叫做默认顺序。调用TreeSet的无参构造,要求对象所属的类实现Comparable接口。
2.比较器排序(集合具备比较性)
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。调用TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。
自然排序实例:
比较器排序实例:
通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。
TreeSet集合两种实现排序方式:
1.自然排序(元素具备比较性)
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。也种方式也成为元素的自然顺序,或者叫做默认顺序。调用TreeSet的无参构造,要求对象所属的类实现Comparable接口。
2.比较器排序(集合具备比较性)
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。调用TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。
自然排序实例:
import java.util.Iterator;
import java.util.TreeSet;
/**
* 需求:
* 1.演示TreeSet的一个特点:把Set集合中元素进行排序
* 2.往TreeSet集合中存储自定义对象学生。
* 想按照学生的年龄进行排序。
*
* 记住,排序时,当主要条件相同时,一定判断一下次要条件。
*/
//学生类
class Student implements Comparable{
private String name;
private int age;
Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
//根据年龄来排序
public int compareTo(Object obj){
if(!(obj instanceof Student)){
throw new RuntimeException("不是学生对象");
}
//向下转型
Student stu = (Student)obj;
System.out.println(this.name + "compareTo age" + stu.name);
//比较
if(this.age > stu.age){
return 1;
}else if(this.age == stu.age){
return this.name.compareTo(stu.name);
}
return -1;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
/*1.演示TreeSet的一个特点:把Set集合中元素进行排序
TreeSet ts = new TreeSet();
ts.add("abc");
ts.add("abc1");
ts.add("1abc");
ts.add("bbc");
//迭代器取出元素
for(Iterator iterator = ts.iterator(); iterator.hasNext();){
iptOut(iterator.next());
}
*/
//2.往TreeSet集合中存储自定义对象学生。
TreeSet ts = new TreeSet();
ts.add(new Student("张三", 18));
ts.add(new Student("李四", 20));
ts.add(new Student("王五", 29));
ts.add(new Student("赵六", 18));
//迭代器取出元素
for(Iterator iterator = ts.iterator(); iterator.hasNext();){
Student stu = (Student)iterator.next();
out(stu.getName() + ":" + stu.getAge());
}
}
public static void out(Object e){
System.out.println(e);
}
}
比较器排序实例:
import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/*
当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
定义一个类,实现Comparator接口,覆盖compare方法。
*/
//学生类,实现Comparable借口,使元素具备比较性
class Student implements Comparable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = 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类对象");
}
//向下转型
Student stu = (Student)obj;
System.out.println(this.name + "compareTo age" + stu.name);
//比较
if(this.age > stu.age){
return 1;
}else if (this.age == stu.age) {
System.out.println(this.name + "compareTo name" + stu.name);
return this.name.compareTo(stu.name);
}
return -1;
}
}
//比较器
class MyComparator implements Comparator{
public int compare(Object obj1, Object obj2){
Student stu1 = (Student)obj1;
Student stu2 = (Student)obj2;
//先比较姓名,再比较年龄
int num = stu1.getName().compareTo(stu2.getName());
if(num == 0){
if(stu1.getAge() > stu2.getAge()){
return 1;
}else if(stu1.getAge() < stu2.getAge()){
return -1;
}
return 0;
}
return num;
}
}
class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator());
ts.add(new Student("张三 ",20));
ts.add(new Student("李四 ",19));
ts.add(new Student("王五 ",22));
ts.add(new Student("张三 ",20));
Iterator it = ts.iterator();
while (it.hasNext()) {
Student stu = (Student)it.next();
System.out.println("name:" +stu.getName() + ",age:" + stu.getAge());
}
}
}
注意:当两种排序都存在时,以比较器为主。