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<E> iterator() { return new Itr(); } ... private class Itr implements Iterator<E> { public boolean hasNext() { return cursor != size(); } ... }
2.几种不允许的和允许的泛型形式
@Test public void test3() { // 几种不允许的和允许的形式 // 如果两边都使用泛型,形式必须一样 // ArrayList<Object> list1 = new ArrayList<String>();// // 不行,前面的接收任何类型,后面的强制只接受String // ArrayList<String> list2 = new ArrayList<Object>();// 同样不行 ArrayList<String> list3 = new ArrayList();// 可以,兼容性考虑,老程序使用新程序接口时 ArrayList list4 = new ArrayList<String>();// 可以,兼容性考虑,新程序使用老程序接口时 } // 老程序调用新程序 public void bb() { aa(new ArrayList()); } public void aa(ArrayList<String> list) { } // 新程序调用老程序 public void dd() { cc(new ArrayList<String>()); } public void cc(ArrayList list1) { }
3.HashSet:
Set集合:元素无序,不可重复。无序指存入和取出的顺序不一定一致
HashSet:底层数据结构是哈希表--->元素按哈希值存放,不按添加顺序存放,hashCode方法设置哈希值,增删改查,先比较哈希值,如果哈希值相同,再用equals比较是否是同一个对象(元素不允许重复)
例1:相同的字符串(常量)具有相同的哈希值------->即使给String对象起名字,也不过是引用
<pre name="code" class="java">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
改进:如果长度相同而内容不同,则无法插入,不合理,所以增加次要排序,修改比较器:
<pre name="code" class="java">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初始化处定义为匿名内部类。