一、泛型和类型安全的容器

package tij.hoding;

import java.util.ArrayList;

public class Test {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList apples=new ArrayList();
        for(int i=0;i<2;i++){
            apples.add(new Apple());
        }
        apples.add(new Orange());
        for(int i=0;i<apples.size();i++){
            Apple a=(Apple) apples.get(i);
        }
    }
}
class Apple {
    private static long counter;
    private final long id = counter++;
    long id() {
        return id;
    }
}
class Orange {}

在运行期的类型转换时会出现问题,因为使用get方法的时候取出来的其实object类型,之前程序中放进去了一个orange却要将他转成一个apple。
因此需要使用类型参数来指定这个容器实例可以保存的类型,通过使用类型参数,就可以在编译期放置将错误的类型放在容器中,上面的代码改成下面类型

package tij.hoding;

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<Apple> apples=new ArrayList<>();
        for(int i=0;i<2;i++){
            apples.add(new Apple());
        }
        for(int i=0;i<apples.size();i++){
            Apple a=(Apple) apples.get(i);
        }
    }
}
class Apple {
    private static long counter;
    private final long id = counter++;
    long id() {
        return id;
    }
}

同样,可以将容器的类型参数的指定类型以及其子类类型的实例变量放进这个容器中,例子略

二、基本概念

Java容器类可以划分为两个不同的概念

  • Collection
    一个存储独立元素的序列,不同序列有不同的规则:List必须按照插入顺序保存元素;Set不能有重复元素;Queue按照排队规则确定对象产生的顺序。
  • Map
    一组成对的简直对对象

然而大多数情况在编写代码的时候都是与接口打交道,唯一需要精确指定使用的容器类型的地方就是创建的时候,如下

List<Apple> apples=new ArrayList<Apple>();

就是说多数情况我们是面向接口编程的,但也并不都是这样,比如如果我们要使用LinkedList里的特有的功能,就不能用这种方法了。

三、添加一组元素

Collections和Arrays类中提供了很多与序列相关的方法,有啥自己去找API看去就行了。
注明

package tij.hoding;

import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<Snow> snow1=Arrays.asList(new Crusty(),new Slush(),new Powder(),new Light());
        List<Snow> snow2=Arrays.asList(new Light(),new Heavy());
    }
}
class Snow{}
class Powder extends Snow{}
class Light extends Powder{}
class Heavy extends Powder{}
class Crusty extends Snow{}
class Slush extends Snow{}

书上说snow2是创建不了的,我用的是JDK1.8发现是可以创建的。

四、容器的打印

没啥说的,数组的打印可以用Arrays.toString方法

五、List

List接口在Collection基础上添加了大量方法

  • 基本的ArrayList,访问快,中间插入和移除较慢
  • LinkedList,进行中间的插入删除代价低
    方法找API看

六、迭代器

对于List,add方法是插入元素的方法,get方法是去除元素的方法,但是这是有弊端的,如果使用容器,如果需要对元素进行操作,就必须要针对容器的确切类型进行编程,举个例子,如果要从一个List里取出元素,我们写了一些方法,但如果希望将这些方法再运用到同是Collection类的set类上,就不行了,因为set根本没有get方法。迭代器解决了这个问题。

package tij.hoding;

import java.util.ArrayList;
import java.util.Iterator;

public class Test {
    public static void main(String[] args) {
        ArrayList<String> str_list = new ArrayList<String>();
        for (int i = 0; i < 10; i++)
            str_list.add("123134");
        Iterator<String> it = str_list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        it = str_list.iterator();
        for (int i = 0; i < 5; i++) {
            it.next();
            it.remove();
        }
        System.out.println("----删除后");
        it = str_list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

于是我们可以写一个方法,接受一个collection类型,然后让他调用iterator()方法,这就不需要考虑这个collection是个list还是set了,也就是说,可以将遍历容器的操作与序列底层的结构分离,迭代器统一了对容器类的访问方式。

1.ListIterator

这是个List专用的迭代器,他可以双向移动,可以生成索引,可以使用set方法对元素进行替换,而且在创建的时候还可以指从哪开始迭代,具体看API

七、LinkedList

LinkedList里有很多功能类似的方法,比如peek、element、getFirst,这是因为常以linkedList为基础制作堆栈等数据结构,在这种数据结构中使用push啊pop啊更合适,虽然我觉得有一些= =额= =恩。。。。

八、Stack

没啥好说的,就是用Linkedlist实现,LIFO

九、Set

Set保证集合内元素的唯一性,同时Set也有不同的类型:HashSet使用了散列函数;TreeSet将元素存储在红黑树数据结构中,可以完成元素按比较方法的排序;LinkedHashSet也使用了散列,但它通过使用链表维护了元素插入顺序

十、Map

存储映射关系的数据结构

十一、Queue

FIFO的容器
看API挺简单的

1.PriorityQueue

优先级队列声明下一个弹出的元素是最需要的元素(优先级最高)。当用PriorityQueue上调用offer方法插入一个对象时,这个对象会在队列中被排序,可以使用对象的自然排序也可以提供自己的Comparator来修改排序规则。PriorityQueue可以确保调用各种方法时,根据优先级进行操作。

package tij.hoding;

import java.util.Arrays;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
        Random rand = new Random(47);
        for (int i = 0; i < 10; i++) {
            priorityQueue.offer(rand.nextInt(i + 10));
        }
        printQ(priorityQueue);
        List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3,
                9, 14, 18, 21, 23, 25);
        priorityQueue = new PriorityQueue<Integer>(ints);
        printQ(priorityQueue);
    }
    @SuppressWarnings("rawtypes")
    static void printQ(Queue queue) {
        while (queue.peek() != null) {
            System.out.print(queue.remove() + " ");
        }
        System.out.println();
    }
}

十二、Collection和Iterator

两种遍历的方法,foreach与迭代器方法。
同时有一种默认的AbstractCollection,继承他可以较为简单的将自己的类变成Collection类型而不用实现原Collection全部的方法

十三、Foreach与迭代器

因为有了迭代器,所以有了Foreach功能,这个功能真的很强大

package tij.hoding;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;

public class Test {

    public static void main(String[] args) {
        Collection<String> cs = new LinkedList<String>();
        Collections.addAll(cs, "Take the long way home".split(" "));
        for (String s : cs) {
            System.out.print(s + " ");
        }
    }
}

而这是因为Java se5引入了Iterable几口,实现了Iterable接口的类都可应用于Foreach之中。比如下面

package tij.hoding;

import java.util.Iterator;

public class Test {

    public static void main(String[] args) {
        for (String s : new IterableClass()) {
            System.out.print(s + " ");
        }
    }
}
class IterableClass implements Iterable<String> {
    protected String[] words = "And that is how we know the Earth to be banana-shaped"
            .split(" ");

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index = 0;
            @Override
            public boolean hasNext() {
                return index < words.length;
            }
            @Override
            public String next() {
                return words[index++];
            }
        };
    }

}

首先要搞懂Foreach语句中for(element:container)里的container要是iterable类型的才可以,foreach在遍历时,首先调用container的iterator方法得到这个iterator,然后就是在调用这个iterator的的hasNext和next方法

1.适配器方法惯用法

假如现在有一个类记录了一串儿单词,希望能运用Foreach方法将他遍历,只要让他实现Iterable接口实现Iterator功能就可以了,但是现在希望他既能顺向遍历,又能逆向遍历,这就不好办了,因为你只能重写Iterator一次啊。
一种解决方案就是所谓的适配机器方法,当你有一个接口并需要另一个接口的时候,编写适配器就可以解决问题。

package tij.hoding;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

public class Test {

    public static void main(String[] args) {
        ReversibleArrayList<String> ral = new ReversibleArrayList<String>(
                Arrays.asList("To be or not to be".split(" ")));
        for (String s : ral) {
            System.out.print(s + " ");
        }
        System.out.println();
        for (String s : ral.reversed()) {
            System.out.print(s + " ");
        }
    }
}

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
    public ReversibleArrayList(Collection<T> c) {
        super(c);
    }
    public Iterable<T> reversed() {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return reversed_iterator();
            }

            private Iterator<T> reversed_iterator() {
                return new Iterator<T>() {
                    int current = size() - 1;
                    public boolean hasNext() {
                        return current > -1;
                    }
                    public T next() {
                        return get(current--);
                    }
                };
            }
        };
    }
}

其实这个程序我看了半天才看懂,首先要搞懂Foreach语句中for(element:container)里的container要是iterable类型的才可以,foreach在遍历时,首先调用container的iterator方法得到这个iterator,然后就是在调用这个iterator的的hasNext和next方法。然后再顺序遍历的时候,传入的是ral,然后调用ral自身的iterator本身的hasNext和next方法,就是顺序遍历了;在逆向遍历的时候,传入的是一个新的iterable,得到的也是一个新的iterator,然后调用的就是这个新的iteratoriterator的两个方法。
另外我想了一下next方法里的get方法是谁的,然后用了内部类访问外部的方法也能调用

ReversibleArrayList.this.get()

于是我们就可以弄很多很多不同的遍历规则了,比如随机遍历啊

package tij.hoding;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

public class Test {

    public static void main(String[] args) {
        ReversibleArrayList<String> ral = new ReversibleArrayList<String>(
                Arrays.asList("To be or not to be".split(" ")));
        for (String s : ral) {
            System.out.print(s + " ");
        }
        System.out.println();
        for (String s : ral.reversed()) {
            System.out.print(s + " ");
        }
        System.out.println();
        for (String s : ral.randomized()) {
            System.out.print(s + " ");
        }
    }
}

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
    public ReversibleArrayList(Collection<T> c) {
        super(c);
    }
    public Iterable<T> reversed() {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return reversed_iterator();
            }

            private Iterator<T> reversed_iterator() {
                return new Iterator<T>() {
                    int current = size() - 1;
                    public boolean hasNext() {
                        return current > -1;
                    }
                    public T next() {
                        return ReversibleArrayList.this.get(current--);
                    }
                };
            }
        };
    }
    public Iterable<T> randomized(){
        return new Iterable<T>(){
            @Override
            public Iterator<T> iterator() {
                List<T> shuffle= ReversibleArrayList.this;
                Collections.shuffle(shuffle,new Random(47));
                return shuffle.iterator();
            }
        };
    }
}

可以注意到在随机遍历的时候,实际返回了一个新的序列的iterator这是为了不打乱原始的序列的顺序
附一张简单的容器分类
clipboard.png


z_dominic
115 声望15 粉丝

你有freestyle吗?