集合框架
集合类:
面向对象编程中会出现很多的对象,为了方便这些对象的存储,便出现了集合框架。集合框架可以存储和操作多个对象,而它和数组的不同是在于,数组只能存储同一类型的对象,而集合可以存储不同类型的对象,而且集合的长度可以是任意的。
集合框架中有很多中容器,用来存储对象。为什么会出现这么多容器呢?是因为每一个容器对对象的存储方式都不同,这种存储方式称为数据结构。
Collection类:
Collection
|--List//元素是有序的,元素可以重复。因为该集合体系有索引。
|--Set//元素是无序的,元素不可以重复。
|--List//元素是有序的,元素可以重复。因为该集合体系有索引。
|--Set//元素是无序的,元素不可以重复。
Collection类中常见方法:
添加元素
add(Object obj);
删除元素
remove(Object obj);
removeAll(集合);//删除参数集合中有的元素
clear();//清空集合
判断元素
contains(Object obj);//是否包含参数
isEmpty();//判空
获取集合长度
size();
取交集
retainAll(集合);//取和参数集合的交集
add(Object obj);
删除元素
remove(Object obj);
removeAll(集合);//删除参数集合中有的元素
clear();//清空集合
判断元素
contains(Object obj);//是否包含参数
isEmpty();//判空
获取集合长度
size();
取交集
retainAll(集合);//取和参数集合的交集
迭代器:
迭代器就是用来从集合中取出元素的方式。而迭代器被定义成内部类的形式,是因为Collection中有很多不同的子容器,这些子容器的数据结构不同,对元素的取出方式也不一样,但对外都提供了Iterator()方法进行取出操作。
迭代器的常见方法:
hasNext();//判断是否有下一个元素
next();//取出下一个元素
remove();//移除元素
next();//取出下一个元素
remove();//移除元素
遍历取出元素时,用循环判断hasNext()进行取出。推荐使用for循环,因为for循环中的迭代器是局部变量,循环后就会在内存中清除,写法如下:
for(Iterator it = a.iterator();it.hasNext(); )
{
System.out.println(it.next());
}
List类:
元素是有序的,元素可以重复。因为该集合体系有索引。
list类的特有方法:list类方法的特点是对索引进行操作。
增
add(index,element);//在指定位置添加元素
addAll(index,Collection);//在指定位置增加给定集合中的元素
删
remove(index);//删除指定位置的元素
改
set(index,element);//修改指定位置的元素。
查
get(index);//通过索引获取元素
subList(from,to);//获取子集的对象元素
add(index,element);//在指定位置添加元素
addAll(index,Collection);//在指定位置增加给定集合中的元素
删
remove(index);//删除指定位置的元素
改
set(index,element);//修改指定位置的元素。
查
get(index);//通过索引获取元素
subList(from,to);//获取子集的对象元素
List的三个子类
1. ArrayList
2.LinkedList
3.Vector
List集合判断元素是否相同,移除等操作,依据的是元素的equals方法。
ArrayList:
对应的数据结构是顺序表,查询速度快,增删速度慢。线程不同步。
Vector:
和ArrayList数据结构相同,因线程同步被ArrayList取代。
LinkedList:
对应的数据结构是链表,增删速度快,查询速度慢。
ListIterator:
它定义了针对于List数据结构的特有方法:
add(obj); //增加
set(obj); //修改为obj
hasPrevious(); //判断前面是否有元素
set(obj); //修改为obj
hasPrevious(); //判断前面是否有元素
Enumeration:
相当于Vector的迭代器。
特有方法:
addElement(obj);//相当于add(obj);
Enumerationelements();//Vector的取出元素方式
hasMoreElements();//相当于Iterator的hasNext()方法
nextElements();//相当于Iterator的next()方法
addElement(obj);//相当于add(obj);
Enumerationelements();//Vector的取出元素方式
hasMoreElements();//相当于Iterator的hasNext()方法
nextElements();//相当于Iterator的next()方法
LinkedList:
特有方法:因为链表增删操作繁琐,所以只有从头和从尾的增删方法。
增
addFirst();
addLast();
查
getFirst();
getLast();
删
removeFirst();
removeLast();
addFirst();
addLast();
查
getFirst();
getLast();
删
removeFirst();
removeLast();
查找和删除方法都会返回当前元素,如果没有获取到元素,则抛出NoSuchElementException。
在JDK1.6以后,出现了替代方法。
增
offFirst();
offLast();
获取
peekFirst();
peekLast();
删
pollFirst();
pollLast();
在JDK1.6以后,出现了替代方法。
增
offFirst();
offLast();
获取
peekFirst();
peekLast();
删
pollFirst();
pollLast();
同上,查找和删除方法都会返回当前元素,如果没有获取到元素,则返回null。
下面一个List集合的练习:
首先是LinkedList练习,用LinkedList实现队列和栈的存取操作。代码如下:
class MyQueue//队列结构元素存取顺序是FIFO(先进先出)
{
private LinkedList link;
public MyQueue()
{
link = new LinkedList();
}
public void push(Object obj)
{
link.add(obj);
}
public Object pop()
{
return link.removeLast();
}
public boolean isNull()
{
return link.isEmpty();
}
}
class MyStack//栈结构存取顺序是FILO(先进后出)
{
private LinkedList link;
public MyStack()
{
link = new LinkedList();
}
public void push(Object obj)
{
link.add(obj);
}
public Object pop()
{
return link.removeFirst();
}
public boolean isNull()
{
return link.isEmpty();
}
}
Set类
在set集合中,元素是无序的,元素不可以重复。
常用的子类有两个:
HashSet,
TreeSet。
HashSet:
基于哈希表的Set集合,首先用hashcode()函数保证元素的唯一性, 如果hashcode的结果相同,则用equals()比较元素是否相同。
下面是HashSet存储自定义元素的程序示例:
import java.util.*;
class Person//人类描述,存储的元素
{
private String name;
private int age;
Person(String name,int age)
{
this.name=name;
this.age=age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p=(Person)obj;
return this.name.equals(p.name)&&this.age==p.age;
}
public int hashCode()
{
return this.name.hashCode()+this.age;
}
}
class HashSetTest
{
public static void main(String[] args)
{
HashSet h=new HashSet();
h.add(new Person("shenm",10));
h.add(new Person("shenm2",6));
h.add(new Person("shenm1",30));
h.add(new Person("shenm0",10));
h.add(new Person("shenm0",10));
for (Iterator it=h.iterator(); it.hasNext(); )//取出元素
{
Person p=(Person)it.next();
print(p.getName()+"..."+p.getAge());
}
}
public static void print(Object obj)
{
System.out.println(obj);
}
}
TreeSet:
基于的数据结构是二叉树,每次存入元素时对元素进行比较。比较的方法有二,一种是让元素自身具备比较性——就是实现Comparable接口,并覆盖CompareTo(Object obj)方法。第二种是自定义一个比较器类,实现Comparator接口,并覆盖Compare(Object o1, Object o2)方法。这两个函数的返回值是正数、负数、零,分别对应元素的比较结果为——大于、小于、等于。
下面给出两种比较方法的代码示例:
1、实现Comparable接口
class Person implements Comparable
{
private String name;
private int age;
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return this.name;
}
public int getAge()
{
return this.age;
}
@Override
public int compareTo(Object obj)
{
if(!(obj instanceof Person))
throw new RuntimeException("not a Person");
Person p = (Person)obj;
System.out.println(this.name + " compare to " + p.name);
if(this.age > p.age)
return 1 ;
if(this.age == p.age)
return this.name.compareTo(p.name);
return -1;
}
}
class TreeSetTest
{
public static void main(String args[])
{
TreeSet<Person> tr = new TreeSet<Person>();
tr.add(new Person("zhangsan1", 11));
tr.add(new Person("zhangsan2", 12));
tr.add(new Person("zhangsan3", 13));
tr.add(new Person("zhangsan4", 15));
tr.add(new Person("zhangsan6", 19));
tr.add(new Person("zhangsan5", 19));
Iterator it = tr.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
print(p.getName() + "---" + p.getAge());
}
}
public static void print(Object obj)
{
System.out.println(obj);
}
}
2、自定义比较器实现Comparator接口
import java.util.*;
class Person
{
private String name;
private int age;
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return this.name;
}
public int getAge()
{
return this.age;
}
}
class TreeSetTest
{
public static void main(String args[])
{
TreeSet<Person> tr = new TreeSet<Person>(new PersonComparator());
tr.add(new Person("zhangsan1", 11));
tr.add(new Person("zhangsan2", 12));
tr.add(new Person("zhangsan3", 13));
tr.add(new Person("zhangsan4", 15));
tr.add(new Person("zhangsan6", 19));
tr.add(new Person("zhangsan5", 19));
Iterator<Person> it = tr.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
print(p.getName() + "---" + p.getAge());
}
}
public static void print(Object obj)
{
System.out.println(obj);
}
}
class PersonComparator implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
int num=new Integer(p1.getName().length()).compareTo(new Integer(p2.getName().length()));
if (num==0)
{
return new Integer(p1.getAge()).compareTo(p2.getAge());
}
return num;
}
}
Map类
Map类存放的元素是键值对,同时要保证键的唯一性。
Map类常见的子类有:
Hashtable
HashMap
TreeMap
Hashtable和HashMap都是基于哈希表的集合,HashMap较新,代替了Hashtable,允许存储null值和null键,线程不同步。
HashMap
TreeMap
Hashtable和HashMap都是基于哈希表的集合,HashMap较新,代替了Hashtable,允许存储null值和null键,线程不同步。
TreeMap是基于二叉树的集合。
Map类特有方法:
增:
put(K key,V value ) //添加元素
putAll(Map<? extends K , ? extends V> m ) //添加一组元素
删:
remove(Object key) //删除指定键值对
clear() //清空容器
查:
containsKey(Object key) //是否包含指定键
containsValue(Object value) //是否包含指定值
isEmpty() //判空
获取:
get(Object key) //通过键值获取对应的值
重要的是下面两个取出方法:
entrySet() //返回一个元素类型是Map.Entry<K, V>的Set集合,包含Map所有的键值对。
keySet() //返回一个包含map所以键值的Set集合。
在进行有关集合的练习时,应注意养成将元素都实现Comparable接口和自定义比较器的习惯。
下面是Map集合取出键值对的两种方式:
1、通过entrySet()方法直接返回包含键值对的Set集合:
代码如下:
示例Map的元素是自定义的学生类
import java.util.*;
class Student implements Comparable<Student>
{
private String name;
private int age;
Student(String name, int age)
{
this.name = name;
this.age = age;
}
@Override
public int hashCode()
{
return name.hashCode() + age * 34;
}
@Override
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
return false;
//throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.age == s.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;
}
@Override
public String toString()
{
return "Student [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(Student s)
{
int num = new Integer(this.age).compareTo(new Integer(s.age));
if(num == 0)
return this.name.compareTo(s.name);
return num;
}
}
class MapTest2
{
public static void main(String[] args)
{
TreeMap<Student, String> tm = new TreeMap<Student, String>(new StuNameComparator());
tm.put(new Student("lisi03", 24), "武汉");
tm.put(new Student("lisi01", 22), "北京");
//tm.put(new Student("lisi01", 21), "天津");
tm.put(new Student("lisi04", 23), "南京");
tm.put(new Student("lisi02", 21), "上海");
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 s = me.getKey();
String addr = me.getValue();
print(s + "--" + addr);
}
}
public static void print(Object obj)
{
System.out.println(obj);
}
}
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 MapTest
{
public static void main(String[] args)
{
HashMap<Student, String> hm = new HashMap<Student, String>();
hm.put(new Student("lisi01", 21), "北京");
hm.put(new Student("lisi01", 21), "天津");
hm.put(new Student("lisi02", 22), "上海");
hm.put(new Student("lisi03", 23), "武汉");
hm.put(new Student("lisi04", 24), "南京");
Iterator<Student> it1 = hm.keySet().iterator();
while(it1.hasNext())
{
Student s = it1.next();
String addr = hm.get(s);
print(s + ".." + addr);
}
}
public static void print(Object obj)
{
System.out.println(obj);
}
}
Map扩展知识:
Map的映射关系不仅可以建立在单个元素之间,也可以建立在集合与元素,集合与集合之间,这样就形成了映射关系的嵌套。
比如传智播客体系,可以分为预热班,就业班,每个班中又有学生元素,学生可以有学号和姓名的映射关系。
给出代码示例:
import java.util.*;
class MapExpandKnow
{
public static void main(String[] args)
{
//预热班
HashMap<String,String> yureban=new HashMap<String,String>();
//就业班
HashMap<String,String> jiuyeban=new HashMap<String,String>();
//传智播客
HashMap<String,HashMap<String,String>> czbk=new HashMap<String,HashMap<String,String>>();
czbk.put("yureban",yureban);
czbk.put("jiuyueban",jiuyeban);
//预热班级中学号与姓名的映射
yureban.put("01","zhangsan");
yureban.put("02","lisi");
//就业班级中学号与姓名的映射
jiuyeban.put("01","wangwu");
jiuyeban.put("02","zhouqi");
//直接显示全部学生信息
getInfo(czbk);
}
//定义一个方法获取全部学生信息,包括在哪个班级,叫什么名字,学号多少
public static void getInfo(HashMap<String ,HashMap<String,String>> hm)
{
for (Iterator<String> it=hm.keySet().iterator();it.hasNext() ; )
{
String s= it.next();
System.out.println(s+":");
HashMap<String,String> stu=hm.get(s);
getStuInfo(stu);
}
}
//获取班级中学生的信息,包括姓名和学号
public static void getStuInfo(HashMap<String,String> hm)
{
for (Iterator<String> it=hm.keySet().iterator();it.hasNext() ; )
{
String key=it.next();//学号
String value=hm.get(key);//姓名
System.out.println(key+"..."+value);
}
}
}
泛型
泛型是JDK1.5版本以后出现的新特性。用于解决安全问题,是一个类型安全机制。泛型要求在定义集合时,明确你要在集合中操作何种类型的数据,格式是通过<>来定义要操作的引用数据类型,如ArrayList<String>。这样就可以将运行时期出现的问题ClassCastException,转移到编译时期。方便于程序员解决问题。让运行时期问题减少、安全。同时也避免了强制转换的麻烦。
当传入的类型不确定时,可以使用通配符?,也称为占位符。使用通配符的好处是可以不用明确传入的类型,这样在使用泛型类或者泛型方法时,提高了扩展性。
泛型限定:对泛型的上下限进行限定,明确泛型的范围。
<? extends E>:可接收E类型或E类型的子类型,称之为上限。
<? super E>:可接收E类型或E类型的父类型,称之为下限。
泛型类:当类中要操作的引用数据类型不确定的时候,早期定义Object来完成扩展。现在定义泛型来完成扩展。
泛型方法:不同方法如果要操作不同类型的参数,而且类型还不确定,那么可以将泛型定义在方法上。
格式如下:
class Test<T>//泛型类
{
private T t;
public void setObject(T t)
{
this.t = t;
}
public T getObject()
{
return t;
}
//-----------------泛型方法
public <E> void print(E e)
{
System.out.println(e);
}
public <Q> void show(Q q)
{
System.out.println(q);
}
}
class GenericTest
{
public static void main(String[] args)
{
Test<String> t = new Test<String>();
t.setObject("GG");
System.out.println(t.getObject());
t.show(4);
}
}