|
第十一章 容器类和泛型编程
一、泛型编程简介和泛型编程思想
模板:类中的每一个数据成员或变量都有具体的类型,而模板中主要数据成员或变量都没有指定的数据类型,这些数据成员或变量在定义时是通过一个代表类型的符号来进行的。在使用模板时,需要使用具体的的类型来代替模板定义中的那些类型代表符号。虽说在编制代码时可以不指定具体的类型,但是这并不意味着可以用用任意类型替换模板中的类型占位符,只有那些适用的类型才可以替换模板中的占位符。
虽然说利用多态也可以实现泛型编程的效果(例如:A类是B和C类的父类,那么将一个方法的参数设定为A类时,B和C类也可以作为该方法的参数值输入)但是,在读取对象的时候需要强制进行对该类进行类型转换,但是转换的类型不正确的话Java无法检测出来,容易出错,所以用泛型更好。(我觉得不好的原因是如果子类扩充了,扩充了的部分是无法在赋值给父类参数后还可以方便调用的,这样需要修改的部分就多了很多,所以很麻烦)
泛型编程并不支持直接产生泛型数组的语法,所以只能咋泛型类之外先产生一个数组实例,然后再将该实例引用传入泛型类进行使用。
Java允许泛型类的设计者指定在对泛型类进行实例化时允许接受的类型,这种机制是通过extends语法实现的。例如
class PersonArray<T extends Person>
{}//这意味着采用上述格式定义的泛型类在产生实例时给出的数据类型必须只能是Person类或者它的子类,否则报错
二、List
List提供列表功能,可以添加一系列元素到List之中,List中的元素允许重复和null值。
1. List 接口
List接口扩展于Collection,接口Collection扩展于Iterable.
接口Iterable要求实现一个名为iterator()的迭代器(即Iterator对象),通过迭代器可以对集合中的元素进行遍历,和移除。
接口Collection表示一组对象,只能间接实现其对象(?),可能允许或者不允许向Collection对象中添加重复元素,Collection对象里面的元素可能有序或无序。
List可以按照用户添加对象的顺序或者按用户指定的顺序保存对象。在去用这些对象时,可以通过对象的索引直接进行访问。由于其实际上是一个接口,功能的真正实现是由Vector\ArrayList\LinkedList完成的。
2.ArrayList
ArraryList是以数组方式实现List类的接口的类,因此有一些数组的特性随机访问快,插入删除慢,但提供比数组对象更多的操作,其数组大小会随着存储在ArraryList中的元素的个数增加而变化。数组大小不一定在每次为ArraryList增加元素时都会变化,数组增长策略的细节在Java中没有明确要求。产生ArraryList实例时可以通过构造器初始化数组的大小(明确定义或者采用没有参数的构造器建立默认大小的数组)
实现方法 | 功能 |
add addAll | 添加元素 |
remove, removeRang, clear | 移出元素 |
isEmpty | 判断列表是否为空 |
contains | 识别表中是否包含特定元素 |
import java.util.ArrayList;
class MyStack<T>
{
private ArrayList<T> al = new ArrayList<T>();
public boolean push(T e)
{
return al.add(e);
}
public T pop()
{
if(al.size()>0)
return al.remove(al.size()-1);
else
return null;
}
public int size()
{
return al.size();
}
}
public class TeatArrayList {
public static void main(String[] args)
{
MyStack<String> ms = new MyStack<String>();
ms.push("TOM~");
ms.push("Marry");
ms.push("jack");
String element;
while((element = ms.pop())!=null)
{
System.out.println(element);
}
}
}
3.LinkedList
.LinkedList是以链表方式来实现的List。
链表中的每一个对象是由两部分组成的,一部分是数据存储区域,一部分是存储链表下一个对象的存储位置的地址。删除插入操作效率高,随机访问效率低。
方法 | 功能 |
addFirst | 将一个元素添加到链表的开始位置 |
addLast | 将一个元素添加到链表的结束位置 |
getFirst | 获得链表开始位置的元素 |
getLast | 获得链表结束位置的元素 |
三、Set
保存和处理不允许重复值的集合,最多只有一个null
1.Set接口
Set的父类接口包括前面说的Collection和Iterable.
子类接口包括NavigableSet和SortedSet, Set接口的实现类常用的主要是HashSet,TreeSet
方法 | 功能 |
add |
|
remove |
|
clear |
|
contains |
|
iterator |
|
2.HashSet
HashSet是以哈希表方式实现Set接口。
哈希表特征:加入到HashSet的对象会在被计算哈希值后存储于特定的位置(输出顺序和加入顺序不一致),可以提升查询性能。哈希码和输入值不是一一对应关系
哈希运用:数据存储,加密。
import java.util.*;
public class TestHashSet {
public static void main(String[] args)
{
HashSet<String> hs = new HashSet<String>();
hs.add("Emiily");
hs.add("Austin");
hs.add("Jobs");
hs.add("Jobs");//虽然有重复加入但是不会报错,但是从输出来看只会存储一个
System.out.println("Marry in present set:"+hs.contains("Marry"));
System.out.println("Jobs in present set:"+hs.contains("Jobs"));
Iterator<String> it = hs.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
3.TreeSet
更注重包含元素间的大小关系。根据需要,会做一些旋转平衡的操作,底层是用红黑树实现
方法 | 功能 |
add |
|
first | 取最小值 |
last | 取最大值 |
ceiling | 查找大于等于某值(返回大于等于某值中最接近某值的那个) |
floor | 查找小于等于某值 |
import java.util.*;
public class TestTreeSet {
public static void main(String[] args)
{
TreeSet<String> ts = new TreeSet<String>();
ts.add("D");
ts.add("c");
ts.add("E");
ts.add("A");
ts.add("f");
System.out.println("the max is :"+ ts.last());
System.out.println("the min is :"+ ts.first());
System.out.println("the character bigger than D :"+ ts.ceiling("C"));//返回D
System.out.println("the character smaller than D :"+ ts.floor("C"));//返回A
System.out.println("o in it?"+ts.contains("o"));
System.out.println("A in it?"+ts.contains("A"));
Iterator<String> it = ts.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
四、Map
Map是由一系列元素对组成,一个元素是希望保存的元素,一个是可以唯一标识这个元素的值--键。例如学号可以作为标识某个学生的键。
1.map接口
方法 | 功能 |
put(键,值) |
|
get(键) |
|
remove |
|
keySet() | 返回该map包含的键组成的Set |
2. HashMap
import java.util.*;
class Word
{
private String enWord;
private LinkedList<String> chParaphrase = new LinkedList<String>();
public Word(String enWord)
{
this.enWord = enWord;
}
public String getWord()
{
return enWord;
}
public void setParaphrase(String paraphrase)
{
chParaphrase.add(paraphrase);
}
public String getParaphrase()
{
String retParaphrase = "";
Iterator<String> it =chParaphrase.iterator();
while(it.hasNext())
{
retParaphrase=retParaphrase+it.next()+" ";
}
return retParaphrase;
}
}
class EnDict
{
Word[] word = new Word[5];
HashMap<String, Word> hm = new HashMap<String, Word>();
public EnDict()
{
word[0] = new Word("good");
word[0].setParaphrase("n.善行;好处");
word[0].setParaphrase("彻底的; 完全的; 着实的:");
word[0].setParaphrase("有趣的; 好玩的:");
word[1] = new Word("bad");
word[1].setParaphrase("a.坏的");
word[2] = new Word("one");
word[2].setParaphrase("n.一");
word[2].setParaphrase("int.任何人");
word[2].setParaphrase("a.一个");
word[3] = new Word("two");
word[3].setParaphrase("num.二");
word[4] = new Word("three");
word[4].setParaphrase("num.三");
word[4].setParaphrase("pron.&a.三(个、只)");
for(int i = 0; i<word.length; i++)
{
hm.put(word[i].getWord().toLowerCase(), word[i]);
}
}
public String queryWord(String enWord)
{
Word word = hm.get(enWord.toLowerCase());
String chParaphrase;
if(word!=null)
{
return word.getParaphrase();
}
else
{
return "no such word";
}
}
}
public class TestHashMap {
public static void main(String[] args)
{
EnDict ed = new EnDict();
String enWord;
while(true)
{
enWord = new Scanner(System.in).next();
if(enWord.equals("-1"))
{
break;
}
System.out.println(ed.queryWord(enWord));
}
}
}
3. treeMap
底层采用红黑树实现,使得存储其中的数据可以以有序的方式访问。
方法 | 功能 |
lowerEntry | 可以挑选出严格小于指定键的最大键的“键-值”对 |
highEntry | 可以挑选出严格大于指定键的最小键的“键-值”对 |
import java.util.*;
public class TestTreeMap {
public static void main(String[] args)
{
System.out.println("== HashMap ==");
HashMap<String,String> hm = new HashMap<String,String>();
hm.put("1003", "zhangsan");
hm.put("1001", "wang wu");
hm.put("1002", "li si");
Iterator<String> it = hm.keySet().iterator();
while(it.hasNext())
{
Object key=it.next();//这里是什么意思?
System.out.println("键:"+key+",值:"+hm.get(key));
}
System.out.println();
System.out.println("== TreeMap ==");
TreeMap<String,String> tm = new TreeMap<String,String>();
tm.put("1003", "zhangsan");
tm.put("1001", "wang wu");
tm.put("1002", "li si");
Iterator<String> it1 = tm.keySet().iterator();
while(it1.hasNext())
{
Object key=it1.next();//这里是什么意思?
System.out.println("键:"+key+",值:"+tm.get(key));
}
}
}
import java.util.*;
public class TestTreeMap1 {
public static void main(String[] args)
{
TreeMap<String,String> tm = new TreeMap<String,String>();
tm.put("1003", "zhangsan");
tm.put("1001", "wang wu");
tm.put("1002", "li si");
tm.put("1004", "li SHI YON");
SortedMap<String,String> subMap = tm.subMap("1002","1004");//将子类对象赋值给父类变量
Iterator it = subMap.keySet().iterator();
while(it.hasNext())
{
Object key = it.next();
System.out.println("键:"+key+",值"+subMap.get(key));
}
}
}
SortedMap是一个扩展的Map的接口,TreeMap是它的一个实现类;TreeMap的subMap方法返回的结果中,键值大于等于它的第一个参数,而小于第二个参数
习题
1.使用List分别存储10个字符串、10个整数,并将它们在标准输出设备上输出
HashMap<int,String> map=new HashMap<int,String>();
map.put(1,"a");
map.put(2,"b");
System.out.println(map.get(0));
System.out.println(map.get(1));
报错:Syntax error on token "int", Dimensions expected after this token
原因:引用类型和原始类型没有搞清楚!
Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。
原始类型 封装类
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。
HashMap<Integer,String> map=new HashMap<Integer,String>();
map.put(1,"a");
map.put(2,"b");
System.out.println(map.get(0));
System.out.println(map.get(1));
本题代码:
package unite_11;
import java.util.*;
public class Question_1 {
public static void main(String[] args)
{
LinkedList<String> lls = new LinkedList<String>();
LinkedList< Integer> lli = new LinkedList< Integer>();//本来写的是LinkedList< int> java报错Syntax error on token "int", Dimensions expected after this token
String enWord;
for(int i = 0; i<10;i++)
{
enWord = new Scanner(System.in).next();
lls.add(enWord);
}
Iterator<String> it1 = lls.iterator();
while(it1.hasNext())
{
System.out.println(it1.next());
}
int j;
for(int i = 0; i<10;i++)
{
j = new Scanner(System.in).nextInt();
lli.add(j);
//System.out.println(j);
}
Iterator<Integer> it2 = lli.iterator();
while(it2.hasNext())//首先这里it2之前错写成了it1输不出来
{
System.out.println(it2.next());
}
}
}
2.请编写一个学生类,然后将该学生类的一系列实例存储于某种容器之中,要求在该容器中每名学生只能被存储一次
import java.util.*;
class Student
{
String number;
String name;
int age;
public void Set(String number,String name,int age)
{
this.number = number;
this.age = age;
this.name = name;
}
public String Get( )
{
String str = "学号:"+this.number+"名字:"+this.name+" 年龄:"+this.age;
return str;
}
}
public class Question_2 {
public static void main(String[] args)
{
int num;
String number;
String name;
int age;
String stu;
System.out.println("输入需要输入学生个数:");
num = new Scanner(System.in).nextInt();
HashSet<String> hs = new HashSet<String>();
Student sd = new Student();
for(int i=0; i<num; i++)
{
System.out.println("输入编号");
number = new Scanner(System.in).next();
System.out.println("输入名称");
name = new Scanner(System.in).next();
System.out.println("输入年龄");
age = new Scanner(System.in).nextInt();
sd.Set(number, name, age);
stu=sd.Get();
hs.add(stu);
}
Iterator<String> it = hs.iterator();
while(it.hasNext())
{
System.out.println("输出学生:");
System.out.println(it.next());
}
}
}
不使用转变了的数组
import java.util.*;
class Student
{
String number;
String name;
int age;
public void Set(String number,String name,int age)
{
this.number = number;
this.age = age;
this.name = name;
}
public String Get( )
{
String str = "学号:"+this.number+"名字:"+this.name+" 年龄:"+this.age;
return str;
}
}
public class Question_2 {
public static void main(String[] args)
{
int num;
String number;
String name;
int age;
System.out.println("输入需要输入学生个数:");
num = new Scanner(System.in).nextInt();
HashSet<Student> hs = new HashSet<Student>();
Student sd = new Student();
for(int i=0; i<num; i++)
{
System.out.println("输入编号");
number = new Scanner(System.in).next();
System.out.println("输入名称");
name = new Scanner(System.in).next();
System.out.println("输入年龄");
age = new Scanner(System.in).nextInt();
sd.Set(number, name, age);
hs.add(sd);
}
Iterator<Student> it = hs.iterator();
while(it.hasNext())
{
Student st = it.next();//it.next()返回值是对象的引用,返回后马上指向下个对象
System.out.println("输出学生:");
System.out.println("输出年龄:"+it.next().age);//这里如果调用了一次,指针就会指向下一个对象,后面那一行就输不出我想要的这个对象的名称,所以如果希望顺利输出这个对象的所有属性,那么就要添上这个while循环里面的第一行 然后用st来调用属性
System.out.println("输出名字"+it.next().name);
System.out.println("输出编号"+it.next().number);
}
}
}
小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)
GMT+8, 2024-5-17 10:06
Powered by Discuz! X3.4
© 2001-2023 Discuz! Team.