黑马程序员——java基础知识之泛型、集合(Map、工具类等)

时间:2023-02-12 11:06:29

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
(一)、泛型
1、泛型:
JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。

2、泛型有什么好处?
①将运行时期出现问题ClassCastException,转移到了编译时期。,
方便于程序员解决问题。让运行时问题减少,安全。,
②避免了强制转换麻烦。

3、泛型的格式
泛型格式:通过<>来定义要操作的引用数据类型。

4、在使用java提供的对象时,什么时候写泛型呢?
通常在集合框架中很常见,
只要见到<>就要定义泛型。
其实<> 就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

例子程序:

class GenericDemo 
{
public static void main(String[] args)
{
//给集合加泛型
ArrayList<String> al = new ArrayList<String>();

al.add("abc01");
al.add("abc0991");
al.add("abc014");

//al.add(4);//al.add(new Integer(4));
//注意迭代器也要加泛型
Iterator<String> it = al.iterator();
while(it.hasNext())
{
String s = it.next();

System.out.println(s+":"+s.length());
}
}
}

例子程序:
注意比较器的时候使用泛型:

class GenericDemo2 
{

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();
System.out.println(s);
}
}
}
//给比较器加泛型
class LenComparator implements Comparator<String>
{

public int compare(String o1,String o2)
{
int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));

if(num==0)
return o2.compareTo(o1);
return num;
}
}

5、泛型定义在类上
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展。

例子程序:

class Worker
{

}
//泛型前做法。
class Tool
{
private Object obj;
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 GenericDemo3
{
public static void main(String[] args)
{
//加泛型后
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Student());
Worker w = u.getObject();;
//加泛型前
/*
Tool t = new Tool();
t.setObject(new Student());
Worker w = (Worker)t.getObject();
*/

}
}

5、泛型定义在方法上
①泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
②为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
例子程序:

class Demo
{
//把泛型定义在方法上
public <T> void show(T t)
{
System.out.println("show:"+t);
}
public <Q> void print(Q q)
{
System.out.println("print:"+q);
}
public static <W> void method(W t)
{
System.out.println("method:"+t);
}
}
class GenericDemo4
{
public static void main(String[] args)
{
Demo d = new Demo();
//调方法时你传什么类型,方法使用什么类型
d.show("haha");
//d.show(4);
d.print(5);
d.print("hehe");

d.show(new Integer(4));
d.print("hah");
}
}

注意:
①泛型定义在类中和定义在方法上,可以一起使用
②静态方法不可以访问类上定义的泛型。静态方法上使用泛型,要把泛型定义在方法上。它的格式是:public static void method(W t)

6、泛型定义在接口中

//接口定义泛型
interface Inter<T>{
void show(T t);
};
//实现类不知道什么类型,也使用泛型,实现Inter接口
class InterImpl<T> implements Inter<T>
{

public void show(T t)
{
System.out.println("show :"+t);
}
}
class GenericDemo5
{

public static void main(String[] args)
{
InterImpl<Integer> i = new InterImpl<Integer>();
i.show(4);
}
}

7、泛型的限定
? 通配符。也可以理解为占位符。
泛型的限定:
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限

例子程序:

//程序的一部分,利用泛型可以接受Person类和Person的子类,也就是上限
public static void printColl(Collection<? extends Person> al)
{
Iterator<? extends Person> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}

总结:
对泛型的定义:
第一、定义泛型:当又不确定的类型需要传入到集合中,需要定义泛型。
第二、定义泛型类:如果类型确定后,所操作的方法都是属于此类型,则定义泛型类。
第三、定义泛型方法:如果定义的方法确定了,里面所操作的类型不确定,则定义泛型方法。

(二)、集合框架(容器)之Map
Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。

Map的主要方法:
1,添加。
put(K key, V value)
putAll()
2,删除。
clear()
remove(Object key)
3,判断。
containsValue(Object value)
containsKey(Object key)
isEmpty()

4,获取。
get(Object key)
size()
values()
entrySet()
keySet()

Map的主要子类:

Map
|–Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。jdk1.0.效率低。
|–HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。
|–TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。

Map方法例子程序:

import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();

//添加元素,添加元素,如果出现添加时,相同的键。那么后添加的值会覆盖原有键对应值。
//并put方法会返回被覆盖的值。
System.out.println("put:"+map.put("01","zhangsan1"));
System.out.println("put:"+map.put("01","wnagwu"));
map.put("02","zhangsan2");
map.put("03","zhangsan3");

System.out.println("containsKey:"+map.containsKey("022"));
//System.out.println("remove:"+map.remove("02"));

System.out.println("get:"+map.get("023"));

map.put("04",null);
System.out.println("get:"+map.get("04"));
//可以通过get方法的返回值来判断一个键是否存在。通过返回null来判断。

//获取map集合中所有的值。
Collection<String> coll = map.values();

System.out.println(coll);
System.out.println(map);
}
}

map集合的两种取出方式:
①Set keySet:将map中所有的键存入到Set集合。因为set具备迭代器。
所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。
Map集合的取出原理:将map集合转成set集合。在通过迭代器取出。

②Set<> entrySet:将map集合中的映射关系存入到了set集合中,
而这个关系的数据类型就是:Map.Entry
Entry其实就是Map中的一个static内部接口。

注意:为什么要定义在内部呢?
因为只有有了Map集合,有了键值对,才会有键值的映射关系。
关系属于Map集合中的一个内部事物。而且该事物在直接访问Map集合中的元素。

例子程序:

import java.util.*;


class MapDemo2
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();

map.put("02","zhangsan2");
map.put("03","zhangsan3");
map.put("01","zhangsan1");
map.put("04","zhangsan4");
//利用第二种方式,entrySet();
//将Map集合中的映射关系取出。存入到Set集合中。
Set<Map.Entry<String,String>> entrySet = map.entrySet();

Iterator<Map.Entry<String,String>> it = entrySet.iterator();

while(it.hasNext())
{
Map.Entry<String,String> me = it.next();
String key = me.getKey();
String value = me.getValue();

System.out.println(key+":"+value);

}

/*//第一种方法:利用keySet();
//先获取map集合的所有键的Set集合,keySet();
Set<String> keySet = map.keySet();

//有了Set集合。就可以获取其迭代器。
Iterator<String> it = keySet.iterator();

while(it.hasNext())
{
String key = it.next();
//有了键可以通过map集合的get方法获取其对应的值。
String value = map.get(key);
System.out.println("key:"+key+",value:"+value);
}
*/
}
}

Map的练习题:训练两种获取map集合元素的方法

package cn.dhjJH;

import java.util.*;

/*每一个学生都有对应的归属地。
学生Student,地址String。
学生属性:姓名,年龄。
注意:姓名和年龄相同的视为同一个学生。
保证学生的唯一性。
步骤:
1,描述学生。

2,定义map容器。将学生作为键,地址作为值。存入。

3,获取map集合中的元素。
*/

public class Test05 {
public static void main (String[] args){
HashMap<Student,String> hm = new HashMap<Student,String>();
hm.put(new Student("zhang1",22), "shanghai");
hm.put(new Student("zhang2",25), "wuhan");
hm.put(new Student("zhang3",26), "changsha");
hm.put(new Student("zhang4",21), "zhuzhou");
//第一种获取map集合中元素的方法,通过keySet方法
Set<Student> keySet = hm.keySet();
Iterator<Student> it = keySet.iterator();
while(it.hasNext()){
Student stu = it.next();
String adr = hm.get(stu);
System.out.println(stu.getName()+"..."+stu.getAge()+"..."+adr);
}

//第二种获取map集合元素的方法,通过entrySet方法
Set<Map.Entry<Student,String>> entrySet = hm.entrySet();
Iterator<Map.Entry<Student,String>> its = entrySet.iterator();
while(its.hasNext()){
//用Map.Entry键值关系来接收,迭代器后的元素
Map.Entry<Student, String> me = its.next();
Student st = me.getKey();
String adrrs = me.getValue();
System.out.println(st.getName()+"..."+st.getAge()+"..."+adrrs);
}
}

}

class Student implements Comparable<Student>{
private String name;
private int age;

public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
//获取name和age的方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写了排序的方法,提供treeSet的集合排序方法,使Student类具有比较性
public int compareTo(Student s){
int sum = new Integer(this.age).compareTo(new Integer(s.age));
if(sum==0)
return this.getName().compareTo(s.getName());
return sum;
}
//重写hashCode和equals方法,当使用哈希表的数据结构式,保证元素不会重复。
public int hashCode(){
return name.hashCode()+age*39;
}
public String toString(){
return name+":"+age;
}
public boolean equals(Object obj){
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name)&&this.age==s.age;
}
}

上一个例子改变:通过treeMap排序:

//Student的外部添加一个比较器
class StuNameComparator implements Comparator<Student>
{

public int compare(Student s1,Student s2)
{
int num = s1.getName().compareTo(s2.getName());
if(num==0)
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));

return num;
}
}

class MapTest2
{

public static void main(String[] args)
{
//通过TreeMap进行元素的排序
TreeMap<Student,String> tm = new TreeMap<Student,String>(new StuNameComparator());

tm.put(new Student("blisi3",23),"nanjing");
tm.put(new Student("lisi1",21),"beijing");
tm.put(new Student("alisi4",24),"wuhan");
tm.put(new Student("lisi1",21),"tianjin");
tm.put(new Student("lisi2",22),"shanghai");

//使用Map类的entrySet方法获取Map容器中的元素
Set<Map.Entry<Student,String>> entrySet = tm.entrySet();

Iterator<Map.Entry<Student,String>> it = entrySet.iterator();

while(it.hasNext())
{
Map.Entry<Student,String> me = it.next();

Student stu = me.getKey();
String addr = me.getValue();
System.out.println(stu+":::"+addr);
}
}
}

练习3:

/*
练习:
"sdfgzxcvasdfxcvdf"获取该字符串中的字母出现的次数。

希望打印结果:a(1)c(2).....

通过结果发现,每一个字母都有对应的次数。
说明字母和次数之间都有映射关系。

注意了,当发现有映射关系时,可以选择map集合。
因为map集合中存放就是映射关系。


什么使用map集合呢?
当数据之间存在这映射关系时,就要先想map集合。

思路:
1,将字符串转换成字符数组。因为要对每一个字母进行操作。

2,定义一个map集合,因为打印结果的字母有顺序,所以使用treemap集合。

3,遍历字符数组。
将每一个字母作为键去查map集合。
如果返回null,将该字母和1存入到map集合中。
如果返回不是null,说明该字母在map集合已经存在并有对应次数。
那么就获取该次数并进行自增。,然后将该字母和自增后的次数存入到map集合中。覆盖调用原理键所对应的值。

4,将map集合中的数据变成指定的字符串形式返回。



*/

import java.util.*;
class MapTest3
{
public static void main(String[] args)
{
String s= charCount("ak+abAf1c,dCkaAbc-defa");
System.out.println(s);
}
//定义方法
public static String charCount(String str)
{
//把字符串转为数组
char[] chs = str.toCharArray();
//用TreeMap容器来转键值对数据
TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();


int count = 0;
//遍历字符数组
for(int x=0; x<chs.length; x++)
{


if(!(chs[x]>='a' && chs[x]<='z' || chs[x]>='A' && chs[x]<='Z'))
continue;

Integer value = tm.get(chs[x]);


if(value!=null)
count = value;
count++;
tm.put(chs[x],count);//直接往集合中存储字符和数字,为什么可以,因为自动装箱。

count = 0;
/*
if(value==null)
{
tm.put(chs[x],1);
}
else
{
value = value + 1;
tm.put(chs[x],value);
}
*/



}

//System.out.println(tm);
//用缓冲的字符串容器去装元素
StringBuilder sb = new StringBuilder();
//通过entrySet方法,获取TreeMap里面的每一个元素
Set<Map.Entry<Character,Integer>> entrySet = tm.entrySet();
Iterator<Map.Entry<Character,Integer>> it = entrySet.iterator();

while(it.hasNext())
{
Map.Entry<Character,Integer> me = it.next();
Character ch = me.getKey();
Integer value = me.getValue();
sb.append(ch+"("+value+")");
}
return sb.toString();
}

}

Map的扩展应用:
在很多项目中,应用比较多的是一对多的映射关系,这就可以通过嵌套的形式将多个映射定义到一个大的集合中,并将大的集合分级处理,形成一个体系。

/*
map扩展知识。

map集合被使用是因为具备映射关系。

"yureban" Student("01" "zhangsan");

"yureban" Student("02" "lisi");

"jiuyeban" "01" "wangwu";
"jiuyeban" "02" "zhaoliu";

一个学校有多个教室。每一个教室都有名称。


*/
import java.util.*;

class Student
{
private String id;
private String name;
Student(String id,String name)
{
this.id = id;
this.name = name;
}
public String toString()
{
return id+":::"+name;
}
}
class MapDemo3
{
//第一种方法:是通过czbk的hashMap集合,嵌套List集合。
public static void demo()
{
HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();

List<Student> reyu = new ArrayList<Student>();
List<Student> jiuye = new ArrayList<Student>();

czbk.put("yureban",reyu);
czbk.put("jiuyeban",jiuye);

reyu.add(new Student("01","zhagnsa"));
reyu.add(new Student("04","wangwu"));
jiuye.add(new Student("01","zhouqi"));
jiuye.add(new Student("02","zhaoli"));


Iterator<String> it = czbk.keySet().iterator();

while(it.hasNext())
{
String roomName = it.next();
List<Student> room = czbk.get(roomName);

System.out.println(roomName);
getInfos(room);
}




}
public static void getInfos(List<Student> list)
{
Iterator<Student> it = list.iterator();
while(it.hasNext())
{
Student s = it.next();
System.out.println(s);
}
}




public static void main(String[] args)
{
demo();

/*
//第二种方法是HashMap集合中嵌套HashMap集合。
HashMap<String,List<Student>> czbk = new HashMap<String,List<Student>>();

HashMap<String,String> yure = new HashMap<String,String>();

HashMap<String,String> jiuye = new HashMap<String,String>();

czbk.put("yureban",yure);
czbk.put("jiuyeban",jiuye);


yure.put("01","zhagnsan");
yure.put("02","lisi");

jiuye.put("01","zhaoliu");
jiuye.put("02","wangwu");



//遍历czbk集合。获取所有的教室。
Iterator<String> it = czbk.keySet().iterator();

while(it.hasNext())
{
String roomName = it.next();
HashMap<String,String> room = czbk.get(roomName);

System.out.println(roomName);
getStudentInfo(room);
}


// getStudentInfo(jiuye);
// getStudentInfo(yure);
*/

}
public static void getStudentInfo(HashMap<String,String> roomMap)
{
Iterator<String> it = roomMap.keySet().iterator();

while(it.hasNext())
{
String id = it.next();
String name = roomMap.get(id);
System.out.println(id+":"+name);
}
}
}

(三)、collections工具类
1、collections有什么作用?
它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。 在Collections工具类中大部分方法是用于对List集合进行操作的,如比较,二分查找,随机排序等。

2、collections的方法
1、查找
Tmax(Collection coll);//根据集合的自然顺序,获取coll集合中的最大元素
Tmax(Collection coll,Comparator< T> comp);//根据指定比较器comp的顺序,获取coll集合中的最大元素
intbinarySearch(Lsit< Comparable< T>> list,T key);//二分法搜索list集合中的指定对象
2、替换
voidfill( list,obj);//将list集合中的全部元素替换成指定对象obj
booleanreplaceAll(List lsit,T oldVal,T newVal);//用newVal替换集合中的oldVal值
void swap(Listlist,int i,int j);/在指定列表的指定位置处交换元素
3排序:
void shuffle(List list);//使用默认随机源对list集合中的元素进行随机排序
void sort(Lsit list);//根据自然顺序对list集合中的元素进行排序
voidsort(List lsit,Comparator< T> c);//根据指定比较器c的排序方式对list集合进行排序
4、反转
reverse(List list);//反转list集合中元素的顺序
Comparator reverseOrder();//返回一个比较器,强行逆转了实现Comparable接口的对象的自然顺序
ComparatorreverseOrder(Comparator cmp);//返回一个比较器,强行逆转了指定比较器的顺序
5、同步的集合
ListsynchronizedList(List list);//返回支持的同步(线程安全的)List集合

(四)用于操作数组的工具类Arrays
1、Arrays的特点
Arrays是用于操作数组的工具类。里边的方法也全是静态的。不需要创建对象。

2、Arrays类中的主要方法:
1、Lsit asList(T… a);//将数组转换为集合
注意:
a、将数组转换成集合,不可使用集合的增删方法,因为数组的长度是固定的。如果进行增删操作,则会产生UnsupportedOperationException的编译异常。
b、如果数组中的元素都是对象,则变成集合时,数组中的元素就直接转为集合中的元素。
c、如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。
2、binarySearch():二分查找方法,fill():替换方法,sort():排序方法等
特点:可对数组元素进行相应操作,可以接受除boolean之外的其他各种基本数据类型及有序的引用类型数组的参数,且还可以对指定元素的范围,并可根据指定比较器进行相应的操作。
如:sort(T[]a,Comparator< T> c)
fill(int[]a,int from,int to)等
3、String toString();//可以接收各种数组类型参数,并返回指定数组内容的字符串表现形式。
4、sort()排序方法:Arrays.sort();

例子小程序片段:

String[] arr = {"abc","cc","kkkk"};
List<String> list = Arrays.asList(arr);
sop("contains:"+list.contains("cc"));

(五)集合变数组
Collection接口中的toArray方法
例子程序:

import java.util.*;
class CollectionToArray
{
public static void main(String[] args)
{
ArrayList<String> al = new ArrayList<String>();

al.add("abc1");
al.add("abc2");
al.add("abc3");

/*
1,指定类型的数组到底要定义多长呢?
当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组。长度为集合的size。
当指定类型的数组长度大于了集合的size,就不会新创建了数组。而是使用传递进来的数组。
所以创建一个刚刚好的数组最优。
2,为什么要将集合变数组?
为了限定对元素的操作。不需要进行增删了。

*/

String[] arr = al.toArray(new String[al.size()]);
System.out.println(Arrays.toString(arr));
}
}

(六)增强For循环
它的格式是:
for(类型 变量名 : 要遍历的集合或数组){
}

注意:
对集合进行遍历。
只能获取集合元素。但是不能对集合进行操作。

迭代器除了遍历,还可以进行remove集合中元素的动作。
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。

传统for和高级for有什么区别呢?
高级for有一个局限性。必须有被遍历的目标。
建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。

例子程序:

ArrayList<String> al = new ArrayList<String>();
al.add("abc1");
al.add("abc2");
al.add("abc3");
for(String s : al)
{
System.out.println(s);
}

(六)、方法的可变参数
①如果一个方法在参数列表中传入多个参数,个数不确定,那么每次都要复写该方法。这时可以用数组作为形式参数。但是在传入时,每次都需要定义一个数组对象,作为实际参数。在JDK1.5版本后,就提供了一个新特性:可变参数。
②可变参数其实就是数组参数的简写形式。不用每一次都手动的建立数组对象。只要将要操作的元素作为参数传递即可。隐式将这些参数封装成了数组。
③在使用时注意:可变参数一定要定义在参数列表的最后面。
例子程序:

   public static void main(String[] args)   
{
show("haha",2,3,4,5,6);
}
public static void show(String str,int... arr)//...就表示可变参数
{
System.out.println(arr.length);
}

(七)、静态导入
①写法:
import staticjava.util.Arrays.*;//导入的是Arrays这个类中的所以静态成员。
import staticjava.lang.System.*//导入了Ssytem类中所以静态成员。
没加static导入的是类,加上static导入的全是某一个类中所以的静态成员。这样写在调用该类的静态方法时可以不用再写类名。如:Arrays.sort(数组);就可以直接写sort(数组);
②注意:
当导入的两个类中有同名成员时,需要在成员前加上相应的类名。
当类名重名时,需要指定具体的包名。当方法重名时,指定具体所属的对象或者类。