------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一、泛型
1,泛型是jdk1.5出现的安全机制。
好处:将运行时期的问题ClassCastException转到了编译时期,避免了强制转换的麻烦。(参考数组的定义)
泛型什么时候用?当操作的引用数据类型(只能是引用型数据类型,不可以是基本数据类型)不确定的时候,
就是用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或者接口,就要明确传入具体的数据类型。
注意:泛型可以两边都不写;两边都写,都写的时候两边的类型必须一致;仅写一边也是可以的。
泛型技术是给编译器使用的技术,用于编译时期,确保类型的安全。运行时会将泛型去掉,生成的class文件是
不带泛型的,称为泛型的擦除。为了兼容运行的类加载器。
泛型的补偿:在运行时,通过获取元素类型进行转换动作,不用使用者再强制转换了。
2,向TreeSet(二叉树结构)集合中添加元素时,要么元素要具有比较功能(实现Comparable接口,覆盖
comapreTo(Object o方法);要么集合具有比较功能,即向TreeSet的构造函数中传递Comparator接口的
子类对象(自定义比较器)。二者选其一,当二者都存在时,以后者为主。代码演示:
import java.util.Iterator;
import java.util.TreeSet;
public class GenericDemo {
public static void main(String[] args) {
// 创建集合,使用泛型,泛型中只能放引用数据类型,不可以放基本数据类型
// TreeSet<Person> ts = new TreeSet<Person>();
TreeSet<Person> ts = new TreeSet<Person>(new ComparatorByName());
// 添加元素,TreeSet为二叉树结构,它的元素应该具有比较性,
// 即要么实现Comparable接口,要么实现让TreeSet具有比较行
ts.add(new Person("lisi4",21));
ts.add(new Person("lisi2",22));
ts.add(new Person("lisi1",23));
ts.add(new Person("lisi6",24));
// 使用迭代器取出元素
for(Iterator<Person> it = ts.iterator(); it.hasNext();){
// 由于使用了泛型,就不在需要强转
Person p = it.next();
System.out.println(p.getName()+"----"+p.getAge());
}
}
}
被添加的元素(Person类):
public class Person implements Comparable<Person>{
private String name;
private int age;
Person(){
super();
}
Person(String name,int age){
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
// 覆盖compareTo方法,按照age排序
public int compareTo(Person p){
int temp = this.age-p.age;
return temp==0? this.name.compareTo(p.name):temp;
}
}
自定义比较器
import java.util.Comparator;
public class ComparatorByName implements Comparator<Person> {
@Override
// 覆盖compare方法,按照name进行排序
public int compare(Person o1, Person o2) {
// TODO Auto-generated method stub
int temp = o1.getName().compareTo(o2.getName());
return temp==0?o1.getAge()-o2.getAge():temp;
}
}
3,泛型类、泛型方法、泛型接口
泛型类:类名<T>,其中T为要使用的类型
泛型方法: a,一般函数可以自定义泛型,形式:public <Q> void show(Q q){}
b,一般函数可以使用类上定义的泛型,形式:public void print(T,t){},其中T为类上定义的泛型
c,静态函数不可以使用类上定义的泛型,如果使用泛型,只能定义在方法上。
形式:public static <Q> void method(Q,q){}
注意:泛型定义在方法上时,只能定义在修饰符后,返回类型前。
泛型接口:将泛型定义在接口上,interface Inter<T>,类在实现接口时,可以指定泛型也可以不指定
演示代码:
public class GenericDemo2 {
public static void main(String[] args) {
// 泛型类
show1();
// 泛型方法
show2();
// 泛型接口
show3();
}
/**
*
*/
private static void show3() {
// 泛型接口1
InterImpl1 i1 = new InterImpl1();
i1.show("abc");
// 泛型接口2,在使用时才明确要传入的内容
InterImpl2<Integer> i2 = new InterImpl2<Integer>();
i2.show(4); // 自动装箱
}
/**
*
*/
private static void show2() {
// 泛型方法
Tool<String> tool = new Tool<String>();
tool.show("abc");
/*
* Tool已经限定了String类,再传其他类就会报错
* 如果还想能够显示其他类,那么就可以在方法上定义泛型
* public void show(String str){System.out.println("show:"+str);}
*/
// tool.show(new Integer(4));
// 将show函数添加上泛型后,public <Q> void show(Q str){System.out.println("show:"+str);}
// 这条语句就可以通过编译了
tool.show(new Integer(4));
tool.print("abc");
// public void print(T str) print方法使用的泛型与类一致,故只能打印与类限定的类型一致的类型
// tool.print(new Integer(4));
}
/**
*
*/
private static void show1() {
// 使用自定义泛型类
Tool<Student> tool = new Tool<Student>();
tool.setT(new Student());
// 由于泛型限定了类型,故传递与泛型不同的类型会出现错误。
// 将运行时期的错误 体现到了编译时期,提高了安全性。
// tool.setT(new Worker());
Person p = tool.getT();
}
}
对应的工具类及接口:
/*
* jdk1.5后,使用泛型来接收类中要操作的引用数据类型
* 当类中要操作的引用数据类型不确定的时候,就使用泛型来表示
*/
public class Tool<T> {
private T t;
public void setT(T t){
this.t = t;
}
public T getT(){
return t;
}
/*
* 一般方法可以使用类上的泛型,也可以自定义泛型
* 泛型定义在修饰符后,返回值前
*/
// 自定义泛型
public <Q> void show(Q str){
System.out.println("show:"+str);
}
// 使用类上的泛型
public void print(T str){
System.out.println("print:"+str);
}
/*
* 当方法为静态时,不能使用类上定义的泛型,
* 如果静态方法使用泛型,只能定义在方法上
*/
public static <T> void method(T str){
System.out.println("method:"+str);
}
}
interface Inter<T>{
public void show(T t);
}
// 泛型接口形式1
class InterImpl1 implements Inter<String>{
@Override
public void show(String t) {
System.out.println("show1:"+t);
}
}
// 泛型接口形式2
class InterImpl2<Q> implements Inter<Q>{
@Override
public void show(Q q) {
System.out.println("show2:"+q);
}
}
4,泛型的高级应用
由于集合存储的对象互不相同,为了取出其中的元素,就需要使用不同的迭代器来取出数据,可不可以使用
一个迭代器来进行多个不同集合元素的操作呢?
思路:首先集合有多种,比如ArrayList、LinkedList、TreeSet等,但他们都是Collection的子类。其次不同集合的元素
类型可能不同,为了能够接收不同的元素的类型,使用通配符?来表示。
演示代码:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
public class GenericAdvanceDemo {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("cde");
LinkedList<String> al2 = new LinkedList<String>();
al2.add("abc2");
al2.add("cde2");
ArrayList<Integer> al3 = new ArrayList<Integer>();
al3.add(4);
al3.add(5);
// 将al的元素打印出
printCollection(al);
/*
* 怎么能用printCollection(al)同时能够打印al和al2中的元素?
* 二者使用的泛型相同,类名不同,但是二者都是Collection的子类,因此
* 将方法中的ArrayList改为Collection即可
*/
printCollection(al2);
/*
* 对于al3,由于泛型又发生了变化,为了使用printCollection方法,需要
* 对泛型进行修改,这时就可以使用通配符?来表示泛型
*/
printCollection(al3);
}
// 可以打印al
// private static void printCollection(ArrayList<String> al) {
// for(Iterator<String> it = al.iterator(); it.hasNext(); ){
// System.out.println(it.next());
// }
// }
// 可以打印al和al2
// private static void printCollection(Collection<String> al) {
// for(Iterator<String> it = al.iterator(); it.hasNext(); ){
// System.out.println(it.next());
// }
// }
// 可以打印al、al2和al3
private static void printCollection(Collection<?> al) {
for(Iterator<?> it = al.iterator(); it.hasNext(); ){
System.out.println(it.next());
}
}
}
泛型的限定(? extends E):接收E及E的子类对象,上限
(? super E):接收E及E的父类对象,下限
一般在存储元素的时候都使用上限,因为这样取出是按照上限类型来运算的,不会出现类型安全隐患(用的较多)。
通常对结合中的元素进行取出操作时,可以使用下限。比如用于比较器Comparator<? super E>(用的较少)。
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">通配符的使用可以向其中传递任何类型的元素,为了限定元素的类型,就使用到了泛型的限定 </span>
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class GenericAdvanceDemo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Worker> al = new ArrayList<Worker>();
al.add(new Worker("xiaoming",33));
al.add(new Worker("xiaoqiang",22));
ArrayList<Student> al2 = new ArrayList<Student>();
al2.add(new Student("wangcai",24));
al2.add(new Student("huaniu",31));
ArrayList<String> al3 = new ArrayList<String>();
al3.add("abc");
al3.add("bkk");
// ? extends E
printCollection(al);
printCollection(al2);
// printCollection(al3); //编译无法通过,因为?extends person限定了泛型的类型
// ? super E
// printCollection2(al); // 因为只能传递Student及其父类Person,故传递Worker会报错
printCollection2(al2);
}
/**
* @param al
*/
// 泛型的限定 Collection<? extends Person>,只能传递Person及Person的子类
private static void printCollection(Collection<? extends Person> coll) {
for(Iterator<?> it = coll.iterator(); it.hasNext();){
System.out.println(it.next().toString());
// 当对类型进行限定后,就可以使用Person类来接收子类对象
// Person p = it.next();
// System.out.println(p.getName()+":"+p.getAge());
}
}
private static void printCollection2(Collection<? super Student> coll){<span style="white-space:pre"> </span>// 只能接收本类及其父类
for(Iterator<? super Student> it = coll.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
}
import java.util.Iterator;
import java.util.TreeSet;
public class GenericAdvanceDemo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet<Person> ts1 = new TreeSet<Person>(new ComparatorByName());
ts1.add(new Person("wangcai--Person",33));
ts1.add(new Person("xiaoqiang--Person",22));
// 学生类不需要再自定义比较器,可以使用父类的比较器
TreeSet<Student> ts2 = new TreeSet<Student>(new ComparatorByName());
ts2.add(new Student("xiaoming--Student",18));
ts2.add(new Student("huaniu--Student",17));
ts1.addAll(ts2);
for(Iterator<? extends Person> it = ts1.iterator(); it.hasNext();){
Person p = it.next();
System.out.println(p.getName()+":"+p.getAge());
}
}
}
使用到的Person、Worker以及Student类如下所示:
public class Person implements Comparable<Person>{
private String name;
private int age;
Person(){
super();
}
Person(String name,int age){
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
// 覆盖compareTo方法
public int compareTo(Person p){
int temp = this.age-p.age;
return temp==0? this.name.compareTo(p.name):temp;
}
}
Woker类:
public class Worker extends Person{
public Worker() {
super();
}
public Worker(String name, int age) {
super(name, age);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Worker---"+getName()+":"+getAge();
}
}
Student类:
public class Student extends Person{
public Student(){
super();
}
public Student(String name, int age){
super(name,age);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student---"+getName()+":"+getAge();
}
}
5,集合的一些技巧:
需要唯一吗?
a,需要:Set
a1,需要指定顺序吗? 需要TreeSet;不需要HashSet。需要存储和取出一致吗?需要LinkedHashSet。
b,不需要:List
b1,需要频繁增删吗?需要LinkedList;不需要ArrayList。
看到Array,就要想到数组,就要想到查询快,有角标。
看到Link,就要想到链表,就要想到增删快,就要想到 add/get/remove first/last
看到Hash,就要想到哈希表,就要想到唯一性,就要想到覆盖hashCode方法和equals方法
看到tree,就要想到二叉树,就要想到排序,就要想到两个接口:Comparable和Comparator
通常这些常用的集合都是非同步的,Vector是同步的。
二、集合工具类Collections
Collections是集合框架的工具类,里面的方法都是静态的。
1,排序:使用sort()或带有比较器的sort()
演示代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
// 创建List集合,添加元素
List<String> list = new ArrayList<String>();
list.add("abcsd");
list.add("sabsdcsd");
list.add("easabcsd");
list.add("wcaabcsd");
list.add("ssabcsd");
System.out.println(list); // [abcsd, sabsdcsd, easabcsd, wcaabcsd, ssabcsd]
// 对List集合进行排序
Collections.sort(list);
System.out.println(list); // [abcsd, easabcsd, sabsdcsd, ssabcsd, wcaabcsd]
Collections.sort(list,new ComparaByLength());
System.out.println(list); // [abcsd, ssabcsd, easabcsd, sabsdcsd, wcaabcsd]
mySort(list);
System.out.println(list); // [abcsd, easabcsd, sabsdcsd, ssabcsd, wcaabcsd]
mySort(list,new ComparaByLength());
System.out.println(list); // [abcsd, easabcsd, sabsdcsd, ssabcsd, wcaabcsd]
}
// 自定义排序
public static <T extends Comparable<? super T>> void mySort(List<T> list){
for(int i=0; i<list.size()-1; i++){
for(int j=i+1; j<list.size(); j++){
if(list.get(i).compareTo(list.get(j))>0){
// T temp = list.get(i);
// list.set(i, list.get(j));
// list.set(j, temp);
Collections.swap(list,i,j);
}
}
}
}
// 自定义排序,使用自定义的比较器
public static <T> void mySort(List<T> list, Comparator<? super T> comp){
for(int i=0; i<list.size()-1; i++){
for(int j=i+1; j<list.size(); j++){
if(comp.compare(list.get(i), list.get(j))>0){
Collections.swap(list,i,j);
}
}
}
}
}
// 定义比较器
class ComparaByLength implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
int temp = o1.length()-o2.length();
return temp==0?o1.compareTo(o2):temp;
}
}
2,二分查找binarySearch,
注意该方法只适用于List集合,同时在使用该方法之前要先进行排序。
(自定义)查找集合中最大最小元素,使用max、min方法。
演示代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("cba");
list.add("zzz");
list.add("nab");
list.add("fifa");
// 对集合进行排序并打印
Collections.sort(list);
System.out.println(list);
// 通过二分查找,找到指定元素的位置,注意要先进行排序
int index = Collections.binarySearch(list, "fifa");
System.out.println(index);
// 查找最大/小元素
String max = Collections.max(list);
String min = Collections.min(list);
System.out.println(max);
System.out.println(min);
// 按照自定义比较器查找最大元素
String max_2 = Collections.max(list,new ComparaByLength());
String min_2 = Collections.min(list,new ComparaByLength());
System.out.println(max_2);
System.out.println(min_2);
}
}
3,reverseOrder、 replaceAll、fill、shuffle(适用于List)以及给非同步集合进行加锁
演示代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
public class CollectionsDemo3 {
public static void main(String[] args) {
// reverseOrder
show1();
// 对自定义比较器进行反向排序
show2();
// repalceAll,fill,shuffle方法只适用于List
show3();
// 给List等集合进行加锁
show4();
}
private static void show4() {
List<String> ts = new ArrayList<String>();
ts.add("abc");
ts.add("nba");
ts.add("mba");
Collections.synchronizedList(ts);
}
private static void show3() {
List<String> ts = new ArrayList<String>();
ts.add("abc");
ts.add("nba");
ts.add("mba");
System.out.println(ts); // [abc, nba, mba]
// 随机排序
Collections.shuffle(ts);
System.out.println(ts);
// 替换指定元素
Collections.replaceAll(ts, "abc", "ABC");
System.out.println(ts); // [ABC, nba, mba]
// 用指定元素替换集合内所有的元素
Collections.fill(ts, "CBA");
System.out.println(ts); // [CBA, CBA, CBA]
}
private static void show2() {
// 将自定义比较器作为Collections。reverseOrder()的参数
TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder(new ComparaByLength()));
ts.add("abc");
ts.add("hahaah");
ts.add("zzz");
ts.add("aa");
ts.add("cba");
System.out.println("self-define:"+ts);
}
// 将Collections。reverseOrder()作为集合的参数进行传递
private static void show1() {
TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
ts.add("abc");
ts.add("hahaah");
ts.add("zzz");
ts.add("aa");
ts.add("cba");
System.out.println(ts);
}
}
4,Arrays数组工具类
一般方法:可以进行二分查找(除了布尔型数组都可以查找),可进行排序、填充、复制等操作
重点方法:asList,将数组转成集合,可以使用集合的方法操作数组中的元素。
注意:数组的长度是固定的,所以集合的增删方法是不可以使用的,
否则会引起UnsupportedOperationException
注意2:如果数组中的元素是对象(比如字符串数组),那么转成集合时,直接将数组中的元素
作为集合中的元素进行存储。如果数组中的元素是基本数值类型,那么会将数组作为集合的元素
进行传递。
演示代码:
import java.util.Arrays;
import java.util.List;
public class CollectionsDemo4 {
public static void main(String[] args){
// 常用简单方法
show1();
// 重点方法asList
show2();
// 数组中的元素是基本数据类型
show3();
}
private static void show3() {
/*
* 如果数组中的元素是对象(比如字符串数组),那么转成集合时,直接将数组中的元素
* 作为集合中的元素进行存储。如果数组中的元素是基本数值类型,那么会将数组作为集合的元素
* 进行传递。
*/
int[] arr = {1,2,3,4};
// 注意泛型装的是int[]
List<int[]> list = Arrays.asList(arr);
System.out.println(list); // [[I@19e0bfd]
}
private static void show2() {
String[] arr = {"abc","aaa","cba"};
List<String> list = Arrays.asList(arr);
System.out.println(list.contains("aaa")); // true
// 对于集合的增删会引起UnsupportedOperationException
// list.add("nba");
}
/**
*
*/
private static void show1() {
int[] arr1 = {1,2,3,4};
int[] arr2 = {1,2,3,4};
char[] arr3 = {'a','b','c','d'};
// 二分查找(除了布尔型数组都可以用)
Arrays.sort(arr3);
System.out.println(Arrays.binarySearch(arr3,'c'));
// equals方法,判断两个数组是否相等
System.out.println(Arrays.equals(arr1, arr2));
// 讲一个数组的元素的一部分赋值给另一个数组
char[] arr4 = Arrays.copyOf(arr3, 5);
// 用指定数值填充数组
Arrays.fill(arr1, 8);
// 用toString方法显示数组元素
System.out.println(Arrays.toString(arr1));
System.out.println(myToString(arr1));
}
// toString的经典实现。
private static String myToString(int[] arr){
int iMax = arr.length-1;
if(iMax == -1)
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i=0; ; i++){
sb.append(arr[i]);
if(i==iMax)
return sb.append(']').toString();
sb.append(",");
}
}
}
5,集合转数组。
使用的是Collection接口中的toArray方法。集合转成数组,可以对集合中的元素进行限定,不允许对其进行增删。
注意:toArray方法需要传入一个指定类型的数组,大小该如何确定?
如果数组长度小于集合的size,那么该方法会创建一个和集合相同大小的数组
如果数组长度大于集合的size,那么该方法会使用指定的数组存储集合中的元素,其他位置默认为null
演示代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CollectionsDemo5 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
// 结合转成数组并打印
/*
* toArray方法需要传入一个指定类型的数组,大小该如何确定?
* 如何数组长度小于集合的size,那么该方法会创建一个和集合相同大小的数组
* 如果数组长度大于集合的size,那么该方法会使用指定的数组存储集合中的元素,其他位置默认为null
*/
String[] arr = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(arr)); // [abc1, abc2, abc3]
}
}
6,增强for循环:for(类型 变量:集合/数组){code...;}
传统for和增强for的区别:
传统for可以完成对语句多次执行,因为可以定义执行循环的增量和条件。
增强for是一种简化形式,它必须有被遍历的目标,该目标要么是数组,要么是Collection单列集合。
对数组遍历如果仅仅是获取数组中的元素,可以使用增强for。
如果需要对数组角标进行操作,建议使用传统for。
代码演示:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class CollectionsDemo6 {
public static void main(String[] args) {
// 增强for遍历集合
show1();
// 增强for遍历数组
show2();
// 使用增强for遍历Map集合
// 不可以直接用,将Map转换为单列Set
show3();
}
private static void show3() {
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(4, "zhangsan");
map.put(5, "xiaoqiang");
map.put(6, "wangcai");
// 通过遍历keySet
for(Integer key:map.keySet()){
String value = map.get(key);
System.out.println(key+"--"+value);
}
// 通过遍历entrySet
for(Map.Entry<Integer, String> me:map.entrySet()){
Integer key = me.getKey();
String value = me.getValue();
System.out.println(key+"--"+value);
}
}
private static void show2() {
int[] arrs = {1,2,3,4,5};
for(int arr:arrs){
System.out.println(arr);
}
}
/**
*
*/
private static void show1() {
// 定义集合,添加元素
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
// 使用迭代器取出元素
for(Iterator<String> it= list.iterator(); it.hasNext();){
System.out.println(it.next());
}
// 使用增强for,简化了书写
for(String str:list){
System.out.println(str);
}
}
}
7,数组新特性:函数的可变参数
其实就是一个数组,但是接收的是数组中的元素,自动将这些元素封装到数组,简化了书写。
注意事项:可变参数类型,必须定义在参数列表的结尾
演示代码:
public class CollectionsDemo7 {
public static void main(String[] args) {
// TODO Auto-generated method stub
int sum = newAdd(3,1,2,3,4,41);
System.out.println(sum);
}
// 可变参数必须放到参数列表的末尾
private static int newAdd(int a, int... arr){
int sum = a;
for(int i=0; i<arr.length; i++){
sum+=arr[i];
}
return sum;
}
}
8,静态导入,其实导入的是类中的静态成员
比如使用Collections中的静态方法,可以这样导包
import static java.util.Collections.*
The End