java date comparetoo(e o)中e是什么类型

Java 中Comparable和Comparator区别比较
字体:[ ] 类型:转载 时间:
本文,先介绍Comparable 和Comparator两个接口,以及它们的差异;接着,通过示例,对它们的使用方法进行说明
Comparable 简介Comparable 是排序接口。若一个类实现了Comparable接口,就意味着“该类支持排序”。& 即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。Comparable 定义Comparable 接口仅仅只包括一个函数,它的定义如下: 代码如下:package java.import java.util.*;public interface Comparable&T& {&&& public int compareTo(T o);}说明:假设我们通过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。Comparator 简介Comparator 是比较器接口。我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。Comparator 定义Comparator 接口仅仅只包括两个个函数,它的定义如下: 代码如下:package java.public interface Comparator&T& {&&& int compare(T o1, T o2);&&& boolean equals(Object obj);}说明:(01) 若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。&&&&&&& 为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。(02) int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。Comparator 和 Comparable 比较Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。我们通过一个测试程序来对这两个接口进行说明。源码如下: 代码如下:import java.util.*;/**&* @desc "Comparator"和“Comparable”的比较程序。&*&& (01) "Comparable"&*&& 它是一个排序接口,只包含一个函数compareTo()。&*&& 一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort() 或 Collections.sort()进行排序。&*&& (02) "Comparator"&*&& 它是一个比较器接口,包括两个函数:compare() 和 equals()。&*&& 一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。&*&*&& 综上所述:Comparable是内部比较器,而Comparator是外部比较器。&*&& 一个类本身实现了Comparable比较器,就意味着它本身支持排序;若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。&*/public class CompareComparatorAndComparableTest{&&& public static void main(String[] args) {&&&&&&& // 新建ArrayList(动态数组)&&&&&&& ArrayList&Person& list = new ArrayList&Person&();&&&&&&& // 添加对象到ArrayList中&&&&&&& list.add(new Person("ccc", 20));&&&&&&& list.add(new Person("AAA", 30));&&&&&&& list.add(new Person("bbb", 10));&&&&&&& list.add(new Person("ddd", 40));&&&&&&& // 打印list的原始序列&&&&&&& System.out.printf("Original& sort, list:%s\n", list);&&&&&&& // 对list进行排序&&&&&&& // 这里会根据“Person实现的Comparable&String&接口”进行排序,即会根据“name”进行排序&&&&&&& Collections.sort(list);&&&&&&& System.out.printf("Name&&&&& sort, list:%s\n", list);&&&&&&& // 通过“比较器(AscAgeComparator)”,对list进行排序&&&&&&& // AscAgeComparator的排序方式是:根据“age”的升序排序&&&&&&& Collections.sort(list, new AscAgeComparator());&&&&&&& System.out.printf("Asc(age)& sort, list:%s\n", list);&&&&&&& // 通过“比较器(DescAgeComparator)”,对list进行排序&&&&&&& // DescAgeComparator的排序方式是:根据“age”的降序排序&&&&&&& Collections.sort(list, new DescAgeComparator());&&&&&&& System.out.printf("Desc(age) sort, list:%s\n", list);&&&&&&& // 判断两个person是否相等&&&&&&& testEquals();&&& }&&& /**&&&& * @desc 测试两个Person比较是否相等。&&&& *&& 由于Person实现了equals()函数:若两person的age、name都相等,则认为这两个person相等。&&&& *&& 所以,这里的p1和p2相等。&&&& *&&&& *&& TODO:若去掉Person中的equals()函数,则p1不等于p2&&&& */&&& private static void testEquals() {&&&&&&& Person p1 = new Person("eee", 100);&&&&&&& Person p2 = new Person("eee", 100);&&&&&&& if (p1.equals(p2)) {&&&&&&&&&&& System.out.printf("%s EQUAL %s\n", p1, p2);&&&&&&& } else {&&&&&&&&&&& System.out.printf("%s NOT EQUAL %s\n", p1, p2);&&&&&&& }&&& }&&& /**&&&& * @desc Person类。&&&& *&&&&&& Person实现了Comparable接口,这意味着Person本身支持排序&&&& */&&& private static class Person implements Comparable&Person&{&&&&&&&&&&&&&& S&&&&&&& public Person(String name, int age) {&&&&&&&&&&& this.name =&&&&&&&&&&& this.age =&&&&&&& }&&&&&&& public String getName() {&&&&&&&&&&&&&&&&&& }&&&&&&& public int getAge() {&&&&&&&&&&&&&&&&&& }&&&&&&& public String toString() {&&&&&&&&&&& return name + " - " +&&&&&&& }&&&&&&& /**&&&&&&&& * 比较两个Person是否相等:若它们的name和age都相等,则认为它们相等&&&&&&&& */&&&&&&& boolean equals(Person person) {&&&&&&&&&&& if (this.age == person.age && this.name == person.name)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&& /**&&&&&&&& * @desc 实现 “Comparable&String&” 的接口,即重写compareTo&T t&函数。&&&&&&&& *& 这里是通过“person的名字”进行比较的&&&&&&&& */&&&&&&& @Override&&&&&&& public int compareTo(Person person) {&&&&&&&&&&& pareTo(person.name);&&&&&&&&&&& //return this.name - person.&&&&&&& }&&& }&&& /**&&&& * @desc AscAgeComparator比较器&&&& *&&&&&& 它是“Person的age的升序比较器”&&&& */&&& private static class AscAgeComparator implements Comparator&Person& {&&&&&&& @Override &&&&&&& public int compare(Person p1, Person p2) {&&&&&&&&&&& return p1.getAge() - p2.getAge();&&&&&&& }&&& }&&& /**&&&& * @desc DescAgeComparator比较器&&&& *&&&&&& 它是“Person的age的升序比较器”&&&& */&&& private static class DescAgeComparator implements Comparator&Person& {&&&&&&& @Override &&&&&&& public int compare(Person p1, Person p2) {&&&&&&&&&&& return p2.getAge() - p1.getAge();&&&&&&& }&&& }}下面对这个程序进行说明。a) Person类定义。如下: 代码如下:private static class Person implements Comparable&Person&{&&&&&& S&&&&&&& ...&&& /** &&&& * @desc 实现 “Comparable&String&” 的接口,即重写compareTo&T t&函数。&&&& *& 这里是通过“person的名字”进行比较的&&&& */&&& @Override&&& public int compareTo(Person person) {&&&&&&& pareTo(person.name);&&&&&&& //return this.name - person.&&& }&& }说明:(01) Person类代表一个人,Persong类中有两个属性:age(年纪) 和 name“人名”。(02) Person类实现了Comparable接口,因此它能被排序。b) 在main()中,我们创建了Person的List数组(list)。如下: 代码如下:// 新建ArrayList(动态数组)ArrayList&Person& list = new ArrayList&Person&();// 添加对象到ArrayList中list.add(new Person("ccc", 20));list.add(new Person("AAA", 30));list.add(new Person("bbb", 10));list.add(new Person("ddd", 40));c) 接着,我们打印出list的全部元素。如下: 代码如下:// 打印list的原始序列System.out.printf("Original sort, list:%s\n", list);d) 然后,我们通过Collections的sort()函数,对list进行排序。&&& 由于Person实现了Comparable接口,因此通过sort()排序时,会根据Person支持的排序方式,即 compareTo(Person person) 所定义的规则进行排序。如下: 代码如下:// 对list进行排序// 这里会根据“Person实现的Comparable&String&接口”进行排序,即会根据“name”进行排序Collections.sort(list);System.out.printf("Name sort, list:%s\n", list);e) 对比Comparable和Comparator&&& 我们定义了两个比较器 AscAgeComparator 和 DescAgeComparator,来分别对Person进行 升序 和 降低 排序。e.1) AscAgeComparator比较器它是将Person按照age进行升序排序。代码如下: 代码如下:/**&* @desc AscAgeComparator比较器&*&&&&&& 它是“Person的age的升序比较器”&*/private static class AscAgeComparator implements Comparator&Person& {&&& @Override&&& public int compare(Person p1, Person p2) {&&&&&&& return p1.getAge() - p2.getAge();&&& }}e.2) DescAgeComparator比较器它是将Person按照age进行降序排序。代码如下: 代码如下:/**&* @desc DescAgeComparator比较器&*&&&&&& 它是“Person的age的升序比较器”&*/private static class DescAgeComparator implements Comparator&Person& {&&& @Override&&& public int compare(Person p1, Person p2) {&&&&&&& return p2.getAge() - p1.getAge();&&& }}f) 运行结果运行程序,输出如下: 代码如下:Original& sort, list:[ccc - 20, AAA - 30, bbb - 10, ddd - 40]Name&&&&& sort, list:[AAA - 30, bbb - 10, ccc - 20, ddd - 40]Asc(age)& sort, list:[bbb - 10, ccc - 20, AAA - 30, ddd - 40]Desc(age) sort, list:[ddd - 40, AAA - 30, ccc - 20, bbb - 10]eee - 100 EQUAL eee - 100
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具博客分类:
排序的算法是我们最常用的算法,初学程序,每个人都尝试过排序。但只是局限于简单的排序。
如将下列数字进行排序
1,3,5,8,3,6
于是我们得出结果
1,3,3,5,6,8
将下列字母(字符)进行排序
a,i,e,f,w,s
于是我们得出结果
a,e,f,i,s,w
但是我们遇到的情况就不是如此简单了。如给公司里的商品进行排序,我们很轻易的想到按照商品的名称排序不就完了,而且简单明了。但现实并如我们相信般简单。同一商品名称可以有不同的批次,进货时间,可能还会有单价的不同。显然只根据商品名称排序是不合理的。
再举个简单例子然后用程序实现。如公司要将员工进行排序(不要说领导排在前面),假设我们的需求比较复杂。先进行姓排序,谁的姓拼音靠前,谁就排前面。然后对名字进行排序。恩.如果同名,女性排前头。如果名字和性别都相同,年龄小的排前头。ok,一个也不算复杂的需求。
如果对java比较熟悉的会知道parator 接口。要实现里面的函数
int compare(Object o1, Object o2) 返回一个基本类型的整型,返回负数表示o1 小于o2,返回0 表示o1和o2相等,返回正数表示o1大于o2。
于是我们设计的人员类要有几个变量,firstname,lastname,sex,age分别表示姓,名,性别,年龄。
public class Person {
String firstname,
public Person(String firstname,String lastname,Boolean sex,Integer age) {
this.firstname =
this.lastname =
this.sex =
this.age =
public String getFirstName() {
public String getLastName() {
public Boolean getSex() {
public Integer getAge() {
//为了输入方便,重写了toString()
public String toString()
return firstname +" "+lastname+" "+(sex.booleanValue()?"男":"女")+" "+
//end person
下面是要实现比较器
public class Comparators {
public static parator getComparator() {
return new parator() {
public int compare(Object o1, Object o2) {
if (o1 instanceof String) {
return compare( (String) o1, (String) o2);
}else if (o1 instanceof Integer) {
return compare( (Integer) o1, (Integer) o2);
}else if (o1 instanceof Person) {
return compare( (Person) o1, (Person) o2);
System.err.println("未找到合适的比较器");
public int compare(String o1, String o2) {
String s1 = (String) o1;
String s2 = (String) o2;
int len1 = s1.length();
int len2 = s2.length();
int n = Math.min(len1, len2);
char v1[] = s1.toCharArray();
char v2[] = s2.toCharArray();
int pos = 0;
while (n-- != 0) {
char c1 = v1[pos];
char c2 = v2[pos];
if (c1 != c2) {
return c1 - c2;
return len1 - len2;
public int compare(Integer o1, Integer o2) {
int val1 = o1.intValue();
int val2 = o2.intValue();
return (val1 & val2 ? -1 : (val1 == val2 ? 0 : 1));
public int compare(Boolean o1, Boolean o2) {
return (o1.equals(o2)? 0 : (o1.booleanValue()==true?1:-1));
public int compare(Person o1, Person o2) {
String firstname1 = o1.getFirstName();
String firstname2 = o2.getFirstName();
String lastname1 = o1.getLastName();
String lastname2 = o2.getLastName();
Boolean sex1 = o1.getSex();
Boolean sex2 = o2.getSex();
Integer age1 = o1.getAge();
Integer age2 = o2.getAge();
return (compare(firstname1, firstname2) == 0 ?
(compare(lastname1, lastname2) == 0 ? (compare(sex1, sex2) == 0 ? (compare(age1, age2) == 0 ? 0 :
compare(age1, age2)) :
compare(sex1, sex2)) :
compare(lastname1, lastname2)) :
compare(firstname1, firstname2));
以上代码有可能因为浏览器的布局自动换行。
compare(Person o1, Person o2)的返回值看起来比较别扭。最简单的是
public int compare(Boolean o1, Boolean o2) {
return (o1.equals(o2)? 0 : (o1.booleanValue()==true?1:-1));
o1和o2相等返回0,否则o1如果是true 就表示o1大于o2。
再尝试输出结果看看
public class Main {
public Main() {
public static void main(String[] args) {
Person[] person = new Person[] {
new Person("ouyang", "feng", Boolean.TRUE, new Integer(27)),
new Person("zhuang", "gw", Boolean.TRUE, new Integer(27)),
new Person("zhuang", "gw", Boolean.FALSE, new Integer(27)),
new text.Person("zhuang", "gw", Boolean.FALSE, new Integer(2)),
for (int i = 0; i & person. i++) {
System.out.println("before sort=" + person[i]);
java.util.Arrays.sort(person, Comparators.getComparator());
for (int i = 0; i & person. i++) {
System.out.println("after sort=" + person[i]);
输出结果:
before sort=ouyang feng 男 27
before sort=zhuang gw 男 27
before sort=zhuang gw 女 27
before sort=zhuang gw 女 2
after sort=ouyang feng 男 27
after sort=zhuang gw 女 2
after sort=zhuang gw 女 27
after sort=zhuang gw 男 27
仔细理解java的Comparator会给你写排序带来很大帮助
浏览 77249
muscle-liu
浏览: 171693 次
来自: 广州
写的不错,就是有个问题楼主忽略了:“如何判断比较的结果是升序还 ...
最后return的那一长串代码,阅读性很差,建议修改为:int ...
中文字符串.getBytes()得到字节数组每个元素比较可以比 ...
如果姓名都是中文的你这个不支持呢
Sorry,看错了。是run方法()7.集合1_ASP.NET技巧_动态网站制作指南
来源:人气:34
1.1 什么是集合
  集合是存储对象的容器。集合中可以存储任意类型的变量。提供了多种集合类,不同集合类的数据结构不同,分别适合在不同的场景使用。
1.2 集合和数组
  数组也用来存储数据,如果定义一个对象数组,例如Object[] objArr;那么对象数组也能存储对象。但是集合和数组有很大区别:
  (1)数组长度是固定的,集合的长度是可变的。如果无法预先知道需要多少存储多少个数据,那么很难使用数组。
  (2)数组一般只能存储同一类型的元素,集合可存储不同类型的元素。
  (3)集合只能存储引用类型的元素。JDK 5实现了自动装箱功能,所以集合形式上也能存储基本数据类型的元素。
1.3 Collection接口
  集合有很多共性,因此集合抽象出了一个Collection接口,下面讲的单例集合都是Collection接口的实现类。下面通过一个实现类来讲Collection接口中的常用方法。
2. Collection接口
  为了了解Collection接口中方法,首先要创建一个集合对象,那么需要用多态方式来创建一个Collection接口的实现类对象。
  先找到一个实现Collection接口的类。查看API,有如下说明:
  “JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set 和 List)的实现。”
  也就是说Collection没有直接的实现类,我们可以找Collection子接口Set或者List接口的实现类。其中,ArrayList是List接口的一个实现类,我们就用它来创建集合对象,以此介绍Collection接口中的方法。【提示,目前看API时会遇到、等字样,而且在IDE中会有警告,这涉及到泛型,暂时不用管它】
2.1 添加和删除对象方法
  (1)public boolean add(Object o):添加一个元素到集合中。此方法总是返回true,因此返回值不重要。
  (2)public boolean addAll(Collection c):将一个集合的元素全部添加到该集合中。
  (3)public boolean remove(Object o):将元素从集合中删除(如果有重复,只会删除一个)。如果集合中没有该元素,则返回false(不会出现异常)。
  (4)public boolean removeAll(Collection c):移除集合中存在于集合c中的元素,包括重复的元素。只要有一个元素被移除了就返回true。
  (5)public void clear():移除集合中所有的元素,集合变为一个空集合。
2.2 其他方法
  (1)public boolean contains(Object o):判断集合中是否包含指定的元素。对于引用类型,contains方法中是调用对象的equals方法来判断的,因此当集合存储自定义对象时,注意重写好自定义类的equals方法。
  (2)public boolean isEmpty():判断集合是否为空。
  (3)public int size():返回集合中元素个数。
  (4)public boolean containsAll(Collection c):判断集合中是否包含了集合c的所有元素。必须全部包含才返回true。
  (5)public boolean retainAll(Collection c):将该集合与集合c进行交集,结果保存在本集合中,如果本集合改变了就返回true,否则返回false。
  (6)public Object[] toArray():将集合转成Object数组。
  (7)public String toString():Collection重写了toString()方法,可以直接将集合以类似数组的方式输出。
  下面是将自定义对象存储再集合中的例子。
  (1)Student类
package com.zhang.
public class Student {
public String getName() {
public void setName(String name) {
this.name =
public int getAge() {
public void setAge(int age) {
this.age =
// 构造方法
public Student(){}
public Student(String name, int age) {
this.name =
this.age =
// 下面重写equals、hashCode和toString方法
public boolean equals(Object obj) {
if(!(obj instanceof Student)) {
if(obj == this) {
Student stu = (Student)
if(stu.getName().equals(this.getName()) && stu.getAge() == this.getAge()) {
// 若姓名和年龄相同,就假定他们是同一对象
public int hashCode() {
return this.getName().hashCode() + this.getAge();
public String toString() {
return this.getName() + ":" + this.getAge();
  (2)主类
package com.zhang.
import java.util.ArrayL
import java.util.C
public class Demo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("张三", 12));
c.add(new Student("李四", 13));
c.add(5); //可以加任意类型
// 判断,底层调用的是equals方法
System.out.println(c.contains(new Student("张三", 12)));
// 得到元素个数
System.out.println(c.size());
// 输出集合,调用的是toString()方法,显示很友好,把其中自定义对象equals方法也调用了
System.out.println(c);
2.3 Iterator迭代器
  迭代器用于对集合元素的遍历。迭代器以内部类的形式存在于集合类中,通过迭代器能够拿到集合中元素,也能通过迭代器来操作元素。
  调用集合的iterator()方法能得到此集合的迭代器。Collection接口继承自Iterable接口,正是Iterable接口定义了iterator()方法。iterator()方法返回的是Iterator接口,该接口有如下方法以便遍历和获取元素:
  (1)boolean hasNext():判断是否还有可迭代的元素。
  (2)Object next():获取下一个元素,同时指针再向后移动。如果没有下一个元素,则抛出NoSuchElementException异常。
  小知识点:如果一个方法能返回任意类型或者接受任意类型,那么返回值类型或者参数类型就可设置为Object,因为Object是所有类的基类。
  因此常用上面的方法实现遍历,例子:(Student类还是上面的代码)
package com.zhang.
import java.util.ArrayL
import java.util.C
import java.util.I
public class Demo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("张三", 12));
c.add(new Student("李四", 13));
Iterator it = c.iterator();
while(it.hasNext()) {
// 先拿出对象。由于存储的都是Student,直接转换
Student stu = (Student) it.next();
System.out.println(stu);
注意,不能这样写:
System.out.println( ((Student) it.next()).getName() );
System.out.println( ((Student) it.next()).getAge() );
这样每次循环都会执行两次next(),会导致跳过元素和出现
NoSuchElementException的异常
  推荐在for循环中创建迭代器并迭代,这样可以尽早释放迭代器资源。代码如下:
package com.zhang.
import java.util.ArrayL
import java.util.C
import java.util.I
public class Demo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Student("张三", 12));
c.add(new Student("李四", 13));
for(Iterator it = c.iterator(); it.hasNext(); ) {
Student stu = (Student) it.next();
System.out.println(stu.getName());
System.out.println(stu.getAge());
  迭代器中还有remove()方法,可在遍历时删除元素,但是使用remove()方法之前必须先调用next()方法,否则会出现IllegalStateException异常。此外,在用迭代器迭代过程中,只能通过迭代器操作元素,不能使用集合本身提供的方法或者其他方法操作集合元素,否则会有安全隐患,会抛出并发修改异常ConcurrentModificationException。这点在List集合中还会强调和加深。
  总结:上述讲的Collection方法和迭代器,在下面讲的单例集合(List和Set集合)中都能使用,因为单例集合就是Collection的实现类。
3. List集合
  List也是接口,最常用的实现类是ArrayList。List集合有如下特点:
  (1)List集合是有序的。List集合会保留元素的存储顺序,这样取出元素时是顺序取出。
  (2)List集合中元素可以重复,即一个集合能存储多个相同的元素。
3.2 List集合特有方法
  (1)public void add(int index, Object o):在指定索引处添加元素。
  (2)public Object get(int index):获取指定位置的元素。
  (3)public Object remove(int index):删除指定索引处的元素,并返回此元素
  (4)public Object set(int index, Object o):将指定索引处的元素修改为新值o,返回值是原本此位置的元素。若索引超出范围,则抛出IndexOutOfBoundsException。
  (5)public boolean addAll(int index, Collection c):在指定索引处添加集合中所有元素。
  (6)public int indexOf(Object o):返回集合中第一次出现该对象的索引,如果没有,就返回-1。
  (7)public int lastIndexOf(Object o):返回集合中最后一次出现该对象的索引,如果没有,就返回-1。
  (8)public List subList(int fromIndex, int toIndex):获得指定区间的子集合。不包含toIndex。
  说明:上述介绍的很多方法,比如remove()、indexOf()等都要先找到集合中与目标相同的对象,调用的方法都是调用equals()方法。
  List集合有get(int index)方法,这样可以结合Collection的size()方法自行实现遍历,而不需用迭代器。例子:
package com.zhang.
import java.util.ArrayL
import java.util.L
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Student("张三", 12));
list.add(new Student("李四", 13));
// 利用get()和size()遍历List集合
for(int i = 0; i & list.size(); i++) {
Student stu = (Student) list.get(i);
System.out.println(stu);
3.3 List特有迭代器——ListIterator
  List集合可以直接用Iterator迭代器,还能用List特有的迭代器ListIterator。通过调用List集合对象的listIterator()方法,能得到ListIterator对象。
  ListIterator继承自Iterator,基本的用法和Iterator相同,只是ListIterator中增加了如下方法:
  (1)public boolean hrevious():判断是否有上一个元素
  (2)public Object previous():获取上一个元素,并将指针再指向前一个元素。
  通过上述方法,能够实现逆向遍历List集合。当然,一般先要使用next()方法,将指针指向后面,才可能逆向遍历,因为一开始的游标(指针)都是在第一个元素的前面。
  另外,ListIterator还有方法:
  public void add(Object o):将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有),或者 previous 返回的下一个元素之后(如果有);如果列表没有元素,那么新元素就成为列表中的唯一元素。新元素被插入到隐式光标前:不影响对 next 的后续调用,并且对 previous 的后续调用会返回此新元素。
  借此机会着重讲“并发修改异常”。
  前面提到过:在用迭代器过程中,只能通过迭代器操作元素,否则会有安全隐患,产生并发修改异常。例子:集合中存储“hello”,“world”和“java”三个字符串,要求遍历集合时,一旦遇到“world”字符串,就在集合中添加一个“c++”字符串。
  错误示范:在用迭代器过程中,使用集合方法操作集合元素:
package com.zhang.
import java.util.*;
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("java");
// 迭代器遍历
for(Iterator it = list.iterator(); it.hasNext(); ) {
String str = (String) it.next();
if(str.equals("world")) {
// 用集合的add()方法添加元素
list.add("c++");
// 最后输出
System.out.println(list);
  运行程序后出现异常:ConcurrentModificationException。为什么出现并发修改异常呢?因为使用迭代器时,如果使用了集合来操作元素,会导致迭代器并不知道集合被修改了,而迭代器的使用是完全依赖于集合的,因此出现并发修改异常。
  解决的办法就是只用迭代器来操作集合(ListIterator中有add方法),或者只用集合的方法来遍历和操作集合。下面是例子。只不过根据他们的实现不用,用ListIterator添加时,元素被添加在当前元素下面,而用集合本身的add()方法添加时,新元素被添加在集合的最后。
  方法1:使用List迭代器的add()方法。
package com.zhang.
import java.util.*;
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("java");
// 迭代器遍历
for(ListIterator it = list.listIterator(); it.hasNext(); ) {
String str = (String) it.next();
if(str.equals("world")) {
it.add("c++");
// 最后输出
System.out.println(list); // [hello, world, c++, java]
  方法2:使用List集合的方法来遍历和操作。
package com.zhang.
import java.util.*;
public class Demo {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("java");
// 用集合的size()和get()遍历,并修改
for(int i = 0; i & list.size(); i++) {
if(list.get(i).equals("world")) {
list.add("c++");
// 最后输出
System.out.println(list); // [hello, world, java, c++]
3.4 List集合常用子类
  List集合常用的子类有ArrayList(前面一直用的)、LinkedList和Vector。详细解释如下:
  (1)ArrayList:底层的数据结构是动态数组。特点是查询速度快(允许直接用索引来查找对应的元素)、增删慢(插入和删除操作涉及元素移动和内存操作)。ArrayList是线程不安全的类,效率较高。
  (2)LinkedList:底层数据结构是用链表存储数据。链表的特点是查询慢(需要遍历节点查找),增删快(只需记住上个节点和下个节点)。LinkedList也是线程不安全的类,效率较高。LinkedList中一些特有的方法(需要针对头尾的操作):
  addFirst(E e)、addLast(E e)、getFirst()、getLast()、removeFirst()和removeLast()等。
  (3)Vector:是线程安全的ArrayList。用法和ArrayList相似。说明一下,Vector中提供了一个elements()实例方法,返回的是一个Enumeration对象。此对象用法和Iterator一致,只不过方法名不一样,方法原型为:
  public boolean hasMoreElement();和public Object nextElement()。
  除此之外,List的实现类还有Stack,就是堆栈结构。其实List就是数据结构里面的线性表,使用时,结合数据结构的知识,就能很好的选择使用,并能迅速的了解不同结构中提供的方法的含义。因此推荐学习完C语言之后研究下数据结构和算法。
4. 泛型(Generic)
4.1 引入泛型
  集合能存储不同的数据类型,但这个特点可能会出现安全问题。因为操作集合元素时,要先拿到该元素,用集合(或迭代器)的方法只能拿到Object类型元素,因为集合能存储任意类型,集合本身无法知道自己所存储的究竟是什么类型。因此我们拿到Object类型元素时再将元素强制转换成需要的类型,这时可能发生转换错误。
  例子:
package com.zhang.
import java.util.ArrayL
public class Demo {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("hello");
list.add("world");
list.add(new Student());
// 该集合中存储了字符串,最后一个元素存储了学生对象
// 遍历取出元素
for(int i = 0; i & list.size(); i++) {
// 强制转换为String类型
String str = (String) list.get(i);
System.out.println(str);
  运行程序会出现“ClassCastException”,即类型转换异常,这是因为集合中还存储了Student对象。这样取出元素会有安全性问题,这种问题在编译时是无法发现的,因此IDE只能提示警告。虽然能通过类型检查(如instanceof)来判断元素的类型,但是这样效率较低。处理这样的问题,一般是直接限定该集合只能存储一种特定类型的对象。
  而泛型就是用来限定该集合只能存储一种特定数据类型的。
  对集合使用泛型的格式:
  集合类 集合变量 = new 集合类();这里的就是泛型,E就指定该集合能存储何种数据类型。比如:
  ArrayList arrayList = new ArrayList();就表示arrayList集合只能存储String类型的数据。
  需要注意的是,这里的E只能写引用类型,不能写基本数据类型,比如不能写成,而只能写成。以后就常用泛型了,使用泛型后,IDE环境中也不会出现警告了。现在也能知道API中等的含义了。
  下面体会下泛型的使用:
package com.zhang.
import java.util.ArrayL
public class Demo {
public static void main(String[] args) {
ArrayList&Student& stuList = new ArrayList&Student&();
stuList.add(new Student("张三", 13));
stuList.add(new Student("李四", 14));
for(int i = 0; i & stuList.size(); i++) {
// 使用泛型无需类型转换
Student stu = stuList.get(i);
System.out.println(stu);
  上面的例子中体会到,使用泛型后,集合中只能存特定类型的数据,并且取出元素时,会自动根据泛型得到对应的数据类型,无需类型转换,即如果这是用String泛型的集合,那么用get()方法得到的就会是String类型的数据。
  很多地方使用泛型更加友好,比如JDK中提供的一个接口Comparable:
package java.
public interface Comparable {
public int compareTo(Object o);
  在JDK 1.5之后,此接口被改成了泛型:
package java.
public interface Comparable&T& {
public int compareTo(T o);
  这样的话,这里的泛型类型就能被以后具体的类型所替换,避免了从特定类型转到Object类型,再从Object类型转到具体类型的过程。(学了后面的泛型方法、泛型类等后理解更深刻)
4.2 泛型方法
  泛型方法就是在方法中使用泛型,实际也就是在方法形参和返回值中使用泛型,使形参接受指定类型的参数,然后返回该类型的数据,这样可以使方法适应不同的数据类型,而且不用像使用Object那样,进行强制转换。
  可以只在形参中使用泛型,返回值可以不用泛型,而是返回特定的一种类型,但是一般不只在返回值中使用泛型,因为没有什么意义。
  泛型相当于将类型当作变量处理。规范泛型的定义一般是一个大写的任意字母(实际上任意合法的变量名都是可以的)。在方法中使用泛型的格式:
  public &声明泛型的变量& 返回值类型 方法名(数据类型和形参…)。尖括号中就是声明泛型的变量,声明好了后,就能在方法中使用该变量作为泛型类型了,比如:
  public
T getModel(T t) {…}。
  上面的方法就先在尖括号中声明了泛型变量T,这个T就相当于一个类型了,然后在返回值、参数和方法体中就能使用这个T类型了。方法说明此方法接受一个泛型类型T参数,方法返回值是也是类型T。这个T到底是什么类型呢,只有在调用方法时,传递参数时才知道。也就是调用方法时传递什么类型,这个T就是什么类型。
  实际上泛型就是实现参数化类型,把类型当作参数一样的传递。
  例子:
package com.zhang.
import java.util.D
public class Demo {
public static void main(String[] args) {
Student stu = getObj(new Student("张三", 12));
System.out.println(stu);
// 传递什么类型进去,就返回什么类型
Date date = getObj(new Date(System.currentTimeMillis()));
System.out.println(date);
// 这是一个静态的泛型方法,直接返回传递进来的参数
public static &E& E getObj(E e) {
4.3 泛型类
  当一个类中多个方法都用到了泛型方法时,特别是在类中也需要使用泛型时,那么就可将泛型声明到类上,使用泛型类。
  泛型类是在类名后声明泛型,也是在尖括号中。这时,泛型的类型就是在创建此对象时使用的泛型类型。当然,类中的静态成员不能使用类中定义的泛型,如果静态成员要使用泛型,只能独立声明泛型,因为静态成员是在类对象存在之前就存在的,不能根据创建对象时使用的类型来决定静态泛型的类型。
  泛型类的格式:
  public class 类名&声明泛型的变量& {
    …
  例子:
  (1)GenericDemo类(泛型类)
package com.zhang.
// 类上声明泛型
public class GenericDemo &E& {
// 类中可以使用泛型类型
private E // 泛型成员
// get和set方法
public void setObj(E obj) {
this.obj =
public E getObj() {
return this.
// 实例方法,展示其中维护的泛型类型对象
public void showObj() {
System.out.println(this.getObj().toString());
// 如果是静态方法,同样还要定义泛型类型,不能使用实例成员
public static &T& void showArr(T[] arr) {
// 用于展示任意类型的数组
for(int i = 0; i & arr. i++) {
System.out.println(arr[i]);
  (2)主类
package com.zhang.
import java.util.D
public class Demo {
public static void main(String[] args) {
// 使用泛型类。
// 创建对象时,new后面的泛型类型可省略,只要声明时带有泛型类型即可
GenericDemo&Student& stu = new GenericDemo&&();
stu.setObj(new Student("张三", 14));
stu.showObj();
// 使用静态方法
String[] strArr = {"hello", "world", "java"};
GenericDemo.showArr(strArr);
  创建对象时,如果不指定泛型的具体类型,那么默认类型是Object(和创建集合对象时一样)。
4.3.1 泛型类的继承
如果一个类继承了泛型类,那么下面的方式:
(1)子类不使用父类泛型,那么直接:
  public class SubClass extends GenericDemo {
    …
  这时在子类中调用父类的泛型方法,其类型是Object。
  或者子类使用自己定义的泛型,那么语法是:
  public class SubClass extends GenericDemo {…}
(2)子类使用和父类一样的泛型:
  public class SubClass extends GenericDemo {…};
(3)子类继承父类的指定类型:
  public class SubClass extends GenericDemo{…}
  当然这时子类也能使用自己的泛型:
  public class SubClass extends GenericDemo{…}
4.4 泛型接口
  接口泛型的声明也是在接口名后面的尖括号中。比如:
package com.zhang.
public interface GenericInterface &E& {
void show(E e);
  那么泛型接口的实现类,可以还是用泛型,也可以处理具体的类型,比如:
  (1)方法1:还是用泛型,处理任意类:
package com.zhang.
// 声明泛型类型的名字可以和接口的名字不一样。只要他们一致就表示处理的还是泛型
public class ImplDemo1&T& implements GenericInterface&T& {
public void show(T t) {
System.out.println(t);
  然后主类创建实现类对象时指定具体类即可。
  (2)方法2:处理具体的类型:
package com.zhang.
public class ImplDemo2 implements GenericInterface&String& {
public void show(String s) {
System.out.println(s);
  当然,如果直接implements GenericInterface,那么具体的类就是Object,此外,实现类也能使用自己的泛型,即:
  public class ImplDemo2 implements GenericInterface。但是就是不能同时使用接口的泛型,又使用自己的泛型,泛型类中也是这样。
4.5 泛型通配符
  先看一个需求:写一个方法,此方法能接受任意泛型的集合对象,然后将对象打印。
  由于能接受任意类型,那么方法参数可定义为Collection,即:
public static void print(Collection&Object& c) {
for(Iterator&Object& it = c.iterator(); it.hasNext(); ) {
Object obj = it.next();
System.out.println(obj);
  我们的想法是,传递一个ArrayList对象或者一个ArrayList对象进去,都能打印出集合中的元素,即能:
public static void main(String[] args) {
ArrayList&String& arr = new ArrayList&String&();
arr.add("hello");
print(arr);
  但是print(arr)这行报错,并不能通过编译,原因是print函数已经限定了类型是Object,因此arr是不能作为参数的。
  解决的办法有:1.就传递给print方法Coolection对象,但是这样可能要将原数据进行转换。2.print方法不使用泛型,即形参直接是Collection c。但是这样没有使用泛型会有警告,JDK不推荐。
  而通配符?就是用于解决这个问题的,可以将?作为泛型类型,这个?就代表任意类型。如果传递进来的泛型是String,那么?就代表String,如果传递进来的泛型是Integer,那么?就代表Integer。因此最终可用这样的代码:
package com.zhang.
import java.util.ArrayL
import java.util.C
import java.util.I
public class Demo {
public static void main(String[] args) {
ArrayList&String& arr = new ArrayList&String&();
arr.add("hello");
ArrayList&Integer& num = new ArrayList&&();
num.add(1);
num.add(23);
print(arr);
print(num);
public static void print(Collection&?& c) {
for(Iterator&?& it = c.iterator(); it.hasNext(); ) {
Object obj = it.next();
// 这里还是用Object是理所当然的,因为不知道具体类型
// 使用?为了使用泛型并且不用将原来对象进行转换
System.out.println(obj);
  通配符用来限定类型的范围。单单的?没有类型的限定,是没有边界的,可以用下面的方式进行限定:
package com.zhang.
import java.util.ArrayL
import java.util.C
public class Demo {
public static void main(String[] args) {
//?通配符可以是任意类型
Collection&?& e1 = new ArrayList&Animal&();
Collection&?& e2 = new ArrayList&Dog&();
//? extends E,可以是E和E的子类
Collection&? extends Animal& e3 = new ArrayList&Animal&();
Collection&? extends Animal& e4 = new ArrayList&Dog&();
//? super E,可以是E及其父类
Collection&? super Animal& e5 = new ArrayList&Animal&();
Collection&? super Animal& e6 = new ArrayList&Dog&();//错误
//先写两个具有继承关系的类
class Animal{
class Dog extends Animal{
  泛型允许程序员在编写代码时,就限制集合的处理类型,从而把原来程序运行时可能发生的问题,转变为编译时问题,以此提高程序的可读性和稳定性。
  注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上就阻止向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受影响,这个过程称为“擦除”。
  泛型的基本术语:
  以ArrayList为例:&&念:typeof。
  ArrayList中的E称为类型参数变量
  ArrayList中的Integer称为实际类型参数
  整个称为ArrayList泛型类型
  整个ArrayList称为参数化的类型ParameterizedType
本篇文章已结束,若您喜欢,欢迎支持我的工作。
优质网站模板

我要回帖

更多关于 date compareto 的文章

 

随机推荐