Day 14 15 集合框架
——体系概述、共性方法、迭代器、List集合共性方法、ListIterator、List集合具体对象的特点、Vector中的枚举、LinkedList、HashSet
——TreeSet、二叉树、实现Comparator方式排序、泛型
Editplus中左边目录栏没有快捷键:alt+shift+3
复制上一行到下一行快捷键:ctrl+J
移动一行到上一行/下一行: alt+shift+向上键(向下键)
向左缩进:shift+tab
Java中进行数值操作:包含头不包含尾。[1,3]:取元素1,2
集合类
为什么出现集合类?
面向对象语言对事物的体现都是以对象的方式,所以为了方便更多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
数组和集合类同时容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
数据多了需要进行封装,封装成对象,对象多了也需要存储,两种存储的地方:数组和集合。对象多了用集合存,数据多了用对象存。
容器分很多种,不断共性抽取,慢慢就产生体系,该体系就称为集合框架。
先看顶层:定义了该体系中最共性最基本的内容,体系基本功能。
为什么会出现这么多的容器呢?
因为每一个容器对数据的存储方式都有不同,这个存储方式称为:数据结构。
共性方法
1.add方法的参数类型是Object,以便接收任意类型对象。
2.集合中存储的都是对象的引用(地址)。
import java.util.*;
class CollectionDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
method_2();
}
public static void method_2()
{
ArrayList al1 = new ArrayList();
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
ArrayList al2 = new ArrayList();
al2.add("java03");
al2.add("java04");
al2.add("java05");
al2.add("java06");
//al1.retainAll(al2);
//取交集,al1中只会保存和al2中相同的元素。
al1.removeAll(al2);
//al1中只会保存和al2中不相同的元素。
sop("al1 : " + al1);
sop("al2 : " + al2);
}
public static void base_method()
{
//创建一个集合容器。使用Collection接口的子类 ArrayList
ArrayList al = new ArrayList();
//1.添加元素
//add(Object obj);容器中添加的对象是任意类型的,使用Object接收,多态
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
//打印原集合
sop("原集合:" + al);
//3.删除元素
//al.remove("java02");
//al.clear();//清空集合
//判断元素
sop("java03是否存在:" + al.contains("java03"));
sop("集合是否为空:" + al.isEmpty());
//2.获取个数,集合长度
sop("size : " + al.size());
//打印改变后的集合
sop(al);
}
}
迭代器
什么是迭代器呢?集合取出元素的方法。
public static void method_get()
{
//取出元素并操作元素,并不是简单的打印
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
/*
Iterator it = al.iterator();//获取迭代器,用于取出集合中的元素
while (it.hasNext())
{
sop(it.next());
}
//while循环执行完后变量it还存在于内存中
//for循环定义的是局部变量,执行完后释放,写下面的
*/
for (Iterator it = al.iterator(); it.hasNext ; )
{
sop(it.next());
}
}
List集合共性方法
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引。
|–Set:元素是无序的,元素不可以重复。
List特有方法,凡是可以操作角标的方法都是该体系特有的方法:
增
add(index,element)
addAll(index,Collection)
删
remove(index)
改
set(index,element)
查
get(index)
subList(from,to)
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);
//在指定位置添加元素
al.add(1,"java09");
//删除指定位置的元素
//al.remove(2);
//修改元素
//al.set(2,"java07");
//通过角标获取元素
sop("get(1) : " + al.get(1));
sop(al);
//获取所有元素
for ( int x = 0 ; x<al.size(); x++)
{
sop("al[" + x + "] = " + al.get(x));
}
//迭代器
Iterator it = al.iterator();
//while
while (it.hasNext())
{
sop("next : " + it.next());
}
/*
//for
for ( Iterator it = al.iterator() ; it.hasNext(); )
{
sop(it.next());
}
*/
//通过indexOf获取对象的位置
sop("index = " + al.indexOf("java02"));
List sub = al.subList(1,3);
sop("sub = " + sub);
}
}
ListIterator 列表迭代器
Iterator只能进行 hasNext、next、remove。
List集合特有的迭代器。ListIterator是Iterator的接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。
所以,在迭代器时,智能用迭代器方式操作元素,可是Iterator方法是有限的,只能对元素进行 判断,取出,删除的操作。如果想要其他的操作如 添加,修改等,就需要使用其子接口,ListIterator。
该接口智能通过List集合的listIterator方法获取。
并发修改异常:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
现在对元素的引用存放在集合中,通过集合的方法可以对集合进行操作,当出现al.iterator()时,即把元素的引用取到迭代器中去。操作元素的方法有两种:集合,迭代器。此处用到集合和迭代器共同操作元素,并发(同时)访问,会有安全隐患。不能对同一组元素进行同时多种操作。
全部使用集合或者迭代器的方法。
obj指向java02,元素的引用从集合中移除,元素还存在在内存当中,元素还可以被obj使用,所以打印。
hasNext():以长相遍历列表时,如果列表迭代器有多个元素,则返回true。
hasPrevious():如果以逆遍历列表,列表迭代器有多个元素,则返回true。
import java.util.*;
class ListDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void method()
{
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
sop("原集合是 :" + al);
//在指定位置添加元素
al.add(1,"java09");
//删除指定位置的元素
//al.remove(2);
//修改元素
//al.set(2,"java07");
//通过角标获取元素
sop("get(1) : " + al.get(1));
sop(al);
//获取所有元素
for ( int x = 0 ; x<al.size(); x++)
{
sop("al[" + x + "] = " + al.get(x));
}
//迭代器
Iterator it = al.iterator();
//while
while (it.hasNext())
{
sop("next : " + it.next());
}
/*
//for
for ( Iterator it = al.iterator() ; it.hasNext(); )
{
sop(it.next());
}
*/
//通过indexOf获取对象的位置
sop("index = " + al.indexOf("java02"));
List sub = al.subList(1,3);
sop("sub = " + sub);
}
public static void main(String[] args)
{
//演示列表迭代器
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
sop(al);
ListIterator li = al.listIterator();
//sop("hasPrevious : " + li.hasPrevious());
//判断迭代前面有没有元素-->false
while (li.hasNext())
{
Object obj = li.next();
if (obj.equals("java02"))
//li.add("java009");
li.set("java006");
}
//sop("hasNext : " + li.hasNext());
//判断迭代后面有没有元素-->false
//sop("hasPrevious : " + li.hasPrevious());
//判断迭代前面有没有元素-->true
sop(al);
while (li.hasPrevious())//逆着取
{
sop("pre : " + li.previous());
}
sop(al);
/*
//在迭代过程中没准备添加或者删除元素
Iterator it = al.iterator();
while (it.hasNext())
{
Object obj = it.next();//不清楚添加什么类型的对象,用Object
if (obj.equals("java02"))
//al.add("java008");-->并发修改异常
it.remove();//将java02的引用从集合中删除了
sop("obj = " + obj);
}
sop(al);
*/
}
}
List集合具体对象的特点
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引。
|–ArrayList:底层的数据结构使用的是数组结构。特点:查询速度快,但是增删稍慢。线程不同步。截图中上面试数组结构,下面是链表结构。
|–LinkedList:底层使用的是链表数据结构。特点:增删速度更快,查询稍慢。
|–Vector:底层是数组数据结构。线程同步。被ArrayList替代了。
ArrayList构造一个初始容量为10的空列表。当超过10会自动new一个新的列表,50%延长15,把原来数组中元素copy到新数组中来,再把新元素添加到后面去,。Vector初始化也为10,当超过时,100%延长20长度延长。ArrayList既能延长又能节约空间,Vector浪费空间。所以Vector已经不用了。
|–Set:元素是无序的,元素不可以重复。
Vector中的枚举
枚举就是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
LinkedList特有方法:
addFirst()
addLast()
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException 没有这个元素异常。
getFirst()
getLast()
获取元素,但元素被删除。如果集合中没有元素,会出现NoSuchElementException 没有这个元素异常。
removeFirst()
removeLast()
前期:如果列表为空,会抛出NoSuchElementException 没有这个元素异常。
后期JDK1.6出现了替代方法:pollFirst 获取并移除此列表的第一个元素。如果为空,返回null。
offerFirst()
OfferLast()
获取元素,但不删除元素。如果集合中没有元素,会返回null。
peekFirst()
peekLast()
获取元素,但元素被删除。如果集合中没有元素,会返回null。
pollFirst()
pollLast()
import java.util.*;
class LinkedListDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
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.getLast());
//sop(link.removeFirst());
//sop("size = " + link.size());
while (link.isEmpty())
{
sop(link.removeFirst());
}
}
}
LinkedList练习
使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出 如同一个杯子
队列:先进先出 First in First out FIFO 如同一个水管
封装原因:直接用LinkList是可以完成的,但是LinkList只有自身含义 链表,我们想把它做成与我们项目相关的一些容器,需要起一些特定的名称用起来更方便一些。这时需要把原有的结构封装进我们的描述当中并对外提供一个自己更能识别的名称名字。
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.removeLast();//先进先出,队列
//return link.removeFirst();//先进后出,堆栈
}
public boolean isNull()
{
return link.isEmpty();
}
}
class LinkedListTest
{
public static void sop(Object obj)
{
System.out.println(obj);
}
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())
{
sop(dl.myGet());
}
}
}
ArrayList练习
1.去除ArrauList集合中的重复元素。
import java.util.*;
class ArrayListTest
{
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("java01");
al.add("java02");
al.add("java01");
al.add("java03");
sop(al);
al = singleElement(al);
sop(al);
/*
在迭代时循环中next调用一次,就要hasNext判断一次。
Iterator it = al.iterator();
while(it.hasNext())
{
sop(it.next() + "....." + it.next());
//-->取两次才判断,奇数时会报错。
}
*/
}
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))
newAl.add(obj);
}
return newAl;
}
}
2.将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一人,为重复元素。
思路:
1.对人描述,将数据封装进人对象。
2.定义容器,将人存入。
3.取出。
判断两个对象是否相同用的是equals方法,使用对象.equals和另外一个对象进行比较,返回为真视为相同。自己的new Person中有equals方法,Person本身集成Object,Object的equals方法比较的是地址值,自己所创建的地址值个对象不相同,复写equals。
List集合判断元素是否相同,依据是元素的equals方法。contains和remove方法底层调用equals。
import java.util.*;
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public boolean equals (Object obj)//Person中的equals方法
{
if (!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name + "......" + p.name);
return this.name.equals(p.name) && this.age == p.age;
//字符串中的equals方法
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class ArrayListTest2
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
ArrayList al = new ArrayList();
al.add(new Person("lisi01",30));
//al.add(Object obj);//Object obj = new Person("lisi01",30);
//提升了,Object中没有getName()方法,向下转型
al.add(new Person("lisi02",32));
al.add(new Person("lisi02",32));
al.add(new Person("lisi03",33));
al.add(new Person("lisi04",35));
al.add(new Person("lisi04",35));
al = singleElement(al);
Iterator it = al.iterator();
while (it.hasNext())
{
//Object obj = it.next();
//Person p = (Person)obj;
Person p = (Person)it.next();
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))
newAl.add(obj);
}
return newAl;
}
}
取得的元素很多,元素中涉及到频繁的增删操作–>LinkedList
涉及到增删操作但不频繁–>LinkedList,ArrayList
涉及到增删和查询–>ArrayList(涉及到频繁的增删不多见,一般就是查询较多,最常用)
Collection
|–List:元素是有序的,元素可以重复,因为该集合体系有索引。
|–ArrayList:底层的数据结构使用的是数组结构。特点:查询速度快,但是增删稍慢。线程不同步。截图中上面试数组结构,下面是链表结构。
|–LinkedList:底层使用的是链表数据结构。特点:增删速度更快,查询稍慢。
|–Vector:底层是数组数据结构。线程同步。被ArrayList替代了。
ArrayList构造一个初始容量为10的空列表。当超过10会自动new一个新的列表,50%延长15,把原来数组中元素copy到新数组中来,再把新元素添加到后面去,。Vector初始化也为10,当超过时,100%延长20长度延长。ArrayList既能延长又能节约空间,Vector浪费空间。所以Vector已经不用了。
|–Set:元素是无序的(存入和取出的顺序不一定一致),元素不可以重复。
|–HashSet:底层数据结构是哈希表。
|–TreeSet:
Set集合的功能和Collection是一致的。
HashSet
在哈希表中,当哈希值重复时还有一种调用方式,如果地址值不同即为不同元素;如果地址值相同则还需要做一个判断,两个元素对象是否相同。判断是否为同一对象–>equals方法。
不为同一对象但是地址值一样会在该地址值下顺延。
先看哈希值是否一样,再在判断是否为同一对象,如果哈希值不一样就不用判断是否为同一对象。HashSet结构就是哈希表结构。
import java.util.*;
/*
class Demo
{
打印的是哈希值,调用的是Demo中的hashCode方法。
toString hashCode()方法,Demo把hashCode复写。
返回一个int型值,之前是由windows系统底层哈希算法算哈希值。
现在自定义一个值。
public int hashCode()
{
return 60;
}
}
*/
class HashSetDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
HashSet hs = new HashSet();
sop(hs.add("java01"));//-->true
sop(hs.add("java01"));//-->false
//发现地址值一样,在存第二个元素时直接不存了
//添加失败为假
hs.add("java02");
hs.add("java03");
hs.add("java03");
hs.add("java04");
//输出还是4个,保证唯一性。输出无序
Iterator it = hs.iterator();
while (it.hasNext())
{
sop(it.next());
}
}
}
存储自定义对象
往HashSet集合中存入自定义对象,姓名和年龄为相同视为同一个人,重复元素。
HashSet是如何保证元素唯一性的?是通过元素的两个方法:hashCode和equals来完成。如果元素的HashCode值相同才会判断equals是否为true,如果元素的HashCode值不同才会调用equals。
当我们在自定义对象的时候,通常要复写hashCode的equals,因为这个对象有可能要存放到HashSet集合当中。在开发时都会复写hashCode和equals。
HashSet删除和判断依据
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode(先)和equals方法(再)。
ArrayList只依赖的是equals。
import java.util.*;
class HashTest
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
// hs.add(new Person("a2",12));
// hs.add(new Person("a4",14));
// sop("a1 : " + hs.contains(new Person("a1",11)));
hs.remove(new Person("a3",13));
Iterator it = hs.iterator();
while (it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName() + ":"+ p.getAge());
}
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
//new4个对象都有自己独立的哈希值,值不同有4个位存储,都存进去不用equals
//覆盖Person的hashCode方法,建立Person自己的哈希值,依据判断条件生成自己的哈希值
public int hashCode()
{
//System.out.println(this.name +"......hashCode");
//return 60;
//按条件设置哈希值
return name.hashCode() + age * 39;
//为保证哈希值的唯一性可以*固定值,注意不要超出int的范围
}
public boolean equals (Object obj)//Person中的equals方法
{
if (!(obj instanceof Person))
return false;
Person p = (Person)obj;
//System.out.println(this.name + "...equals..." + p.name);
return this.name.equals(p.name) && this.age == p.age;
//字符串中的equals方法
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
TreeSet
Set:无序,不可以重复元素。
|–HashSet:数据结构是哈希表。线程是非同步的。保证元素唯一性的原理:判断元素的hashCode值是否相同,如果相同,还会继续判断元素的equals方法,否则为true。
|–TreeSet:可以对Set集合中的元素进行排序。底层数据是二叉树。保证元素唯一性的依据:compareTo方法是return 0 判断元素是否相同。
TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序。
TreeSet排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时让集合自身具备比较性。在集合初始化时,就有了比较方式(构造函数)。
当两种排序都存在时,以比较器为主。
定义个一类实现Comparator接口,覆盖compare方法。
Comparartor中–compare方法
comparable–compareTo方法
TreeSet存储自定义对象
需求:往TreeSet集合中存储自定义对象学生,想按照学生的年龄进行排序。
往treeSet集合中存对象,自动排序,没有告诉怎么排序,学生对象不具备比较性(自然顺序,默认顺序),没法排序。
CompareTo 比较此对象与指定对象排序,如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
排序时,当主要条件相同时,一定判断一下次要条件。
import java.util.*;
class TreeSetDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
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("lisi02",22));//重复元素,进不来
Iterator it = ts.iterator();
while (it.hasNext())
{
//sop(it.next());
Student stu = (Student)it.next();
sop(stu.getName() + "..." + stu.getAge());
}
}
}
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)
{
//return 1;//怎么存进去怎么取出来,-1取倒序,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);
}
return -1;
//降序:把-1改成1,1改成-1,中间取反
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
二叉树
减少比较次数,提高效率,数据多了会找一个中间点进行比较。
return 1;//怎么存进去怎么取出来,-1取倒序,0只有一个元素,都相等
实现Comparator方式排序
当元素自身不具备比较性,或者具备的比较性不是所需要的,这时需要让容器自身 具备比较性。定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
当两种排序都存在时,以比较器为主。
练习
按字符串长度排序
字符串本身具备比较性,但是它的比较性不是所需要的,这时候就需要比较器。
import java.util.*;
class TreeSetTest
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
TreeSet ts = new TreeSet(new StringLengthComparator());
/*匿名内部类,不直观
TreeSet ts = new TreeSet(new Comparator()
{
public int compare(Object o1,Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if(num == 0)
return s1.compareTo(s2);
return num;
}
});
*/
ts.add("abcd");
ts.add("cc");
ts.add("cba");
ts.add("aaa");
ts.add("z");
ts.add("hahaha");
Iterator it = ts.iterator();
while (it.hasNext())
{
sop(it.next());
}
}
}
class StringLengthComparator implements Comparator
{
public int compare(Object o1,Object o2)
{
String s1 = (String)o1;
String s2 = (String)o2;
/*
if(s1.length()>s2.length())
return 1;
if(s1.length()==s2.length())
return 0;
return -1;
*/
//自己本身具备比较性
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if(num == 0)
return s1.compareTo(s2);
/*
if(num == 1)
return 1;
不能这样写,比较结果为整数或负数,不一定为1或-1。
*/
return num;
}
}
泛型
参考定义数组,在定义时已经定义好数据类型。
{}:被程序结构使用
():被参数使用
[]:被数组使用
<>:被泛型使用
JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。
好处:
1.将运行时期出现问题ClassCastException转移到编译时期,方便程序员解决问题,让运行事情问题减少,安全。
2.避免了强制转换麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
在使用java提供的对象时,什么时候使用泛型?
通常在集合框架中很常见,只要见到<>就要定义泛型。其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
import java.util.*;
class GenericDemo2
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
TreeSet<String> ts = new TreeSet<String>(new LenComparator());
ts.add("abcd");
ts.add("cc");
ts.add("cba");
ts.add("aaa");
ts.add("z");
ts.add("hahaha");
Iterator<String> it = ts.iterator();
while (it.hasNext())
{
String s = it.next();
sop(s);
}
}
}
class LenComparator implements Comparator<String>
{
//public int compare(Object o1,Object o2)
public int compare(String o1,String o2)
{
// String si = (String)o1;//不需要强制转换了
// String s2 = (String)o2;
int num = new Integer(o1.length()).compareTo(new Integer(o2.length()));
//倒序
//int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));
if (num == 0)
return o1.compareTo(o2);
//return o2.compareTo(o1);
return num;
}
}
要想自定义一个学生对象,定义hashCode和equals,还要实现一个comparable接口,让学生具备默认的比较性。至于用不用再说。这样既可以存放HashSet中又可以存放到TreeSet中。
泛型类
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候(基本数据类型不确定定义不了),早期定义Object来完成扩展。现在定义泛型来完成扩展。
/*
//一个类对应一个工具类麻烦,如果只为设置和获取对象,
//对象不确定,都是后期来的,可以提高程序扩展性,可以抽取这些对象的共性类型。
class Tool
{
private Worker w;
public void setWorker(Worker w)//用来设置和获取工人对象
{
this.w = w;
}
public Worker getWorker()
{
return w;
}
}
*/
//泛型前做法
class Tool
{
private Object obj;//后期出现不确定-->Object
public void setObject(Object obj)//用来设置和获取工人对象
{
this.obj = obj;
}
public Object getObject()
{
return obj;
}
}
//泛型类,定义一个参数,对方在使用工具类时,由他来指定他要操作什么类型对象
class Utils<QQ>
{
private QQ q;
public void setObject(QQ q)
{
this.q = q;
}
public QQ getObject()
{
return q;
}
}
class Worker
{
}
class Student
{
}
class GenericDemo3
{
public static void main(String[] args)
{
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Worker());
Worker w = u.getObject();
/*
//泛型前做法,主观判断传入的是什么类型,有强转
Tool t = new Tool();
t.setObject(new Worker());
Worker w = (Worker)t.getObject();
*/
}
}
泛型方法
泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。为了让不同的方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
/*
class Demo<T>
{
public void show(T t)//显示什么类型不知道
{
System.out.println("show:" + t);
}
public void print(T t)
{
System.out.println("print:" + t);
}
//之前传入函数时需要明确传入类型
public void show(String s)
{}
public void show(Integer i)
{}
}
*/
class Demo<T>
{
public <T> void show(T t)
{
System.out.println("show:" + t);
}
public <Q> void print(Q q)//写成T也没关系,这个T只在这个方法内有效
{
System.out.println("print:" + q);
}
}
class GenericDemo4
{
public static void main(String[] args)
{
Demo<String> t = new Demo<String>();
d.show("haha");
d.show(5);
/*
Demo d = new Demo();
d.show("haha");
d.print(new Integer(6));
*/
/*
Demo<Integer> d = new Demo<Integer>();
d.show(new Integer(4));
d.print(9);
Demo<String> d1 = new Demo<String>();
d1.println("haha");
*/
}
}
静态方法泛型
特殊之处:静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。泛型定义在方法上,放在修饰符后面,返回值类型的前面。
class Demo<T>
//<T>每次只有在建立对象时才会被明确,所以static存在时还没有对象就会报错。
{
public void show(T t)
{
System.out.println("show:" + t);
}
public <Q> void print(Q q)//写成T也没关系,这个T只在这个方法内有效
{
System.out.println("print:" + q);
}
//public static void method(T t)
public static <W> void method(W w)
{
System.out.println("method:"+ w);
}
}
class GenericDemo5
{
public static void main(String[] args)
{
Demo<String> d = new Demo<String>();
d.show("haha");
//d.show(3);
//-->报错,show类型是随对象走的,print定义在方法上,所以传什么都可以
d.print(5);
d.print("hehe");
Demo.method("hahhahahaha");
}
}
泛型接口
泛型定义在接口上
interface Inter<T>
{
void show(T t);
}
/*
//子类在实现时已经指定类型了,这个show方法只能操作String
class InterImpl implements Inter<String>
{
public void show(String t)
{
System.out.println("show:" + t);
}
}
*/
class InterImpl<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show:" + t);
}
}
class GenericDemo6
{
public static void main(String[] args)
{
InterImpl<Integer> i = new InterImpl<Integer>();
i.show(3);
//InterImpl i = new InterImpl();
//i.show("hahah");
}
}
泛型限定
import java.util.*;
class GenericDemo7
{
public static void main(String[] args)
{
//需要写两个迭代器,都是遍历集合,不需要那么麻烦
//为提高代码复用性,
ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
ArrayList<Integer> al1 = new ArrayList<Integer>();
al1.add(4);
al1.add(5);
al1.add(6);
printColl(al);
printColl(al1);
}
/*
//ArrayList<String> al = new ArrayList<Integer>();左右两边类型不匹配,存在安全隐患,error
//想打印任意类型对象,当对象类型不确定时,使用占位符?表示
//如果T是一个具体类型,和?区别:可以对T进行具体操作
public static <T> void printColl(ArrayList<T> al)
{
Iterator<T> it = al.iterator();
while (it.hasNext())
{
T t = it.next();
System.out.println(t);
}
}
*/
public static void printColl(ArrayList<?> al)
{
Iterator<?> it = al.iterator();
while (it.hasNext())
{
System.out.println(it.next().toString());
//打印不出length,?类型不确定,length为具体方法,传Integer打印不出
//泛型缺点:不能使用对象特有方法
}
}
}
?:通配符,也可以理解为占位符。
泛型的限定:
? extends E:可以接收E类型或者E的子类型。上限。
? extends E:可以接收E类型或者E的父类型。下限。
import java.util.*;
class GenericDemo8
{
public static void main(String[] args)
{
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("abc1"));
al.add(new Person("abc2"));
al.add(new Person("abc3"));
//printColl(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("abc--1"));
al1.add(new Student("abc--2"));
al1.add(new Student("abc--3"));
printColl(al1);
//ArrayList<Person> al1 = new ArrayList<Student>(); error
}
public static void printColl(ArrayList<? extends Person> al)
//只想打印Person和Person的子类型,或 ? super Student
{
Iterator<? extends Person> it = al.iterator();
while (it.hasNext())
{
System.out.println(it.next().getName());
}
}
}
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);
}
}
/*
class Student implements Comparable<Person>//<? super E>
{
public int compareTo(Person s)
{
this.getName();
}
}
class Comp implements Comparator<Person>
{
public int compare(Person s1,Person s2)
{
Person s1 = new Student("abc1");
return s1.getName().compareTo(s2.getName());
}
}
TreeSet<Student> ts = new TreeSet<Student>(new Comp);
ts.add(new Student("abc1"));
ts.add(new Student("abc2"));
ts.add(new Student("abc3"));
*/
import java.util.*;
class GenericDemo9
{
public static void main(String[] args)
{
//TreeSet<Student> ts = new TreeSet<Student>(new StuComp());
//把比较器扔进来
TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc03"));
ts.add(new Student("abc02"));
ts.add(new Student("abc06"));
ts.add(new Student("abc01"));
Iterator<Student> it = ts.iterator();
while (it.hasNext())
{
System.out.println(it.next().getName());
}
//TreeSet<Worker> ts1 = new TreeSet<Worker>(new WorkComp());
TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());
ts1.add(new Worker("wabc--03"));
ts1.add(new Worker("wabc--02"));
ts1.add(new Worker("wabc--06"));
ts1.add(new Worker("wabc--01"));
Iterator<Worker> it1 = ts1.iterator();
while (it1.hasNext())
{
System.out.println(it1.next().getName());
}
}
}
/*
class StuComp implements Comparator<Student>
{
public int compare(Student s1,Student s2)
{
return s1.getName().compareTo(s2.getName());
}
}
class WorkComp implements Comparator<Worker>
{
public int compare(Worker s1,Worker s2)
{
return s1.getName().compareTo(s2.getName());
}
}
*/
//两个比较器,如果只指定当前类型,只能比较当前对象。
//指定父类型,Person既能接收Student又能接收Worker
class Comp implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
return p2.getName().compareTo(p1.getName());
}
}
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);
}
}
class Worker extends Person
{
Worker(String name)
{
super(name);
}
}