------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、泛型
了解泛型之前我们先看一下没有泛型会产生什么结果:
package com.leaf.test;
import java.util.ArrayList;
import java.util.List;
class ListDemo
{
public static void main(String[] args)
{
List list = new ArrayList();
list.add("abc");
list.add("abc");
list.add(new Integer(1));
String str = (String)list.get(2);
}
}
result:
定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于result中的错误。因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。
在我们平时的代码中,我们发现主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。
那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。
泛型的基本使用:
示例(错误):
示例(正确):
import java.util.*;
/*
泛型的最基本使用
*/
package com.leaf.generic;
import java.util.ArrayList;
import java.util.List;
class GenericDemo
{
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("abc");
System.out.println(list);
}
}
result:
泛型的定义:
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型是提供给javac编译器使用的,可以限定集合输入的类型,让编译器挡住源程序的非法输入, 是程序的运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全 一样,由编译器生成的字节码文件会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可
泛型的原理及深层使用:泛型的术语、泛型的通配符、限定通配符
泛型的术语:
ArrayList<E>中的E称为类型变量或者类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或者实际类型参数
ArrayList<Integer>中的<>称为typeOf
ArrayList称为原始类型
参数化类型与原始类型的兼容性
参数化类型可以引用一个原始类型的对象,编译报告警告,例如:
Collection<String> c = new Vector();//可不可以就是编译器的一句话
原始类型可以引用一个参数化类型的对象,编译器警告,例如:
Collection c = new Vector<String>();
参数化类型不考虑参数的继承关系
Vector<String> v = newVector<Object>();//错误
Vector<Object> v = newVector<String>();//错误
在创建数组实例时,数组的元素不能使用参数化的类型,例如:
Vector<Integer> vectorList[] = newVector<Integer>[10];
下列代码可以:
Collection c = newCollection<String>();
Collection<Object> col = c;
是不是看不懂上面这些东西,下面来写一个示例:
package com.leaf.generic;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
public class GenericTest {
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 没有泛型的集合,会报警告
ArrayList col = new ArrayList();
col.add(1);
// 会因为类型无法转换而报错
// int i = (Integer)col.get(1);
// 泛型的定义在字节码文件中会被去除
ArrayList<Integer> col1 = new ArrayList<Integer>();
ArrayList<String> col2 = new ArrayList<String>();
// col.add(1);//报错,因为类型不匹配,Integer类型的数据无法存入定义了String类型泛型的集合中
// col.add(1L);//同上
col2.add("abc");
String str = col2.get(0);
System.out.println("col1和col2的字节码文件是一样的吗?-->"
+ col1.getClass().equals(col2.getClass()));
// 反射也可以定义泛型
Constructor<String> con = String.class.getConstructor();
String s = con.newInstance();
// printCollection(col2);//定义了限定通配符,传入String类型泛型的集合就会报错
printCollection(col1);
}
//通配符"?"表示任何类型 extends表示必须是Number或者Number的子类才可以,可以使用&限定多个类
public static void printCollection(Collection<? extends Number> col){
/*
* 使用通配符得到的集合不能使用add等与具体类型有关系的方法
* col.add(1)//错误,因为他不知道会匹配到什么类型
* 可以使用与类型无关的方法,如:size();
*
* 使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要作用
* 是用做引用,可以调用与参数无关的方法,不能调用与参数有关的方法
*/
System.out.println(col.size());
}
}
result:
示例2:一般情况下,编写代码能遇到的最复杂的泛型就是在使用Map集合的Map.Entry的时候了,如下述代码,你能看懂了,泛型对你来说也就算基本掌握了
package com.leaf.generic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class GenericTest1 {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("zs",20);
map.put("laobi", 23);
map.put("zxx", 12);
Set<Map.Entry<String,Integer>> set = map.entrySet();
for(Iterator<Map.Entry<String, Integer>> it = set.iterator();it.hasNext();){
Map.Entry<String, Integer> me = it.next();
System.out.println(me.getKey()+":"+me.getValue());
}
Collection<String> col = new ArrayList<String>();
col.add("a");
col.add("b");
col.add("c");
addCollection(col, "d");
System.out.println(col);
String[] strArr = new String[4];
copyColToArr(col, strArr);
System.out.println(Arrays.asList(strArr));
}
public static <T> void addCollection(Collection<T> col,T t){
col.add(t);
}
public static <T> T[] copyColToArr(Collection<T> col,T[] arr){
Iterator<T> it = col.iterator();
int count = 0;
while(it.hasNext()){
T t = it.next();
arr[count] = t;
count++;
}
return arr;
}
}
result:
二、Collections工具类:
示例:
对集合进行排序
package com.leaf.test;
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) {
demo1();
}
public static void demo1() {
List<String> list = new ArrayList<String>();
list.add("abcde");
list.add("bcda");
list.add("cdsfe");
list.add("sdfdefdfg");
list.add("efdfg");
list.add("hasfhfg");
// 对list集合进行指定顺序的排序,使用collections提供的方法是按照list集合中的比较器比较,也就是按照字母的ASSIC码表的顺序
Collections.sort(list);
System.out.println(list);
//对集合使用自定义的排序方式,使用集合中字符串的长度进行排序
Collections.sort(list, new ComparatorByLength());
System.out.println(list);
//模拟写一个和collections中提供的方法一样的方法
mySort(list, new ComparatorByLength());
System.out.println(list);
}
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 ComparatorByLength implements Comparator<String> {
public int compare(String o1, String o2) {
int temp = o1.length() - o2.length();
return temp == 0 ? o1.compareTo(o2) : temp;
}
}
result:
注:工具类,内部都是静态方法,直接使用Collections.方法名调用
示例:
package com.leaf.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionsDemo2 {
public static void main(String[] args) {
demo1();
}
public static void demo1() {
List<String> list = new ArrayList<String>();
list.add("abcde");
list.add("bcda");
list.add("cdsfe");
list.add("sdfdefdfg");
list.add("efdfg");
list.add("hasfhfg");
//寻找:
int index = Collections.binarySearch(list, "bcda");
System.out.println("index:"+index);
//获取长度最长的字符串
String max = Collections.max(list);
System.out.println("max="+max);
}
class ComparatorByLength implements Comparator<String> {
public int compare(String o1, String o2) {
int temp = o1.length() - o2.length();
return temp == 0 ? o1.compareTo(o2) : temp;
}
}
}
result:
示例:
package com.leaf.test;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;
public class CollectionsDemo3 {
public static void main(String[] args) {
System.out.println("使用了Collections.reserseOrder(...)");
demo3();
System.out.println("未使用了Collections.reserseOrder(...)");
demo4();
}
public static void demo4(){
TreeSet<String> ts = new TreeSet<String>();
ts.add("abc");
ts.add("hahaha");
ts.add("zzz");
ts.add("aa");
ts.add("cba");
System.out.println(ts);
}
public static void demo3() {
TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder());
ts = new TreeSet<String>(
Collections.reverseOrder(new ComparatorByLength1()));
ts.add("abc");
ts.add("hahaha");
ts.add("zzz");
ts.add("aa");
ts.add("cba");
System.out.println(ts);
}
}
class ComparatorByLength1 implements Comparator<String> {
public int compare(String o1, String o2) {
int temp = o1.length() - o2.length();
return temp == 0 ? o1.compareTo(o2) : temp;
}
}
result:
示例:
package com.leaf.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo4 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("b");
list.add("cds");
list.add("sdfde");
list.add("abc");
list.add("ha");
System.out.println("-------------------------------------");
demo5(list);
System.out.println("-------------------------------------");
demo6(list);
}
public static void demo5(List<String> list){
//replaceAll方法
System.out.println("使用replaceAll方法替换元素");
System.out.println("未替换");
System.out.println(list);
Collections.replaceAll(list, "abc", "wahaha");
System.out.println("替换后。。。");
System.out.println(list);
}
public static void demo6(List<String> list){
//使用默认随机源随机更改指定列表的序列。所有序列更改发生的可能性都是大致相等的。
System.out.println("使用shuffle更改指定列表的序列");
System.out.println("更改前");
System.out.println(list);
Collections.shuffle(list);
System.out.println("更改后。。。");
System.out.println(list);
}
}
result:
示例:
package com.leaf.test;
import java.util.ArrayList;
import java.util.List;
public class CollectionsDemo5 {
/**
* 给非同步的集合加锁
* @param args
*/
public static void main(String[] args) {
//非同步的集合
List list = new ArrayList();
//加上锁,变成同步的
list = new MyCollections().synList(list);
}
}
class MyCollections{
public List synList(List list){
return new MyList(list);
}
private class MyList extends ArrayList{
private List list;
private final Object lock = new Object();
MyList(List list){
this.list = list;
}
public boolean add(Object obj){
synchronized(lock){
return list.add(obj);
}
}
public boolean remove(Object obj){
synchronized(lock){
return list.remove(obj);
}
}
}
}
三、Arrays工具类
数组的工具类,里面的存放的都是操作数组的静态方法
示例:
package com.leaf.test;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int[] arr = { 10, 5, 6, 8, 2, 9, 2, 3, 4, 5 };
//打印出数组
System.out.println("-----------打印出数组------------");
System.out.println(Arrays.toString(arr));
//对数组进行排序
System.out.println("-----------数组排序------------");
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
result:
重点:
List asList(数组)将数组转成集合
好处:
可以使用集合的方法操作数组
弊端:
因为数组的长度是有限的,所以集合的增删操作是不可用的,否则会发送UnsupportedOperationException。
示例:
package com.leaf.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArraysDemo2 {
public static void main(String[] args) {
String[] arr = {"abc","qwe","zxc"};
List<String> list = new ArrayList<String>();
System.out.println(list);
list = Arrays.asList(arr);
System.out.println(list);
System.out.println(list.contains("abc"));
}
}
result:
示例:由于集合中不能存储基本数据类型,所以当数组中的元素是基本数据类型的时候,那么数据就变成对象存入集合,如果数组中的元素是对象,则是对象变成集合中的元素
package com.leaf.test;
import java.util.Arrays;
import java.util.List;
class ArraysDemo4 {
public static void main(String[] args) {
int[] arr1 = { 3, 1, 5, 6 };
List list1 = Arrays.asList(arr1);
System.out.println(list1);
Integer[] arr2 = { 3, 1, 5, 6 };
List list2 = Arrays.asList(arr2);
System.out.println(list2);
}
}
result:
集合转数组:
使用的是Collection接口中的toArray方法
注:集合转成数组,可以对集合中的元素操作的方法进行限定,不允许对其进行增删
toArray方法需要传入一个指定类型的数组
长度该如何定义?
使用集合的size方法得到集合的长度,进行定义数组。
示例:
package com.leaf.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ColToArray {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
String[] arr = list.toArray(new String[2]);
System.out.println(Arrays.toString(arr));
}
}
result: