1、泛型
泛型是JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。
如:ArrayList<String> a1 = new ArrayList<String>( );
这就表示这个集合存入的类型为String类。
1)泛型好处:
将运行时期出现的问题ClassCastException,转移到编译时期,方便与程序员解决问题,让运行问题减少,更安全;
避免强制转换的麻烦。
如:迭代器:Terator<String> it = a1.iterator();
则不需强转类型:String s =it.next();
2)泛型的格式:
通过<>来定义要操作的引用数据类型。
3)在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,只要见到<>就是要定义泛型,<>就是用来接受引用数据类型的。
4)注意:
A、用equals覆写Object时,因其没有泛型,所以在用时要强转类型,在转之前还要判断一下是否同类,即:instanceof。
B、泛型类: 当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成。但是在对泛型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
C、除了定义泛型类,还可以定义泛型方法,泛型方法可以放到泛型类里面,即泛型类里还可以有泛型方法定义。
D、静态方法不可以访问类上定义的泛型,如果静态方法操作的应用数据类型不确定,可将泛型定义在方法上。
如:public static <T> void method(T t){}
E、可定义泛型接口:
如:interface Inter<T>
{
Void show(T t);
}
在实现泛型接口时,可以在实现就确定接口类型:
如: implements Inter<String>
5)泛型限定
当传入的类型不确定时,可以使用通配符?,也可以理解为占位符<?>。使用通配符的好处是可以不用明确传入的类型,这样在使用泛型类或者泛型方法时,提高了扩张性。
对于一个范围内的一类事物,可以通过泛型限定的方式定义:
两种方式:
A、? extends E:可接收E类型或E类型的子类型,称为上限定。
如:ArrayList<? extends Number> x = new ArrayList<Integer>();
B、? super E:可接收E类型或E类型的父类型,称为下限定。
如:ArrayList<? super Integer> x = new ArrayList<Number>();
2、Map集合
Map<K,V>集合是一个接口,它与List、set集合不同的是,它是双列集合,以键值对的形式存储。集合存储键值对,是一对一对往里存,而且要保证键的唯一性。
1)Map集合的子集
Hashtable:底层是哈希表数据结构,不可以存入null键和null值,该集合是线程同步的,效率低。
HashMap:底层是哈希表数据结构,允许使用null键和null值,该集合是线程不同步的,效率高,JDK1.2版本出现,替代了Hashtable。
TreeMap:底层是二叉树数据结构,线程不同步,可以用于给Map集合中的键进行排序。
Map和Set很像。其实Set底层就是使用了Map集合。
2)Map集合共性方法
A、添加
V put(K key,V value):添加元素,添加时如果出现相同的键,那么后添加的值会覆盖原有键对应值,并put方法会返回被覆盖的值;
void putAll(Map<? extends K,? extends V> m):添加一个集合;
B、删除
clear():清空集合;
V remove(Object key):删除指定键值对,若不存在,返回null;
C、判断
containsValue(Object value):判断值是否存在;
containsKey(Object key):判断键是否存在;
isEmpty():判断集合是否为空;
D、获取
V get(Object key):通过键获取对应的值,若无返回null;
size():获取集合的长度;
Collection<V> values():获取集合中所有值,返回个Collection集合;
还有两个取出方法,很重要:
Set<Map.Entry<K,V>> entrySet():返回映射中含映射关系的集合;
Set<K> keySet():返回含键的集合。
3)Map的两种取出方式:
A、keySet( ):将Map中所有的键存入到Set集合,因为Set集合具备迭代器,所以可以通过迭代方式取出所有键值,再通过get(key)方法,获取每一个键对应的值。
Map集合的取出原理:将Map集合转成Set集合,再通过迭代器取出。
B、entrySet():将Map集合中的映射关系存入到Set集合中,而这个关系的数据类型就是Map.Entry;
关于Map.Entry:其实Entry也是一个接口,它是Map接口中的一个内部接口。
为何要定义在其内部呢?
a、Map集合中存的是映射关系这样的两个数据,是先有Map这个集合,才可有映射关系的存在,而且此类关系是集合的内部事务;
b、这个映射关系可直接访问Map集合中的内部成员,所以定义在内部。
Map.Entry<k,v>接口含有方法:
getKey();getValue();setValue()。
综合示例:
学生Student,地址String,每一个学生都有对应的归属地;
学生属性:姓名,年龄;
保证学生的唯一性:姓名和年龄相同的视为同一个学生。
思路:1、先描述学生;
2、定义map集合,将学生作为键,地址作为值存入;
3、获取map集合中的元素。
import java util.*;
class Student implementsComparable<Student>
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
public int compareTo(Student s)
{
int num =
new Integer(this.age).compareTo(newInteger(s.age));
if(num==0)
returnthis.name.compareTo(s.name);
return num;
}
public int hashCode()
{
return name.hashCode()+age*10;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw newException(“对象不是学生”);
Student s = (Student)obj;
Return this.name.equals(s.name)&& this.age==s.age;
}
public String getName()
{
return name;
}
Public int getAge()
{
Return age;
}
public String toString()
{
return name+”:”+age;
}
}
class MapDemo
{
public static voidmain(String[] args)
{
HashMap<Student,String> hm =
new HashMap<Student,String>();
hm.put(new Student(“zhangsan”,20),”shanghai”);
hm.put(new Student(“lisi”,25),”beijing”);
hm.put(new Student(“wangwu”,22),”guangzhou”);
//第一种取出方式:
Set<Student> keyset = hm.keySet();
Iterator<Student>it = keySet.iterator();
while(it.hasNext())
{
Student stu =it.next();
String addr =hm.get(stu);
System.out.println(stu+”:”addr);
}
//第二种取出方式:
Set<Map.Entry<Student,String>> entrySet = hm.keySet(); Iterator<Map.Entry<Student,String>>iter =
entrySet.iterator();
while(iter.hasNext())
{
Map.Entry<Student,String> me = iter.next();
String stu =me.getKey();
String addr =me.getValue();
System.out.println(stu+”:”addr);
}
}
}
4)Map集合的扩展
A、什么时候用map集合?
当数据之间存在着映射关系的时候,就先考虑使用Map集合。
B、在很多时候应用比较多的是一对多的映射关系,可以通过嵌套的形式将多个映射定义到一个大的集合中,然后分级处理,形成一个体系。
3、collections
Collections是集合框架的一个工具类,里边的方法都是静态的,不需要创建对象,可直接用类调用方法,其大部分方法是用于对List集合进行操作的,如比较、二分法搜索、随机排序等。
1)常见操作
A、查找
max(Collection<?extends T> c):根据集合的自然顺序,获取c集合中的最大元素;
max(Collection<?extends T> c,Comparator<? super T> co):根据指定比较器co的顺序,获取c集合中的最大元素;
int binarySearch(Lsit<?extends Comparable<? super T>> list,Tkey):二分法搜索list集合中的指定对象;
B、替换
void fill(List<? superT> list, T obj):将list集合中的全部元素替换成指定对象obj;
boolean replaceAll(List<T>lsit,T oldValue,T newValue):用newValue替换集合中的oldValue值;
void swap(Listlist,inti,int j):在指定列表的指定位置处交换元素;
C、排序
void shuffle(List<?>list):使用默认随机源对list集合中的元素进行随机排序;
void sort(Lsit<T>list):根据自然顺序对list集合中的元素进行排序;
void sort(List<T>lsit,Comparator<? super T> c):根据指定比较器的排序方式对list集合进行排序;
D、反转
reverse(List<?>list):反转list集合中元素的顺序;
Comparator reverseOrder():返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序;
Comparator reverseOrder(Comparator<T>cmp);//返回一个比较器,强行逆转了指定比较器的顺序;
2)Collections和Collection的区别:
Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。
两个常用的子接口:
List:可以重复元素,有序的,有索引;
Set:不可重复元素,无序的。
Collections是集合框架中的一个工具类,类中的方法都是静态的,提供的方法中有可以对list集合进行排序,二分查找等方法。
因为要提高效率,常用的集合通常都是线程不安全的,所以如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。
4、Arrays
Arrays是集合框架工具类,用于操作数组,包含的都是静态方法。
方法:
1)asList(arr[]):把arr数组变成List集合,这样做的好处就是可以使用集合的思想和方法来操作数组中的元素,如:contains(),get(),subList()等方法。
但是要注意,将数组变成集合,不可使用集合的增删方法,因为数组的长度是固定的,如果进行增删操作,会产生UnsupportedOperationException编译异常;如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
2)String toString():可以接收各种数组类型参数,并返回指定数组内容的字符串表现形式。
5、toArray
<T> T[] toArray(T[] a):将集合转变成数组,是Collection接口中的方法,当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size;当指定类型的数组长度大于了集合的size,就不会新创建了数组,而是使用传递进来的数组,所以创建一个刚刚好的数组最优。
将集合变数组的作用:为了限定对元素的操作(不需要进行增删了)。
6、高级for循环
格式:
for(数据类型变量名: 被遍历的集合(collection)或数组) {执行语句}
1)高级for循环对集合进行遍历,只能获取集合元素,但是不能对集合进行操作,迭代器除了遍历,还可以进行remove集合中元素的动作,如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的操作。
2)高级for和传统for的区别:
高级for循环有一个局限性,必须要有被遍历的目标,集合或数组;
建议在遍历数组的时候,还是使用传统for,因为可对角标定义操作。
7、可变参数
如果一个方法在参数列表中传入了多个参数,个数不确定,那么每次都要复写该方法,这时可以用数组作为形式参数,但是在传入时,每次都需要定义一个数组对象,作为实际参数。
在JDK1.5版本后,就提供了一个新特性:可变参数。
可变参数就是数组参数的简写形式,不用每一次都建立数组对象,只要将要操作的元素作为参数传递即可,隐式将这些参数封装成了数组。
在使用时注意:可变参数一定要定义在参数列表的最后面。
8、静态导入
1)写法:
importstatic java.util.Arrays.*;
导入Arrays这个类中的所有静态成员;
import static java.lang.System.*;
导入System类中所以静态成员。
注意:没加static导入的是类,加上static导入的是某一个类中所有的静态成员,这样在调用该类的静态方法时就可以不用再写类名。
如:Arrays.sort(数组)就可以直接写sort(数组)。
2)当导入的两个类中有同名成员时,需要在成员前加上相应的类名;当类名相同时,就需要指定具体的包名;当方法重名时,需指定具体所属的对象或者类。