浅析java中的集合类(容器)

泛型和类型安全的容器

在javaSE5之前,我们主要考虑的问题就是编译器允许你向容器插入不正确的类型。例如在一个apple对象的容器中,插入了orange。正常情况下,编译器会发出警告,因为没有使用泛型。我们可以使用javaSE5中的注解特性来取消警告。
class Apple{
    private static long counter;
    private final long id = counter++;
    public long id(){
        return id;
    }
}

class Orange{}

public class ApplesAndOrange{
    @SuppressWarnings("unchecked")
    main(String[] args){
        ArrayList apples = new ArrayList();
        for(int i=0;i<3;i++){
            apples.add(new Apple());
            apples.add(new Orange());
        }
        for(int i=0;i<apples.size();i++){
            ((Apple)apples.get(i).id();
        }
    }
}

apple和orange类是有区别的。它们除了都是Object之外没有任何共性(如果一个类没有声明继承自哪个类,那么他们自动继承Object)。因为ArrayList保存的是Object,因此你不仅可以通过add()将Apple放进去,还可以把Orange()放进去,而且无论在编译还是运行过程中,都是正确的。但当你要从ArrayList中使用Apple时,其实得到的是Object引用,必须将其转化为Apple,这时我们就需要用(Apple)来强转。当你想要将Orange转为Apple时,就会出现异常情况了。

class Apple{
    private static long counter;
    private final long id = counter++;
    public long id(){
        return id;
    }
}

class Orange{}

public class ApplesAndOrange{
    @SuppressWarnings("unchecked")
    main(String[] args){
        ArrayList<Apple> apples = new ArrayList<Apple>();
        for(int i=0;i<3;i++){
            apples.add(new Apple());
            //apples.add(new Orange());
        }
        for(int i=0;i<apples.size();i++){
            System.out.println(apples.get(i).id());
        }
        for(Apple c : apples){
            System.out.println(apples.get(i).id());
        }
    }
}

如果这样在将Orange放入ArrayList中,就会出现编译错误,而不是运行错误了。并且我们也不需要进行强转了,因为List知道其是什么类型,在调用get()时会替你执行转换操作。并且不需要索引,可以直接使用foreach语法来选择List中的每个元素。当我们确定了泛型后,我们不仅仅可以放入该确切的对象,还能将其子类添加其中。程序输出的是Object的toString方法,该方法打印类名,后面跟随散列码的无符号十六进制表示(该散列码是通过hashCode()方法产生的)。

基本概念

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念。
1.Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序。
2.Map。一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一个对象关联在一起了;或者被称为“字典”。

通常在编写大部分代码都是在与接口打交道,并且你唯一需要指定所使用的精确类型的地方就是在创建的时候。
List<Apple> apples = new ArrayList<Apple>();
//ArrayList已经被向上转型为List了。使用接口的目的在与如果你决定去修改你的实现,你所需的只是在创建处修改它。
List<Apple> apples = new LinkedList<Apple>();
//因此,你应该创建一个具体类的对象,将其转型为对应的接口,然后在其余的代码中都使用该接口。
但是这种方法并非总能有用,因为某些类具有额外的功能,例如:LinkedList具有List接口中未包含的额外方法,而TreeMap也具有在Map中未包含的方法。如果你需要这些方法,就不能使用向上转型。

Coolection接口概括了序列的概念——一种存放一组对象的方式。
public static void main(String[] args) {
        Collection<Integer> c = new ArrayList<Integer>();
        for(int i = 0; i < 10;i++){
            c.add(i);
        }
        for (Integer i : c) {
            System.out.print(i + ", ");
        }
    }
因为这个示例只使用了Collection方法,因此任何继承自Collection的类的对象都可以正常工作。

添加一组元素

在java.util包中的Arrays和Collections类中都有很多实用方法,可以在一个Collection中添加一组元素。Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表,并将其转为一个List对象。Collections.addAll()方法接受个一Collection对象,以及一个数组或者是一个用逗号分隔的列表,并将元素添加到Collection中。
public static void main(String[] args) {
        //Collection对象 接受了一个12345的数组
        Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
        System.out.println("collection init:" + collection);
        Integer[] moreInts = {6,7,8,9,10};
        //Collection 加入了整个moreInts数组
        collection.addAll(Arrays.asList(moreInts));
        System.out.println("collection addAllIntArr:" + collection);
        //使用Collections 给Collection增加11-15
        Collections.addAll(collection,11,12,13,14,15);
        System.out.println("use Collections add int:" + collection);
        Collections.addAll(collection,moreInts);
        System.out.println("use Collections add moreInts:" + collection);
        List<Integer> list = Arrays.asList(16,17,18,19,20);
        System.out.println("list:" + list);
        list.set(1, 99);
        System.out.println("list change:" + list);
    }
//out put
collection init:[1, 2, 3, 4, 5]
collection addAllIntArr:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
use Collections add int:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
use Collections add moreInts:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10]
list:[16, 17, 18, 19, 20]
list change:[16, 99, 18, 19, 20]
Collection的构造器可以接受可以接受另一个Collection,用它来将自身初始化,因此你可以使用Arrays.List来为这个构造器产生输入。
Collection.addAll()成员方法只能接受另一个Collection对象作为参数,因此它不如Arrays.asList()或Collections.addAll()灵活,这两个方法使用的都是可变参数列表。
我们也可以直接使用Arrays.adList()的输出作为List。但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。如果叫你试图用add或者delete方法,可能在运行时会活的Unsupported Operation。
Arrays.asList()方法的限制是它对所产生的List的类型做出来最理想的假设,而并没有注意它是什么类型。
class Sonw{}
class Power extends Sonw{}
class Light extends Power{}
class Heavy extends Power{}
class Crusty extends Sonw{}
class Slush extends Sonw{}
public class TerminationCondition{
    public static void main(String[] args) {
        List<Sonw> snow1 = Arrays.asList(new Crusty(),new Slush(), new Power());
        List<Sonw> snow2 = Arrays.asList(new Light(),new Heavy());
        List<Sonw> snow3 = new ArrayList<Sonw>();
        Collections.addAll(snow3, new Light(), new Heavy());
        List<Sonw> snow4 = Arrays.<Sonw>asList(new Light(),new Heavy());
    }
}
当试图创建snow2时,Arrays.asList中只有Power类型,因此它会创建Power类型。
正如创建Snow4时,可以在asList之前插入一条“线索”,来告诉编译器Arrays.asList()产生的List类型。
Map更加复杂,并且出来用另一个Map之外,Java的标准类库中没有提供任何自动化初始它们的方式。

容器的打印

你必须使用Arrays.toString()来产生数组的可打印表示,但是容器无需任何帮助。
static Collection fill(Collection<String> collection){
        collection.add("rat");
        collection.add("cat");
        collection.add("dog");
        collection.add("dog");
        return collection;
    }
    static Map fill(Map<String,String> map){
        map.put("rat", "Fuzzy");
        map.put("cat", "Rags");
        map.put("dog", "Bosco");
        map.put("dog", "Spot");
        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>()));
    }
    //output
[rat, cat, dog, dog]
[rat, cat, dog, dog]
[rat, cat, dog]
[cat, dog, rat]
[rat, cat, dog]
{rat=Fuzzy, cat=Rags, dog=Spot}
{cat=Rags, dog=Spot, rat=Fuzzy}
{rat=Fuzzy, cat=Rags, dog=Spot}
这里展示了java类库中的两种主要类型,它们区别在于容器中,每个“槽”保存的元素个数。Collection在每个槽中只能保存一个元素。此类容器包括:List,它以特定的顺序保存一组元素;Set,元素不能重复;Queue,只运行在容器的一端插入对象,并从另一端移除对象。Map在每个槽保存两个对象,即键和值。

各个容器的实现类的区别

ArrayList和LinkedList都属于List类型,它们都是按照被插入的顺序保存元素。不同之处是在执行某些类型操作时性能不一样,而且LinkedList的操作多于ArrayList。下篇将会提及到。
HashSet,TreeSet和LinkedHashSet都属于Set类型,都继承了Set不能有相同元素的特性,但是输出也显示了它们存储元素的方式不同。HashSet使用的是非常复杂的方式来存储元素,目前我们只需要记住这种方式是最快获取元素的方式,因此,存储的顺序看起来并无实际的意义。如果对顺序很关系,那么可以使用TreeSet,他按照比较结果的升序保存对象;或者使用LinkedHashSet,它按照被添加的顺序保存对象。Map的3中对象排列顺序和Set一样,Hash查找最快,Tree是升序,Linked是插入顺序。Map中,key是唯一的,如果使用相同的key来保存数据,那么之前该key对应的值将被替换。

下节会仔细写到每个容器的特性和迭代器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值