Java面向对象————集合框架(Collection)
2014-07-16 17:10
393 查看
Java面向对象——集合框架(Collection)
面向对象
集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。
集合的特点:
长度可以发生改变。
只能存储对象。
可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。
集合和数组的区别:
长度问题
数组固定。
集合可变。
存储元素问题
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
是否同一类型
数组元素类型一致。
集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
功能是否一致
数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。
集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。
如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。
Collection:定义了集合框架的共性功能。
|------List(列表):元素是有序的,元素可以重复,因为该集合体系有索引。
|--ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
|--LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
|--Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。
|------Set(集):元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。线程是不同步的。
采用散列函数对元素进行排序(Asiic),是专门为快速查询而设计的。存入HashSet的对象必须定义hashCode方法。
|--LinkedHashSet:有序。
内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。
|--TreeSet:底层的数据结构是二叉树。线程是不同步的。
对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
采用红黑树的数据结构进行排序元素,使用它可以从Set中提取有序(升序或者降序)的序列。
需要注意的是,存入自定义类时,TreeSet需要维护元素的存储顺序,因此自定义类要实现Comparable接口并定义compareTo方法。
注意:集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
注意:如果使用的集合涉及到了频繁的插入,建议使用LinkedList。
Collection
ArrayList特有方法:既然是对数据进行操作的,当然就有 增、删、改、查 这四个方法。
代码示例:
import java.util.List;
import java.util.ArrayList;
public class Test{
public static void main(String[] args){
ArrayList al = new ArrayList();
al.add("黑马程序员——Java01");//添加了一个元素
al.add("黑马程序员——Java02");
al.add("黑马程序员——Java03");
al.add("黑马程序员——Java04");
sop(al.get(1));
al.remove("黑马程序员——Java04");//删除一个元素
sop(al.size());//打印下集合的长度 结果为3.因为删了一个
al.clear();//清空集合
sop(al.contains("黑马程序员——Java03"));
sop(al.isEmpty);
method();
}
public static void method(){
List list1 = new ArrayList();
list1.add("黑马程序员——Java01");
list1.add("黑马程序员——Java02");
list1.add("黑马程序员——Java03");
list1.add("黑马程序员——Java04");
List list2 = new ArrayList();
list2.add("黑马程序员——Java01");
list2.add("黑马程序员——Java02");
list2.add("黑马程序员——Java05");
list2.add("黑马程序员——Java06");
//list1.retainAll(list2);//取焦急。拿list1元素跟list2的元素比,如果相同就保留。调用父类的方法
//sop(list1);
list1.removeAll(list2);//删除焦急。拿list1的元素跟list2的元素比,如果相同就删除。调用父类的方法
sop(list1);
}
public static void sop(Object obj){
System.out.println(obj);
}
}
注意:Test.java
使用了未经检查或不安全的操作。 注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。
集合注意:
1、直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2、集合对象中存储的其实都是元素对象的引用。
3、add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。
当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。
迭代器(Iterator)
1、什么是迭代器?其实就是集合的取出元素的方式。
就把取出方式定义在集合的内部,这样取出方式就可以直接访问结合内容的元素。那么取出方式就定义成了内部类。
而每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容:取出。那么可以写共性取出。
那么这些内部类都符合一个规则。该规则是Iterator。
迭代时注意:在迭代时候,不可以通过集合对象的方法操作集合中的元素,因为会发生 ConcurrenModificationException异常。所以在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。
如何获取集合的取出对象呢?
通过一个对外提供的方法:iterator()
Iterator
迭代器的使用
1.使用步骤
1)通过集合对象获取迭代器对象。
2)通过迭代器对象判断。
3)通过迭代器对象获取。
2.迭代器原理
由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。
这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种
集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
1)创建集合对象。
2)创建元素对象。
3)把元素添加到集合中。
4)遍历集合。
1.通过集合对象获取迭代器对象。
2.通过迭代器对象判断。
3.通过迭代器对象获取。
4.Collection存储字符串和自定义对象并遍历。
存储字符串代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Test{
public static void main(String[]
args){
ArrayList al = new ArrayList();
al.add("黑马程序员——Java01");
al.add("黑马程序员——Java02");
al.add("黑马程序员——Java03");
al.add("黑马程序员——Java04");
for(Iterator iter = al.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
存储自定义对象代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
class Student{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "姓名:"+name+"...年龄:"+age;
}
}
public class Test{
public static void main(String[] args){
List list = new ArrayList();
list.add(new Student("黑马程序员——张三",23));
list.add(new Student("黑马程序员——李四",24));
list.add(new Student("黑马程序员——王五",25));
list.add(new Student("黑马程序员——赵六",26));
for(Iterator iter = list.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。
ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
ListIterator
List集合的遍历方式有俩种:一种是Iterator 一种是ListIterator
ListIterator代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
public class Test{
public static void main(String[] args){
List list = new ArrayList();
list.add("黑马程序员——Java01");
list.add("黑马程序员——Java02");
list.add("黑马程序员——Java03");
list.add("黑马程序员——Java04");
for(ListIterator listIter = list.listIterator();listIter.hasNext();){
String name = listIter.next();
sop(name);
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
ListIterator迭代器
是Iterator的子接口。有自己的特有功能,可以逆向遍历数据,但是需要先正向遍历。一般不用。
面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
使用集合操作。
使用列表迭代器操作。
常见的数据结构
加钱:数据结构+算法+UML+设计模式。
数据结构:
栈,队列,数组,链表。
栈:先进后出。 如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。
LinkedList特有方法:
1.6出现了替换方法
集合框架(LinkedList练习)
/*
使用LinkedFirst模拟一个堆栈或者队列数据结构
堆栈:先进后出 如同一个罐子
队列:先进先出 如同一个水管
*/
import java.util.LinkedList;
class Duilie{
private LinkedList link;
Duilie(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addFirst(obj);
}
public Object myGet(){
return link.removeLast();
}
public boolean isNull(){
return link.isEmpty();
}
}
public class Test{
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());
}
}
}
集合框架(ArrayList练习)
/*
去除ArrayList集合的重复元素。
*/
import java.util.ArrayList;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("Java01");
al.add("Java02");
al.add("Java03");
al.add("Java02");
al.add("Java04");
al = singleElement(al);
System.out.println(al);
}
public static ArrayList singleElement(ArrayListal){
ArrayList newAl = new ArrayList();
for(Iterator it
= al.iterator();it.hasNext();){
Object obj = it.next();
if(!newAl.contains(obj)){//如果是同一个元素,那么就不存入。
newAl.add(obj);
}
}
return newAl;
}
}
集合框架(ArrayList练习2)
/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素
比如,存人对象,视为同一个人,为重复元素。
思路:
1、对人描述,将数据封装进人对象
2、定义容器,将人进行存入
3、取出
List集合判断是否包含其实是根据这个对象判断是否和内部的对象, equals,可以复写这个equals,写自己的判断
当List remove 删除时候,也是i先equals判断有没有,如果所以要注意
*/
import java.util.ArrayList;
import java.util.Iterator;
class Person{
private String name;
privateint age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
returnname.equals(p.name) && this.age==p.age;
}
public String getName(){
return name;
}
publicint getAge(){
return age;
}
}
public class Test{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add(new Person("list01",31));
al.add(new Person("list02",32));
al.add(new Person("list02",32));
al.add(new Person("list03",33));
al.add(new Person("list04",34));
al.add(new Person("list04",34));
al.add(new Person("list05",35));
al = singleElement(al);
for(Iterator it = al.iterator();it.hasNext();){
Person p = (Person)it.next();
sop(p.getName()+":"+p.getAge());
}
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
public static ArrayList singleElement(ArrayList al){
ArrayList newAl = new ArrayList();
for(Iterator it = al.iterator();it.hasNext();){
Object obj = it.next();
if(!newAl.contains(obj)){
newAl.add(obj);
}
}
return newAl;
}
}
集合框架(Vector中的枚举)
import java.util.Vector;
import java.util.Enumeration;
public class Test{
public static void main(String[]
args){
Vector v = new Vector();
v.add("Java01");
v.add("Java02");
v.add("Java03");
v.add("Java04");
for(Enumeration en
= v.elements();en.hasMoreElements();){
sop(en.nextElement());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
枚举就是Vector特有的取出方式
发现枚举和迭代器很像。
其实枚举和迭代器是一样的。
因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了。
Vector和ArrayList在使用上非常相似,都可用来表示一组数量可变的对象应用的集合,并且可以随机地访问其中的元素。
Vector的方法都是同步的(Synchronized),是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。(建议使用ArrayList,因为高效,就算是多线程咱们也不用怕,因为可以自己加锁)
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Set :元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
HashSet 底层数据结构是哈希表。
HashSet是如何保证元素唯一性的呢?
是通过两个方法,hashCode和equals来完成。
如果元素hashCode值相同,才会判断equals是否为true。
如果元素的hashCode不同,不会调用equals方法。
怎么重写hashCode()和equals()方法呢?
hashCode():把对象的所有成员变量值相加即可。
如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student))
;
Student stu = (Student)obj;
return this.name.equals(stu.name) && this.age
== stu.age;
所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
举例:
publicint hashCode(){
return name.hashCode()+age*21;
}
public boolean equals(Object obj){
if(!obj instanceof Person)
return false;
Person p=(Person)obj;
return name.equals(p.name) && age==p.age;
}
TreeSet :底层的数据结构是二叉树。线程是不同步的。
存储字符串对象代码示例:
import java.util.TreeSet;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
TreeSet ts =new TreeSet();
ts.add("Java01");
ts.add("Java03");
ts.add("Java02");
ts.add("Java04");
for(Iterator it = ts.iterator();it.hasNext();){
sop(it.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
TreeSet集合两种实现排序方式:
自然排序(元素具备比较性)
TreeSet的无参构造,要求对象所属的类实现Comparable接口。
比较器排序(集合具备比较性)
TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。
Comparable
Comparator
Comparable存储自定义对象代码示例:
/*
需求:往
TreeSet 集合中存储自定义对象学生。
想按照学生的年龄进行排序。
*/
import java.util.Iterator;
import java.util.TreeSet;
class Student implements Comparable{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
publicint compareTo(Object obj){
if(!(obj instanceof Student)){
throw new RuntimeException("不是学生对象");
}
Student s = (Student)obj;
int num = this.age-s.age;
if(num==0)
return
this.name.comparTo(s.name);
else
return num;
}
public String toString(){
return name +"..."+age;
}
}
public class Test{
public static void main(String[] args){
TreeSet ts =new TreeSet();
ts.add(new Student("黑马程序员——张三",21));
ts.add(new Student("黑马程序员——王五",23));
ts.add(new Student("黑马程序员——周七",19));
ts.add(new Student("黑马程序员——赵六",18));
for(Iterator iter = ts.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
Comparator存储自定义对象代码示例:
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Comparator;
class Student{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
publicint getAge(){
return age;
}
}
class MyCompare implements Comparator{
publicint compare(Object obj1,Object obj2){
Student s1 = (Student)obj1;
Student s2 = (Student)obj2;
int num =new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
if(num==0)
return s1.getName().compareTo(s2.getName());
returnnum;
}
}
public class Test{
public static void main(String[] args){
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student("黑马程序员——张三",21));
ts.add(new Student("黑马程序员——王五",23));
ts.add(new Student("黑马程序员——周七",19));
ts.add(new Student("黑马程序员——赵六",18));
for(Iterator iter = ts.iterator();iter.hasNext();){
Student stu = (Student)iter.next();
sop(stu.getName()+"..."+stu.getAge());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
注意:如果同时有两种方案,以谁为主呢?以比较器为主。
集合框架(TreeSet练习)
/*
练习,按照字符串长度排序
字符串本身具备比较器,但是他的比较方式不是所需要的
这时就只能使用比较器。
也可以不定义类,写匿名内部类也可以。
*/
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Comparator;
public class Test{
public
static void main(String args[]){
TreeSet ts =new TreeSet(new Comparator(){
publicint compare(Object obj1,Object obj2){
String s1
= (String)obj1;
String s2
= (String)obj2;
int num
= s1.length()-s2.length();
if(num==0)
return s1.compareTo(s2);
return num;
}
});
ts.add("黑马");
ts.add("csdn");
ts.add("黑马论坛");
ts.add("云计算");
for(Iterator it
= ts.iterator();it.hasNext();){
sop(it.next());
}
}
public
static void sop(Object obj){
System.out.println(obj);
}
}
集合框架(泛型概述)
/*
泛型:JDK1.5 以后出现的新特性,用于解决安全问题,是一个安全机制。
好处:
1、将运行时期出现的问题(classCastException),转移到了编译时期。
方便于程序员解决问题。让运行时期问题减少,安全。
2、避免了强制转换的麻烦。
泛型格式:通过 <> 来定义要操作的引用数据类型。
什么时候使用泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型
其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到尖括号中即可,和函数传参数一样。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
List<Integer>
al = new ArrayList<Integer>();
al.add(4);//自动装箱功能,现在作为了解。以后会学到的,包括泛型。
al.add(new Integer(4));
int x=5;
al.add(x);
for(Iterator<Integer>
it = al.iterator();it.hasNext();){
sop(it.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
集合框架(泛型类)
/*
泛型类
舍呢么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候
早起定义Object 来完成扩展
现在,定义泛型来完成扩展。
*/
//JDK1.5之前的做法
class Tool{
private Object obj;
public void setObject(Object obj){
this.obj=obj;
}
public Object getObject(){
return obj;
}
}
//JDK1.5泛型出现后,用泛型的做法
//泛型类
class Utils<E>{
private E o;
public void setObject(E o){
this.o = o;
}
public E getObject(){
return o;
}
}
class Worker{
}
class Student{
}
public class Test{
public static void main(String args[]){
//JDK1.4做法。
Tool t = new Tool();
//可以传入Student
t.setObject(new Student());
Objectobj = t.getObject();
Student stu = (Student)obj;
//泛型出现以后的做法
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Worker());
Worker w = u.getObject();
}
public static void sop(Object obj){
System.out.println(obj);
}
}
泛型可以让程序员避免强制转换的麻烦,并且把运行时强转问题转化到了编译时期。
集合框架(泛型方法)
/*
泛型类定义的泛型,在整个类中有效,如果被方法是用到泛型,类的对象明确。操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上,而不定义在类上。
public <T> void show(T t){
}
*/
//类型已经确定.
class GenericTest<T>{
public void show(T t){
sop("show:"+t);
}
public void print(T t){
sop("print:"+t);
}
public static void sop(Objectobj){
System.out.println(obj);
}
}
//类型不确定,使用泛型方法
class GenericTest2{
//泛型方法
public <Q>void show(Q q){
sop("show_2:"+q);
}
public <Q> void print(Q q){
sop("print_2:"+q);
}
public static void sop(Object obj){
System.out.println(obj);
}
}
public class Test{
public static void main(String args[]){
//泛型确定
GenericTest<String> d = new GenericTest<String>();
d.show("黑马程序员");
d.print("csdn社区");
//泛型不确定
GenericTest2 d2 = new GenericDemo_2();
d2.show("黑马程序员");
d2.print(new Integer(4));
d2.print(4);
}
public static void sop(Objectobj){
System.out.println(obj);
}
}
集合框架(静态方法泛型)
泛型类中也可以定义泛型方法
特殊之处:静态方法不可以访问类上定义的泛型,因为只有在实例化的时候,才会使用泛型
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
<T> 泛型写在返回值前。
/*
public
static <T> void setName(T t){
}
*/
集合框架(泛型接口)
interface Inter<T>{
void show(T t);
}
class InterImpl implements Inter<String>{
void show(Stringt)//这里只能是
String{
}
}
class InterImpl2<T> implements Inter<T>{
void show(T t);//这里类型不固定{
}
}
集合框架(泛型通配符)
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class Test{
public static voidmain(String args[]){
List<String> al = new ArrayList<String>();
al.add("黑马程序员");
al.add("CSDN社区");
al.add("黑马程序员论坛");
List<Integer> al1 = newArrayList<Integer>();
al1.add(1);
al1.add(2);
al1.add(3);
printCollection(al);
printCollection(al1);
}
public static void printCollection(List<?> al){
for(Iterator<?> it = al.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
集合框架(泛型限定(上限))
/*
? 通配符
? extends E:可以接收 E 类型或者 E 的子类型。上限。
? super E:可以接收E类型或者E的父类型,下限
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
class Person{
private String name;
Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class Student extends Person{
Student(String name){
super(name);
}
}
public class Test{
public static void main(String args[]){
List<Person> al =newArrayList<Person>();
al.add(new Person("张三"));
al.add(new Person("李四"));
al.add(new Person("王五"));
List<Student> al1 = new ArrayList<Student>();
al1.add(new Student("赵六"));
al1.add(new Student("周七"));
al1.add(new Student("郑八"));
printCollection(al);
printCollection(al1);
}
//printCollection(al1);这是错误的,不能直接写
//public static void printCollection(ArrayList<Person> al)这样只能接受person
public static void printCollection(List<? extends Person> al){/*这样可以接收Person和Person的子类为了面向接口编程,而且上面定义的容器也是面向接口编程,这里写成List,否则编译不通过。因为ArrayList子类不能接收List父类。*/
for(Iterator<? extends Person> it = al.iterator();it.hasNext();){
System.out.println(it.next().getName());
}
}
}
集合框架(泛型限定(下限))
/*
? 通配符
? extends E:可以接收 E 类型或者 E 的子类型。上限。
? super E: 可以接收E类型或者E的父类型,下限
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
class Person{
private String name;
Person(String name){
this.name = name;
}
public String toString(){
return name;
}
}
class Student extends Person{
Student(String name){
super(name);
}
}
public class Test{
public static void main(String args[]){
List<Person> al =new ArrayList<Person>();
al.add(new Person("张三"));
al.add(new Person("李四"));
al.add(new Person("王五"));
List<Student> al1 = new ArrayList<Student>();
al1.add(new Student("赵六"));
al1.add(new Student("周七"));
al1.add(new Student("郑八"));
printCollection(al);
printCollection(al1);
}
public static void printCollection(List<? super Student> al){
for(Iterator<? super Student> it = al.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
使用泛型的时制:
是么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义的Object来完成扩展。现在定义泛型类来完成扩展
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让相同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
特殊之处:
静态方法不可以访问类上定义的泛型。因为只有在实例化的时候,才会使用泛型。
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
? 通配符。也可以理解为占位符。
泛型的限定:
?extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
面向对象
集合概述:
Java是面向对象语言,如果我们要针对多个对象进行操作,就必须对多个对象进行存储。
而对多个元素进行存储,前面我们学习过数组,数组的弊端,长度固定。这样,数组将不能满足变化的要求。所以,Java就提供了集合供我们使用。
集合的特点:
长度可以发生改变。
只能存储对象。
可以存储多种类型对象(一般存储的还是同一种,因为1.5JDK的新特性 泛型)。
集合和数组的区别:
长度问题
数组固定。
集合可变。
存储元素问题
数组可以是基本类型,也可以是引用类型。
集合只能是引用类型。(JDK1.5以后还可以存储基本数据类型,因为JDK1.5自动装箱拆箱)
是否同一类型
数组元素类型一致。
集合元素类型可以不一致。(建议还是同一种类型,因为JDK1.5出现了泛型)
功能是否一致
数组只能对数据进行存取操作,当然删除数组中的元素还可以删除,就是null。
集合不但可以对数据进行基本操作,还提供了更强大的功能,比如删除 修改...。
集合体系的由来
集合是存储多个元素的容器,但是,由于数据结构不同,Java就提供了多种集合类。
而这多种集合类有共性的功能,所以,通过不断的向上抽取,最终形成了集合体系
结构。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称之为:数据结构。
数据结构:数据存储的方式。
如何学习和使用一个继承体系呢?
学习顶层:因为顶层定义的是共性内容。
使用底层:因为底层才是具体的实现。
简单一句话:参阅顶层内容,建立底层对象。
Collection:定义了集合框架的共性功能。
|------List(列表):元素是有序的,元素可以重复,因为该集合体系有索引。
|--ArrayList:底层是数组数据结构,查询速度很快,但是增删稍慢,线程不同步,默认10个元素。(线程不同步)
|--LinkedList:底层是链表数据结构,查询很慢,增删速度很快。(线程不同步)
|--Vector:底层是数组数据结构,和ArrayList一样,查询,增删,都很慢,Vector是1.0出现,ArrayList是1.2出现,(线程同步)但已经不用了。
|------Set(集):元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。线程是不同步的。
采用散列函数对元素进行排序(Asiic),是专门为快速查询而设计的。存入HashSet的对象必须定义hashCode方法。
|--LinkedHashSet:有序。
内部使用散列以加快查询速度,同时使用链表维护元素插入的次序,在使用迭代器遍历Set时,结果会按元素插入的次序显示。
|--TreeSet:底层的数据结构是二叉树。线程是不同步的。
对Set集合中的元素的进行指定(我们指定的比较器)顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
采用红黑树的数据结构进行排序元素,使用它可以从Set中提取有序(升序或者降序)的序列。
需要注意的是,存入自定义类时,TreeSet需要维护元素的存储顺序,因此自定义类要实现Comparable接口并定义compareTo方法。
注意:集合只能保存引用数据类型,也就是存储的是对象的地址值,而不是对象本身。集合中的元素相当于引用类型变量。
注意:如果使用的集合涉及到了频繁的插入,建议使用LinkedList。
Collection
布尔型 | add(E e)此处的E现在可以当成Object去理解 将指定的元素添加到此列表的尾部。 |
布尔型 | addAll(Collection<? extendsE> c) 将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
无返回值 | clear() 移除此列表中的所有元素。 |
布尔型 | contains(Object o) 如果此列表中包含指定的元素,则返回 true。 |
布尔型 | containsAll(Collection<?> c) 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
布尔型 | equals(Object o) 比较此 collection 与指定对象是否相等。 |
整数 | hashCode() 返回此 collection 的哈希码值。 |
布尔型 | isEmpty() 如果此 collection 不包含元素,则返回 true。 |
迭代器 | iterator() 返回在此 collection 的元素上进行迭代的迭代器。 |
布尔型 | remove(Object o) 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。 |
布尔型 | removeAll(Collection<?> c) 移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。 |
布尔型 | retainAll(Collection<?> c) 仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。 |
整数 | size() 返回此 collection 中的元素数。 |
对象[] | toArray() 返回包含此 collection 中所有元素的数组。 |
| toArray(T[] a) 返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 |
无返回值 | add(int index,E element) 将指定的元素插入此列表中的指定位置。 |
布尔型 | addAll(Collection<? extendsE> c) 按照指定 collection 的迭代器所返回的元素顺序,将该 collection 中的所有元素添加到此列表的尾部。 |
布尔值 | addAll(int index,Collection<? extendsE> c) 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中。 |
E | get(int index) 返回此列表中指定位置上的元素。 |
整数 | indexOf(Object o) 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 -1。 |
整数 | lastIndexOf(Object o) 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 -1。 |
E | set(int index,E element) 用指定的元素替代此列表中指定位置上的元素。 |
import java.util.List;
import java.util.ArrayList;
public class Test{
public static void main(String[] args){
ArrayList al = new ArrayList();
al.add("黑马程序员——Java01");//添加了一个元素
al.add("黑马程序员——Java02");
al.add("黑马程序员——Java03");
al.add("黑马程序员——Java04");
sop(al.get(1));
al.remove("黑马程序员——Java04");//删除一个元素
sop(al.size());//打印下集合的长度 结果为3.因为删了一个
al.clear();//清空集合
sop(al.contains("黑马程序员——Java03"));
sop(al.isEmpty);
method();
}
public static void method(){
List list1 = new ArrayList();
list1.add("黑马程序员——Java01");
list1.add("黑马程序员——Java02");
list1.add("黑马程序员——Java03");
list1.add("黑马程序员——Java04");
List list2 = new ArrayList();
list2.add("黑马程序员——Java01");
list2.add("黑马程序员——Java02");
list2.add("黑马程序员——Java05");
list2.add("黑马程序员——Java06");
//list1.retainAll(list2);//取焦急。拿list1元素跟list2的元素比,如果相同就保留。调用父类的方法
//sop(list1);
list1.removeAll(list2);//删除焦急。拿list1的元素跟list2的元素比,如果相同就删除。调用父类的方法
sop(list1);
}
public static void sop(Object obj){
System.out.println(obj);
}
}
注意:Test.java
使用了未经检查或不安全的操作。 注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。
集合注意:
1、直接存储基本类型值也是可以的,因为JDK1.5后,有自动装箱技术,会将基本数据类转换成数据对象。JDK1.4绝对不行,因为集合中存放的对象的引用,当直接存放基本类型值的时候,会将这个基本类型值变成一个对象(自动装箱)。
2、集合对象中存储的其实都是元素对象的引用。
3、add方法的参数是Object类型可以接受所有类型的对象。但会出现向上转型,取出元素时的类型还是Object,不能使用具体对象的特有内容,想要是有特有内容,必须向下转型。集合框架中存放的都是对象的地址。当我们想用对象的特有方法的时候必须向下转型。
当我们想直接打印一个集合,其实是调用了这个对象的toString方法,当我们要打印一个对象的长度的时候,首先我们会想到length方法。但是由于我们添加数据的时候用的add方法,而add方法的参数类型是Object,也就说把存进去的数据向上转型了。但是我们都知道Object是没有length方法,此时无法使用length方法。那么就要向下转型才可以,只有使用子类的特有方法size。
迭代器(Iterator)
1、什么是迭代器?其实就是集合的取出元素的方式。
就把取出方式定义在集合的内部,这样取出方式就可以直接访问结合内容的元素。那么取出方式就定义成了内部类。
而每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容:取出。那么可以写共性取出。
那么这些内部类都符合一个规则。该规则是Iterator。
迭代时注意:在迭代时候,不可以通过集合对象的方法操作集合中的元素,因为会发生 ConcurrenModificationException异常。所以在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作
如果想要其他的操作,如添加、修改等,就需要使用其子接口,ListIterator。
如何获取集合的取出对象呢?
通过一个对外提供的方法:iterator()
Iterator
布尔型 | hasNext() 如果仍有元素可以迭代,则返回 true。 |
E | next() 返回迭代的下一个元素。 |
无返回值 | remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 |
1.使用步骤
1)通过集合对象获取迭代器对象。
2)通过迭代器对象判断。
3)通过迭代器对象获取。
2.迭代器原理
由于多种集合的数据结构不同,所以存储方式不同,所以,取出方式也不同。
这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种
集合的时候,只要该集合内部实现这个接口即可。
3.集合的常见使用步骤
1)创建集合对象。
2)创建元素对象。
3)把元素添加到集合中。
4)遍历集合。
1.通过集合对象获取迭代器对象。
2.通过迭代器对象判断。
3.通过迭代器对象获取。
4.Collection存储字符串和自定义对象并遍历。
存储字符串代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
public class Test{
public static void main(String[]
args){
ArrayList al = new ArrayList();
al.add("黑马程序员——Java01");
al.add("黑马程序员——Java02");
al.add("黑马程序员——Java03");
al.add("黑马程序员——Java04");
for(Iterator iter = al.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
存储自定义对象代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
class Student{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "姓名:"+name+"...年龄:"+age;
}
}
public class Test{
public static void main(String[] args){
List list = new ArrayList();
list.add(new Student("黑马程序员——张三",23));
list.add(new Student("黑马程序员——李四",24));
list.add(new Student("黑马程序员——王五",25));
list.add(new Student("黑马程序员——赵六",26));
for(Iterator iter = list.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
由于Iterator迭代器对数据的操作功能太少,只有对数据进行获取和删除等功能,所以就有了ListIterator迭代器。
ListIterator迭代器不仅可以对迭代器进行获取和删除,还可以对迭代器进行添加和修改。
ListIterator
无返回值 | add(E e) 将指定的元素插入列表(可选操作)。 |
布尔型 | hasNext() 以正向遍历列表时,如果列表迭代器有多个元素,则返回 true(换句话说,如果 next 返回一个元素而不是抛出异常,则返回true)。 |
布尔型 | hasPrevious() 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。 |
E | next() 返回列表中的下一个元素。 |
整数 | nextIndex() 返回对 next 的后续调用所返回元素的索引。 |
E | previous() 返回列表中的前一个元素。 |
整数 | previousIndex() 返回对 previous 的后续调用所返回元素的索引。 |
无返回值 | remove() 从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。 |
无返回值 | set(E e) 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。 |
ListIterator代码示例:
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
public class Test{
public static void main(String[] args){
List list = new ArrayList();
list.add("黑马程序员——Java01");
list.add("黑马程序员——Java02");
list.add("黑马程序员——Java03");
list.add("黑马程序员——Java04");
for(ListIterator listIter = list.listIterator();listIter.hasNext();){
String name = listIter.next();
sop(name);
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
ListIterator迭代器
是Iterator的子接口。有自己的特有功能,可以逆向遍历数据,但是需要先正向遍历。一般不用。
面试题:并发修改异常
并发修改异常的产生原因:用迭代器遍历集合,用集合去操作集合。
解决方案:
使用集合操作。
使用列表迭代器操作。
常见的数据结构
加钱:数据结构+算法+UML+设计模式。
数据结构:
栈,队列,数组,链表。
栈:先进后出。 如同一个杯子
队列:先进先出。如同一个水管
数组:查询快,增删慢。
链表:查询慢,增删快。
LinkedList特有方法:
无返回值 | addFirst(E e) 将指定元素插入此列表的开头。 |
无返回值 | addLast(E e) 将指定元素添加到此列表的结尾。 |
E | element() 获取但不移除此列表的头(第一个元素)。 |
E | getFirst() 返回此列表的第一个元素。 |
E | getLast() 返回此列表的最后一个元素。 |
布尔型 | offerFirst(E e) 在此列表的开头插入指定的元素。 |
布尔型 | offerLast(E e) 在此列表末尾插入指定的元素。 |
E | removeLast() 移除并返回此列表的最后一个元素。 |
E | removeLast() 移除并返回此列表的最后一个元素。 |
布尔型 | offer(E e) 将指定元素添加到此列表的末尾(最后一个元素)。 |
布尔型 | offerFirst(E e) 在此列表的开头插入指定的元素。 |
布尔型 | offerLast(E e) 在此列表末尾插入指定的元素。 |
E | peek() 获取但不移除此列表的头(第一个元素)。 |
E | peekFirst() 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E | peekLast() 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
E | poll() 获取并移除此列表的头(第一个元素) |
E | pollFirst() 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。 |
E | pollLast() 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。 |
/*
使用LinkedFirst模拟一个堆栈或者队列数据结构
堆栈:先进后出 如同一个罐子
队列:先进先出 如同一个水管
*/
import java.util.LinkedList;
class Duilie{
private LinkedList link;
Duilie(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addFirst(obj);
}
public Object myGet(){
return link.removeLast();
}
public boolean isNull(){
return link.isEmpty();
}
}
public class Test{
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());
}
}
}
集合框架(ArrayList练习)
/*
去除ArrayList集合的重复元素。
*/
import java.util.ArrayList;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add("Java01");
al.add("Java02");
al.add("Java03");
al.add("Java02");
al.add("Java04");
al = singleElement(al);
System.out.println(al);
}
public static ArrayList singleElement(ArrayListal){
ArrayList newAl = new ArrayList();
for(Iterator it
= al.iterator();it.hasNext();){
Object obj = it.next();
if(!newAl.contains(obj)){//如果是同一个元素,那么就不存入。
newAl.add(obj);
}
}
return newAl;
}
}
集合框架(ArrayList练习2)
/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素
比如,存人对象,视为同一个人,为重复元素。
思路:
1、对人描述,将数据封装进人对象
2、定义容器,将人进行存入
3、取出
List集合判断是否包含其实是根据这个对象判断是否和内部的对象, equals,可以复写这个equals,写自己的判断
当List remove 删除时候,也是i先equals判断有没有,如果所以要注意
*/
import java.util.ArrayList;
import java.util.Iterator;
class Person{
private String name;
privateint age;
Person(String name,int age){
this.name = name;
this.age = age;
}
public boolean equals(Object obj){
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
returnname.equals(p.name) && this.age==p.age;
}
public String getName(){
return name;
}
publicint getAge(){
return age;
}
}
public class Test{
public static void main(String args[]){
ArrayList al = new ArrayList();
al.add(new Person("list01",31));
al.add(new Person("list02",32));
al.add(new Person("list02",32));
al.add(new Person("list03",33));
al.add(new Person("list04",34));
al.add(new Person("list04",34));
al.add(new Person("list05",35));
al = singleElement(al);
for(Iterator it = al.iterator();it.hasNext();){
Person p = (Person)it.next();
sop(p.getName()+":"+p.getAge());
}
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
public static ArrayList singleElement(ArrayList al){
ArrayList newAl = new ArrayList();
for(Iterator it = al.iterator();it.hasNext();){
Object obj = it.next();
if(!newAl.contains(obj)){
newAl.add(obj);
}
}
return newAl;
}
}
集合框架(Vector中的枚举)
迭代器 | elements() 返回此向量的组件的枚举。 |
import java.util.Enumeration;
public class Test{
public static void main(String[]
args){
Vector v = new Vector();
v.add("Java01");
v.add("Java02");
v.add("Java03");
v.add("Java04");
for(Enumeration en
= v.elements();en.hasMoreElements();){
sop(en.nextElement());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
枚举就是Vector特有的取出方式
发现枚举和迭代器很像。
其实枚举和迭代器是一样的。
因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了。
Vector和ArrayList在使用上非常相似,都可用来表示一组数量可变的对象应用的集合,并且可以随机地访问其中的元素。
Vector的方法都是同步的(Synchronized),是线程安全的,而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。(建议使用ArrayList,因为高效,就算是多线程咱们也不用怕,因为可以自己加锁)
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Set :元素是无序的(存入和取出顺序不一定一致),元素不可以重复。
boolean | add(E e) 如果 set 中尚未存在指定的元素,则添加此元素(可选操作)。 | |
boolean | addAll(Collection<? extendsE> c) 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)。 | |
void | clear() 移除此 set 中的所有元素(可选操作)。 | |
boolean | contains(Object o) 如果 set 包含指定的元素,则返回 true。 | |
boolean | containsAll(Collection<?> c) 如果此 set 包含指定 collection 的所有元素,则返回 true。 | |
boolean | equals(Object o) 比较指定对象与此 set 的相等性。 | |
int | hashCode() 返回 set 的哈希码值。 | |
boolean | isEmpty() 如果 set 不包含元素,则返回 true。 | |
Iterator<E> | iterator() 返回在此 set 中的元素上进行迭代的迭代器。 | |
boolean | remove(Object o) 如果 set 中存在指定的元素,则将其移除(可选操作)。 | |
boolean | removeAll(Collection<?> c) 移除 set 中那些包含在指定 collection 中的元素(可选操作)。 | |
boolean | retainAll(Collection<?> c) 仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。 | |
int | size() 返回 set 中的元素数(其容量)。 | |
Object[] | toArray() 返回一个包含 set 中所有元素的数组。 | |
| toArray(T[] a) 返回一个包含此 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型。 |
布尔型 | add(E e) 如果此 set 中尚未包含指定元素,则添加指定元素。 |
无返回值 | clear() 从此 set 中移除所有元素。 |
布尔型 | contains(Object o) 如果此 set 包含指定元素,则返回 true。 |
布尔型 | isEmpty() 如果此 set 不包含任何元素,则返回 true。 |
迭代器 | iterator() 返回对此 set 中元素进行迭代的迭代器。 |
布尔型 | remove(Object o) 如果指定元素存在于此 set 中,则将其移除。 |
整数 | size() 返回此 set 中的元素的数量(set 的容量)。 |
是通过两个方法,hashCode和equals来完成。
如果元素hashCode值相同,才会判断equals是否为true。
如果元素的hashCode不同,不会调用equals方法。
怎么重写hashCode()和equals()方法呢?
hashCode():把对象的所有成员变量值相加即可。
如果是基本数据类型,就加值。如果是引用类型,就加哈希值。
return name.hasCode()+ age * 27;
equals(obj):先判断是否是同一类型的对象,再把传递进来的对象进行向下转型。再判断对象的内容是否相同。
if(!(obj instanceof Student))
;
Student stu = (Student)obj;
return this.name.equals(stu.name) && this.age
== stu.age;
所有成员变量的值比较。基本类型用==,引用类型用equals()。
注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。
举例:
publicint hashCode(){
return name.hashCode()+age*21;
}
public boolean equals(Object obj){
if(!obj instanceof Person)
return false;
Person p=(Person)obj;
return name.equals(p.name) && age==p.age;
}
TreeSet :底层的数据结构是二叉树。线程是不同步的。
比较器 | comparator() 返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回null。 |
import java.util.TreeSet;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
TreeSet ts =new TreeSet();
ts.add("Java01");
ts.add("Java03");
ts.add("Java02");
ts.add("Java04");
for(Iterator it = ts.iterator();it.hasNext();){
sop(it.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
TreeSet集合两种实现排序方式:
自然排序(元素具备比较性)
TreeSet的无参构造,要求对象所属的类实现Comparable接口。
比较器排序(集合具备比较性)
TreeSet的带参构造,要求构造方法接收一个实现了Comparator接口的对象。
Comparable
整数 | compareTo(T o) 比较此对象与指定对象的顺序。 |
整数 | compare(T o1, T o2) 比较用来排序的两个参数。 |
布尔值 | equals(Object obj) 指示某个其他对象是否“等于”此 Comparator。 |
/*
需求:往
TreeSet 集合中存储自定义对象学生。
想按照学生的年龄进行排序。
*/
import java.util.Iterator;
import java.util.TreeSet;
class Student implements Comparable{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
publicint compareTo(Object obj){
if(!(obj instanceof Student)){
throw new RuntimeException("不是学生对象");
}
Student s = (Student)obj;
int num = this.age-s.age;
if(num==0)
return
this.name.comparTo(s.name);
else
return num;
}
public String toString(){
return name +"..."+age;
}
}
public class Test{
public static void main(String[] args){
TreeSet ts =new TreeSet();
ts.add(new Student("黑马程序员——张三",21));
ts.add(new Student("黑马程序员——王五",23));
ts.add(new Student("黑马程序员——周七",19));
ts.add(new Student("黑马程序员——赵六",18));
for(Iterator iter = ts.iterator();iter.hasNext();){
sop(iter.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
Comparator存储自定义对象代码示例:
import java.util.Iterator;
import java.util.TreeSet;
import java.util.Comparator;
class Student{
private String name;
privateint age;
Student(String name,int age){
this.name=name;
this.age=age;
}
public String getName(){
return name;
}
publicint getAge(){
return age;
}
}
class MyCompare implements Comparator{
publicint compare(Object obj1,Object obj2){
Student s1 = (Student)obj1;
Student s2 = (Student)obj2;
int num =new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
if(num==0)
return s1.getName().compareTo(s2.getName());
returnnum;
}
}
public class Test{
public static void main(String[] args){
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student("黑马程序员——张三",21));
ts.add(new Student("黑马程序员——王五",23));
ts.add(new Student("黑马程序员——周七",19));
ts.add(new Student("黑马程序员——赵六",18));
for(Iterator iter = ts.iterator();iter.hasNext();){
Student stu = (Student)iter.next();
sop(stu.getName()+"..."+stu.getAge());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
注意:如果同时有两种方案,以谁为主呢?以比较器为主。
集合框架(TreeSet练习)
/*
练习,按照字符串长度排序
字符串本身具备比较器,但是他的比较方式不是所需要的
这时就只能使用比较器。
也可以不定义类,写匿名内部类也可以。
*/
import java.util.TreeSet;
import java.util.Iterator;
import java.util.Comparator;
public class Test{
public
static void main(String args[]){
TreeSet ts =new TreeSet(new Comparator(){
publicint compare(Object obj1,Object obj2){
String s1
= (String)obj1;
String s2
= (String)obj2;
int num
= s1.length()-s2.length();
if(num==0)
return s1.compareTo(s2);
return num;
}
});
ts.add("黑马");
ts.add("csdn");
ts.add("黑马论坛");
ts.add("云计算");
for(Iterator it
= ts.iterator();it.hasNext();){
sop(it.next());
}
}
public
static void sop(Object obj){
System.out.println(obj);
}
}
集合框架(泛型概述)
/*
泛型:JDK1.5 以后出现的新特性,用于解决安全问题,是一个安全机制。
好处:
1、将运行时期出现的问题(classCastException),转移到了编译时期。
方便于程序员解决问题。让运行时期问题减少,安全。
2、避免了强制转换的麻烦。
泛型格式:通过 <> 来定义要操作的引用数据类型。
什么时候使用泛型呢?
通常在集合框架中很常见。
只要见到<>就要定义泛型
其实<>就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到尖括号中即可,和函数传参数一样。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class Test{
public static void main(String args[]){
List<Integer>
al = new ArrayList<Integer>();
al.add(4);//自动装箱功能,现在作为了解。以后会学到的,包括泛型。
al.add(new Integer(4));
int x=5;
al.add(x);
for(Iterator<Integer>
it = al.iterator();it.hasNext();){
sop(it.next());
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
集合框架(泛型类)
/*
泛型类
舍呢么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候
早起定义Object 来完成扩展
现在,定义泛型来完成扩展。
*/
//JDK1.5之前的做法
class Tool{
private Object obj;
public void setObject(Object obj){
this.obj=obj;
}
public Object getObject(){
return obj;
}
}
//JDK1.5泛型出现后,用泛型的做法
//泛型类
class Utils<E>{
private E o;
public void setObject(E o){
this.o = o;
}
public E getObject(){
return o;
}
}
class Worker{
}
class Student{
}
public class Test{
public static void main(String args[]){
//JDK1.4做法。
Tool t = new Tool();
//可以传入Student
t.setObject(new Student());
Objectobj = t.getObject();
Student stu = (Student)obj;
//泛型出现以后的做法
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Worker());
Worker w = u.getObject();
}
public static void sop(Object obj){
System.out.println(obj);
}
}
泛型可以让程序员避免强制转换的麻烦,并且把运行时强转问题转化到了编译时期。
集合框架(泛型方法)
/*
泛型类定义的泛型,在整个类中有效,如果被方法是用到泛型,类的对象明确。操作的具体类型后,所有要操作的类型就已经固定了。为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上,而不定义在类上。
public <T> void show(T t){
}
*/
//类型已经确定.
class GenericTest<T>{
public void show(T t){
sop("show:"+t);
}
public void print(T t){
sop("print:"+t);
}
public static void sop(Objectobj){
System.out.println(obj);
}
}
//类型不确定,使用泛型方法
class GenericTest2{
//泛型方法
public <Q>void show(Q q){
sop("show_2:"+q);
}
public <Q> void print(Q q){
sop("print_2:"+q);
}
public static void sop(Object obj){
System.out.println(obj);
}
}
public class Test{
public static void main(String args[]){
//泛型确定
GenericTest<String> d = new GenericTest<String>();
d.show("黑马程序员");
d.print("csdn社区");
//泛型不确定
GenericTest2 d2 = new GenericDemo_2();
d2.show("黑马程序员");
d2.print(new Integer(4));
d2.print(4);
}
public static void sop(Objectobj){
System.out.println(obj);
}
}
集合框架(静态方法泛型)
泛型类中也可以定义泛型方法
特殊之处:静态方法不可以访问类上定义的泛型,因为只有在实例化的时候,才会使用泛型
如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上。
<T> 泛型写在返回值前。
/*
public
static <T> void setName(T t){
}
*/
集合框架(泛型接口)
interface Inter<T>{
void show(T t);
}
class InterImpl implements Inter<String>{
void show(Stringt)//这里只能是
String{
}
}
class InterImpl2<T> implements Inter<T>{
void show(T t);//这里类型不固定{
}
}
集合框架(泛型通配符)
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class Test{
public static voidmain(String args[]){
List<String> al = new ArrayList<String>();
al.add("黑马程序员");
al.add("CSDN社区");
al.add("黑马程序员论坛");
List<Integer> al1 = newArrayList<Integer>();
al1.add(1);
al1.add(2);
al1.add(3);
printCollection(al);
printCollection(al1);
}
public static void printCollection(List<?> al){
for(Iterator<?> it = al.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
集合框架(泛型限定(上限))
/*
? 通配符
? extends E:可以接收 E 类型或者 E 的子类型。上限。
? super E:可以接收E类型或者E的父类型,下限
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
class Person{
private String name;
Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class Student extends Person{
Student(String name){
super(name);
}
}
public class Test{
public static void main(String args[]){
List<Person> al =newArrayList<Person>();
al.add(new Person("张三"));
al.add(new Person("李四"));
al.add(new Person("王五"));
List<Student> al1 = new ArrayList<Student>();
al1.add(new Student("赵六"));
al1.add(new Student("周七"));
al1.add(new Student("郑八"));
printCollection(al);
printCollection(al1);
}
//printCollection(al1);这是错误的,不能直接写
//public static void printCollection(ArrayList<Person> al)这样只能接受person
public static void printCollection(List<? extends Person> al){/*这样可以接收Person和Person的子类为了面向接口编程,而且上面定义的容器也是面向接口编程,这里写成List,否则编译不通过。因为ArrayList子类不能接收List父类。*/
for(Iterator<? extends Person> it = al.iterator();it.hasNext();){
System.out.println(it.next().getName());
}
}
}
集合框架(泛型限定(下限))
/*
? 通配符
? extends E:可以接收 E 类型或者 E 的子类型。上限。
? super E: 可以接收E类型或者E的父类型,下限
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
class Person{
private String name;
Person(String name){
this.name = name;
}
public String toString(){
return name;
}
}
class Student extends Person{
Student(String name){
super(name);
}
}
public class Test{
public static void main(String args[]){
List<Person> al =new ArrayList<Person>();
al.add(new Person("张三"));
al.add(new Person("李四"));
al.add(new Person("王五"));
List<Student> al1 = new ArrayList<Student>();
al1.add(new Student("赵六"));
al1.add(new Student("周七"));
al1.add(new Student("郑八"));
printCollection(al);
printCollection(al1);
}
public static void printCollection(List<? super Student> al){
for(Iterator<? super Student> it = al.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
使用泛型的时制:
是么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义的Object来完成扩展。现在定义泛型类来完成扩展
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让相同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
特殊之处:
静态方法不可以访问类上定义的泛型。因为只有在实例化的时候,才会使用泛型。
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
? 通配符。也可以理解为占位符。
泛型的限定:
?extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
相关文章推荐
- Java面向对象——集合框架(Collection)
- 黑马程序员 Java面向对象——集合框架(Collection)
- 黑马程序员--Java面向对象——集合框架(Collection)
- Java基础之集合框架(一)--Collection、List、LinkedList、HashSet
- java 集合框架(Collection)和数组的排序 推荐
- Java 集合框架(Collection)和数组的排序
- Java 集合框架 - Collection体系部分
- Java面向对象——集合框架总结
- CoreJava学习4——集合之Collection框架和List集合
- Java 集合框架(Collection)和数组的排序
- 黑马程序员——java基础之集合框架(Collection 和 Map)
- Java--面向对象之七:Collection集合框架
- Java面向对象——集合框架(Map)
- java 集合框架(Collection)和数组的排序
- 【黑马程序员】java中----------Collection集合框架
- java 集合框架(Collection)和数组的排序
- Java集合总结之Collection整体框架
- Java 集合框架(Collection)和数组的排序
- java 集合框架(Collection)和数组的排序
- Java学习第17天:集合框架Collection的知识补充和其他类(还有增强for)