11章 持有对象

11.1  泛型和类型安全的容器

    该部分主要是针对一个基本的容器类问题,就是当你不使用泛型(不定义类型参数)时,你无法保证加入到容器里的对象都是同一类型的对象,这样在数据读取的时候很容易就会发生类型错误。

    举例:假设有一个Apple类和Orange类,当代码像下面一样编写时,编译器是不会报告错误,可是当你取数据的时候,你根本不知道取出来的对象到底是Apple还是Orange,很可能就会发生类型转换异常,运行时就会报告: java.lang.ClassCastException: Orange1 cannot be cast to Apple1
  ArrayList arraylist = new ArrayList();
  arraylist.add(new Apple()); 
  arraylist.add(new Orange()); 

  Apple1 apple = (Apple1)arraylist.get(0);
  Apple1 apple1 = (Apple1)arraylist.get(1);

针对上面的问题,可以使用泛型,指定容器能够容纳的对象类型,从而在编译期就能够阻止以上问题的发生

  ArrayList<Apple1> arraylist = new ArrayList<Apple1>();   //泛型
  arraylist.add(new Apple1());
  //arraylist.add(new Orange1());   //该段代码就会在编译期时检测到错误

  Apple1 apple = arraylist.get(0);   //因为容器已经确定保存的对象类型,因此这个地方的类型强制转换都不需要了。


课后习题(Exercise 1):

class Gerbil {
private final int gerbilNumber;
Gerbil(int gerbilNumber) {
this.gerbilNumber = gerbilNumber;
}
public String toString() {
return "gerbil " + gerbilNumber;
}
public void hop() {
System.out.println(this + " is hopping");
}
}
public class E01_Gerbil {
public static void main(String args[]) {
ArrayList<Gerbil> gerbils = new ArrayList<Gerbil>();
for(int i = 0; i < 10; i++)
gerbils.add(new Gerbil(i));
for(int i = 0; i < gerbils.size(); i++)
gerbils.get(i).hop();
}
}

11.2 基本概念

   无

11.3 

11.4 容器的打印

public class Demo11_4 {
public static Collection<String> fill(Collection<String> collection) {
collection.add("One");
collection.add("Two");
collection.add("Three");
collection.add("Three");
return collection;
}
public static Map<String,String> fill(Map<String,String> map) {
map.put("One","1");
map.put("Two","2");
map.put("Three","3");
return map;
}
public static void main(String[] args) {
System.out.println(fill(new ArrayList<String>()));
System.out.println(fill(new LinkedList<String>()));
System.out.println(fill(new HashSet<String>()));
System.out.println(fill(new TreeSet<String>()));
System.out.println(fill(new LinkedHashSet<String>()));
System.out.println(fill(new HashMap<String,String>()));
System.out.println(fill(new TreeMap<String,String>()));
System.out.println(fill(new LinkedHashMap<String,String>()));
}
}

输出:

[One, Two, Three, Three]
[One, Two, Three, Three]
[Three, One, Two]
[One, Three, Two]
[One, Two, Three]
{Three=3, One=1, Two=2}
{One=1, Three=3, Two=2}
{One=1, Two=2, Three=3}


2个注意点:

1) 因为Set不允许有相同的值,当加入相同的值时,会覆盖,因此HashSet和TreeSet输出的值只有3个

2)TreeSet输出的值是 [One, Three, Two] ,因为TreeSet是按照比较结果的升序保存对象,对不同的对象,有不同的比较(以上程序是String类型)


  • BigDecimal、BigIneger以及所有数值型对应包装类:按它们对应的数值的大小进行比较。
  • Character:按字符的UNICODE值进行比较。
  • Boolean:true对应的包装类实例大于false对应的包装类实例。
  • String:按字符串中字符的UNICODE值进行比较。
  • Date、Time:后面的时间、日期比前面的时间、日期大。

11.5 List

List的一些常用方法,详细见API,范例中的Pets可以从以下地址下载(非本人发布,借用fisherwan发布的资源)

http://download.youkuaiyun.com/download/u011248694/7537217


11.6 迭代器

迭代器(Iterator)

  迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

  Java中的Iterator功能比较简单,并且只能单向移动:

  (1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

  (2) 使用next()获得序列中的下一个元素。

  (3) 使用hasNext()检查序列中是否还有元素。

  (4) 使用remove()将迭代器新返回的元素删除。

  Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素

范例:

SimpleIteration就介绍了简单的迭代器用法,看下CrossContainerIteration

public class CrossContainerIteration {
  public static void display(Iterator<Pet> it) {
    while(it.hasNext()) {
      Pet p = it.next();
      System.out.print(p.id() + ":" + p + " ");
    }
    System.out.println();
  }	
  public static void main(String[] args) {
    ArrayList<Pet> pets = Pets.arrayList(8);
    LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
    HashSet<Pet> petsHS = new HashSet<Pet>(pets);
    TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
    display(pets.iterator());
    display(petsLL.iterator());
    display(petsHS.iterator());
    display(petsTS.iterator());
  }
}

display的方法,可以忽略传入的容器对象,而根本不用考虑说是ArrayList的对象,还是LinkedList等其他。个人感觉有点像多态的处理方式。

display()方法在这边是通过传入Iterator对象来实现忽略容器类型,实际上针对这个范例,还可以通过容器基类接口Collection来实现,因为Iterator本身就是对 collection 进行迭代的迭代器:

<pre name="code" class="java">public static void display(Collection pets)
 {
	    Iterator<Pet> it = pets.iterator();
            ...............
    }
public static void main(String[] args) {
ArrayList<Pet> pets = Pets.arrayList(8);
LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
HashSet<Pet> petsHS = new HashSet<Pet>(pets);
TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);

display(pets);   //ArrayList
display(petsLL); //LinkedList
display(petsHS); //HashSet
display(petsTS); //TreeSet
</pre>注意点:<pre name="code" class="java">LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
HashSet<Pet> petsHS = new HashSet<Pet>(pets);
TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);

 
以上几个容器之间是可以互相转换的,但是不能直接通过类型强制转换完成


11.6.1 Listinterator 

Listiterator是iterator的子类,比iterator多了向前访问的功能,以及一些其他的,可查询API,Listinterator 由于涉及到前后移动,在游标cursor上很容易就犯错了

自己在学习的时候写了以下代码片,结果对输出很不理解,绕晕了:

  public static void main(String[] args) {
    ArrayList<Pet> pets = Pets.arrayList(8);
    System.out.println("pets = "+pets);
    ListIterator<Pet> it = pets.listIterator();
    
		while(it.hasNext()) {
			Pet pet = it.next();
<span style="white-space:pre">			</span>System.out.println(pet.id()+ " "+ pet);
			if(pet.id() == 2)
			{
				pet = it.previous();
				System.out.println("previous = "+ pet.id()+ " "+ pet);
				pet = it.next();
				System.out.println("next = "+ pet.id()+ " "+ pet);
			}
			}
		}

输出

pets = [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
0 Rat
1 Manx
2 Cymric
previous = 2 Cymric
next = 2 Cymric
3 Mutt
4 Pug
5 Cymric
6 Pug
7 Manx

在previous和next处竟然都输输出Cymric。

经过调试发现,原来是next()方法和previous()的方法在游标cursor的移动上有区别。

说明:当访问到 it.next(); 时,是先把当前位置给对象,然后游标cursor再往前移动,而 访问到 it.previous();时,是游标cursor先往前移动,再把位置给对象。

也就是说,在即将进入到 if 语句的时候,此时pet对象指向的位置是2,而cursor=3,此时执行previous()的时候,cursor=2,然后再把位置给pet对象,也就是说pet.id() =2

,此时又接着执行了next(),执行的时候,cursor先把2赋给pet.id(),然后cursor=3; 执行完后跳出if语句,又进入到while循环里,此时又执行了一次next动作,cursor又同样的,先把当前值3给pet.id,所以pet取的值就是3 Mutt。

可以看下ArrayList.class里的代码

public E previous() {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i;
            return (E) elementData[lastRet = i];
        }
<pre name="code" class="java"> public E next() {
      checkForComodification();
       int i = cursor;
       if (i >= size)
          throw new NoSuchElementException();
          Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
}


 













评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值