1.迭代器
迭代器是一个接口,是一种取出集合中元素的方式。每个集合的内部以内部类的方式实现了此接口,用于取出集合的元素。这些内部类都符合迭代器接口。集合通过一个iterator方法提供这个取出元素的方式。不同的集合因为底层数据结构不同,而实现取出元素的方式不同。但都符合此接口提供的共性方法用于操纵集合元素。
常见的迭代取出方式:
public void test() {
// TODO Auto-generated method stub
ArrayList al = new ArrayList();
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
for (Iterator it = al.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
java01
java02
java03
java04
为什么定义为内部类:如果定义在外部,想操纵集合元素就要创建集合对象。所谓内部类就是一种事物内部的事物,并且这种事物可以直接访问容器中的成员,而无需创建对象。集合的内部类还妙在其实现了一个对外的接口(iterator),只要用方法返回这个内部类对象,就可用接口的共性方法实现操纵元素的目的,而无需关心其内部如何实现。
JDK的AbstractList中关于iterator的内部实现方式:
public Iterator iterator() {
return new Itr();
}
...
private class Itr implements Iterator {
public boolean hasNext() {
return cursor != size();
}
...
}
2.几种不允许的和允许的泛型形式
@Test
public void test3() {
// 几种不允许的和允许的形式
// 如果两边都使用泛型,形式必须一样
// ArrayList
3.HashSet:
Set集合:元素无序,不可重复。无序指存入和取出的顺序不一定一致
HashSet:底层数据结构是哈希表--->元素按哈希值存放,不按添加顺序存放,hashCode方法设置哈希值,增删改查,先比较哈希值,如果哈希值相同,再用equals比较是否是同一个对象(元素不允许重复)
例1:相同的字符串(常量)具有相同的哈希值------->即使给String对象起名字,也不过是引用
public class HashSetDemo {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add("java01");// 无序,不按插入顺序
hs.add("java03");
hs.add("java01");
hs.add("java02");//转成String对象---->集合只能存对象(引用)
hs.add("java01");
hs.add(new String("java01"));// 同样判断重复,没插进去
hs.add(new String("java05"));
for (Iterator it = hs.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
结果:
java05java02java03java01
例2:
package it.cast.practice2;
public 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 void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int hashCode() {
return name.hashCode() + age * 5;// name是String类型,相同字符串地址相同
}
// 覆写equals---->参数别写成Person,必须是Object才是复写,都是Object中的方法!
public boolean equals(Object obj) {// 如果不覆写,会简单比较地址,都是new对象,地址当然不同,无法去除重复元素
// 加上这一句
if (!(obj instanceof Person))
return false;// 直接返回false---->不能抛异常,但在内部try可以,不可以抛比父类方法更多的异常
Person p = (Person) obj;// 必须强转,上面的参数和Object中一致
return this.name.equals(p.getName()) && this.age == p.getAge();
}
public String toString() {
return name + "..." + age;
}
}
主程序:
public class HashSetPerson {
/**
* @param args
*/
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("lixiaoming", 35));// 要求姓名年龄相同视为同一个人
hs.add(new Person("lixiaoming", 35));
hs.add(new Person("lixiaoming", 35));
hs.add(new Person("lixiaoming", 35));
hs.add(new Person("lixiaoming", 36));
hs.add(new Person("lixiaomin", 35));// hashCode不同直接不同
hs.add(new Person("lixiao", 35));
System.out.println(hs);
System.out.println(hs.contains(new Person("lixiaomin", 35)));//先比较哈希值,相同再用equals比较,可以将equals直接return true验证
hs.remove(new Person("lixiaomin", 35));//这个方法也是一样
System.out.println(hs.contains(new Person("lixiaomin", 35)));
System.out.println(hs);// 复写hashCode前,全部插入
// hashCode直接return
// 1,则比较hashCode相同后,用equals发现对象也相同,只插入一个!但姓名年龄不完全相同的也具有同样hashCode值不合理,应该不一样,直接不比equals了!
// 同一个人:保证hashCode相同,并且equals返回true,那么把hashCode方法和equals都写成名字年龄组合形式,同样的名字年龄返回同样哈希值,并且equals为true.
}
}
[lixiaoming...36, lixiaoming...35, lixiao...35, lixiaomin...35]
true
false
[lixiaoming...36, lixiaoming...35, lixiao...35]
4.TreeSet:
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Person("lisi", 29));
System.out.println(ts);
}
}
[lisi...29]
再插一个:
ts.add(new Person("zhangsan", 30));
Exception in thread "main" java.lang.ClassCastException: cn.itcast.treeset.Person cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(TreeMap.java:542)
at java.util.TreeSet.add(TreeSet.java:238)
at cn.itcast.treeset.TreeSetDemo.main(TreeSetDemo.java:12)
类型转换异常,无法转换成Comparable
原因:
TreeSet集合:会对元素进行排序,要求元素具有比较性,即实现Comparable接口,这种排序被称作类的自然排序
那么:修改Person类为实现Comparable接口,实现compareTo方法,按照年龄排序
package cn.itcast.treeset;
public class Person implements Comparable {//注意这里一定要实现Comparable接口!
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Object obj) {// 其实这个有泛型
// 先判断
if (!(obj instanceof Person)) {
throw new RuntimeException("不是学生对象");// 不能声明!
}
// 强转
Person p = (Person) obj;
// 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
return this.age - p.getAge();
}
/**
* @return the name
*/
String getName() {
return name;
}
/**
* @param name
* the name to set
*/
void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
int getAge() {
return age;
}
/**
* @param age
* the age to set
*/
void setAge(int age) {
this.age = age;
}
public String toString() {
return name + "..." + age;
}
}
[lisi...29, zhangsan...30]
问题:同一年龄不同姓名的按照compareTo判断相同后,不能插入,因为Set集合没有重复元素。而要求按照姓名年龄排序,也有个主次要条件的问题:先比较年龄,年龄相同比较姓名---------->注意是在compareTo中,返回负数,0,正数,而String类型已实现了Comparable接口,直接调用其compareTo方法即可排序。改进的compareTo方法:(按照姓名年龄判断和排序,返回0判定为相同元素,Set集合)
public int compareTo(Object obj) {// 其实这个有泛型
// 先判断
if (!(obj instanceof Person)) {
throw new RuntimeException("不是学生对象");// 不能声明!
}
// 强转
Person p = (Person) obj;
// 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
// return this.age - p.getAge();
if (this.age < p.getAge()) {
return -1;
}
if (this.age == p.getAge()) {// 年龄相同的,比较姓名
return this.name.compareTo(p.getName());
}
return 1;
}
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Person("lisi", 29));
ts.add(new Person("lisi", 30));
ts.add(new Person("liasi", 29));
ts.add(new Person("liasi", 30));
ts.add(new Person("zhangsan", 30));
System.out.println(ts);
}
}
[liasi...29, lisi...29, liasi...30, lisi...30, zhangsan...30]
问题:如果元素自身不存在比较性(无法修改),或者具备的比较性不是所需,该怎么办呢?此时就需要集合具有比较元素的功能,java提供了一个比较器用于对集合初始化,提供比较功能。
例子:
Person类保持不变,自定义比较器类:
package cn.itcast.treeset;
import java.util.Comparator;
public class MyComparator implements Comparator {// 实现Comparator接口
// 实现其compare方法,接收两个Object类型对象,注意这个在后面也有泛型
public int compare(Object o1, Object o2) {
System.out.println("Hi there,Comparator");
if (!(o1 instanceof Person) || !(o2 instanceof Person)) {
throw new RuntimeException("非人类");
}
// 强转
Person p1 = (Person) o1;
Person p2 = (Person) o2;
if (p1.getAge() > p2.getAge()) {
return 1;
}
if (p1.getAge() == p2.getAge()) {
return p1.getName().compareTo(p2.getName());
}
return -1;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator());
ts.add(new Person("lisi", 29));
ts.add(new Person("lisi", 30));
// ts.add(new Person("lisi", 30));
ts.add(new Person("lisi", 30));
ts.add(new Person("liasi", 29));
// ts.add(new Person("liasi", 30));
// ts.add(new Person("zhangsan", 30));
// ts.add(new Person("zhangsan", 29));
System.out.println(ts);
}
}
Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
Hi there,Comparator
[liasi...29, lisi...29, lisi...30]
(注:TreeSet元素以二叉树结构(有序)先和最小的比,找到自己位置后就不比了)
TreeSet集合比较元素,以Comparator为主(覆盖元素自身的自然排序比较方式)
练习:
需求:TreeSet存储字符串,要求以字符串长度排序
分析:字符串(String对象)本身具备比较性,但不是所需要的(并且无法更改),所以要自定义比较器传给TreeSet容器
定义前的自然排序:
public class StringLengthCompare {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet ts = new TreeSet();
ts.add("asljfslfjslfjsl");
ts.add("fksdf");
ts.add("Askfhskfskfhksdfvskfv");
ts.add("b");
for (Iterator it = ts.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
Askfhskfskfhksdfvskfv
asljfslfjslfjsl
b
fksdf
自定义比较器,按字符串长度排序
package cn.itcast.treeset;
import java.util.Comparator;
public class StringComparator implements Comparator {
public int compare(Object o1, Object o2) {
if (!(o1 instanceof String) || !(o2 instanceof String)) {
throw new RuntimeException("非字符串类");
}
String s1 = (String) o1;
String s2 = (String) o2;
return s1.length() - s2.length();
}
}
public class StringLengthCompare {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet ts = new TreeSet(new StringComparator());
ts.add("asljfslfjslfjsl");
ts.add("fksdf");
ts.add("Askfhskfskfhksdfvskfv");
ts.add("b");
for (Iterator it = ts.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
b
fksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv
改进:如果长度相同而内容不同,则无法插入,不合理,所以增加次要排序,修改比较器:
public int compare(Object o1, Object o2) {
if (!(o1 instanceof String) || !(o2 instanceof String)) {
throw new RuntimeException("非字符串类");
}
String s1 = (String) o1;
String s2 = (String) o2;
// return s1.length() - s2.length();
/*
* int i1 = new Integer(s1.length()); int i2 = new Integer(s2.length());
*
* if (i1 > i2) { return 1; } if (i1 == i2) { return s1.compareTo(s2);//
* 字符串自然顺序 } return -1;
*/
int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
if (num == 0)
return s1.compareTo(s2);
return num;
}
}
public class StringLengthCompare {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet ts = new TreeSet(new StringComparator());
ts.add("asljfslfjslfjsl");
ts.add("fksdf");
ts.add("fksdf");
ts.add("xksdf");
ts.add("aksdf");
ts.add("Bskfhskfskfhksdfvskfv");
ts.add("Askfhskfskfhksdfvskfv");
ts.add("b");
for (Iterator it = ts.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
}
b
aksdf
fksdf
xksdf
asljfslfjslfjsl
Askfhskfskfhksdfvskfv
Bskfhskfskfhksdfvskfv
注:Comparator可以直接在TreeSet初始化处定义为匿名内部类。
5.Map集合
示例:
import java.util.*;
class MapDemo
{
public static void main(String[] args)
{
//HashMap与HashTable底层都是哈希表结构
//容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。
Map map=new HashMap();
System.out.println(map.put("01","zhangsan"));//返回的是其替换了的值
System.out.println(map.put("01","zhangsan"));//key的hasCode相同
System.out.println(map.put("01","wangwu"));//相同的键(hashCode比较),新的值会替换旧值!返回的是其替换了的值!
map.put("02","zhangsan1");
map.put("03","zhangsan2");
map.put("04","zhangsan3");
System.out.println("-----------------");
System.out.println(map);
System.out.println("containsKey:"+map.containsKey("01"));
System.out.println("-----------------");
Collection coll=map.values();
for(String s:coll)
{
System.out.println(s);
}
System.out.println("-----------------");
//Map集合没有迭代器,其取出原理是将其转化成Set集合用迭代器
Set set=map.keySet();
for(Iterator its=set.iterator();its.hasNext();)
{
String key=its.next();
String value=map.get(key);
System.out.println(key+"="+value);
}
System.out.println("-----------------");
//Map.Entry类型和迭代器实现原理相似,都是在集合类内部实现的,通过entrySet方法可以获得这个对象
//Entry也是一个接口,它是Map接口中的一个内部接口
/*内部实现:
interface Map
{
//关系是Map内部事物,并且直接访问Map内部元素,所以定义成了内部规则(接口)
public static interface Entry//静态,并且对外暴露,只有内部接口才能加static关键字(类成员)
{
public abstract Object getKey();
public abstract Object getValue();
}
}
class HashMap implements Map
{
class Somename implements Map.Entry
{
public Object getKey(){}//实现
public Object getValue(){}//实现
}
}
*/
Set> entryset=map.entrySet();
for(Map.Entry entry:entryset)
{
System.out.println(entry.getKey()+"="+entry.getValue());
}
System.out.println("-----------------");
}
}
D:\java\practice4>javac MapDemo.java
D:\java\practice4>java MapDemo
null
zhangsan
zhangsan
-----------------
{04=zhangsan3, 01=wangwu, 02=zhangsan1, 03=zhangsan2}
containsKey:true
-----------------
zhangsan3
wangwu
zhangsan1
zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------
04=zhangsan3
01=wangwu
02=zhangsan1
03=zhangsan2
-----------------
D:\java\practice4>