Java编程思想读书笔记——持有对象

第十一章 持有对象

集合类:对象类型为List、Set、Queue和Map的类型。

11.1 泛型和类型安全的容器

使用预定义的泛型容器可以保存特定类型的对象,无论存取都不必关心类型转换。

该类型的子类对象也可以通过向上转型,存入到泛型容器中。

11.2 基本概念

Java容器类可以划分成两类:Collection与Map
1) Collection:一个独立元素的队列,这些元素都服从一条或多条规则。这一类又可以分为List、Set和Queue三个分类。
2) Map:一组成对的键值对对象,允许使用键查找值,也被称为字典或者关联数组。

通常情况下,在创建一个具体类对象时,会将其向上转型成为对应的接口,如:

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

但是如果需要使用到具体类的特有方法时,就无需向上转型,直接创建具体类对象即可。

Collection接口概括了序列的概念,而序列是一种存放一组对象的方式。该接口有add()方法,表示将一个新元素放置到Collection中。

11.3 添加一组元素

创建Collection对象的方式:
1) 直接创建实现Collection接口的具体类对象,如ArrayList等;

a) 可以不接受参数,然后使用for循环逐个元素添加,也可以使用addAll()方法,将一组元素直接添加;
b) 可以接受一个Collection对象作为参数,将该Collection对象的中所有元素存入新创建的Collection对象中。

2) 使用其他方法

a) 使用Arrays.asList(),该方法接受一个数组或者可变参数列表,并将之转换为List对象,需要注意的是,此种方式获得的List对象,由于底层实现仍然是数组,在添加或者删除元素时会出现UnsupportedOperationException异常。
b) 使用Collections.addAll(),这一方法可以接收一个Collection对象、一个数组或者是可变参数列表作为参数,得到新的Collection对象;
c) 使用Collection.addAll(),这一方法与上一种不同的是,它只能接受Collection对象作为参数,灵活性受到限制。
package com.mzm.chapter11;

import java.util.*;

/**
 * 
 */
public class AddingGroups {

    public static void main(String[] args){
        Collection<Integer> collection = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        Integer[] moreInts = {6, 7, 8, 9, 10};
        collection.addAll(Arrays.asList(moreInts));

        Collections.addAll(collection, 11, 12, 13, 14, 15);
        Collections.addAll(collection, moreInts);

        List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
        list.set(1, 99);
        //产生UnsupportedOperationException
        //list.add(21);
    }
}

之后的显示类型参数说明可能在之后的JDK版本中被修正了,没有书中说的运行结果。

10.4 容器的打印

Collection的toString()方法,以”[“开头,以”]”结尾,每个元素之间用”,”分隔;
Map的toString()方法,以”[“开头,以”]”结尾,每一对键值对之间,以”,”分隔,针对每一对键值对,键在前,值在后,中间使用”=”分隔。

10.5 List
addAll(int index, List<T> subList):在List的索引为index的位置,插入subList的所有元素;
contains(T t):判断某个对象是否在List中;
remove(T t):移除某个对象;
remove(int index):移除List中索引为index的元素;
removeAll():移除List中的全部元素;
indexOf(T t):获取某个对象在List中的索引;
add(int index, T t):在List中间插入元素,该操作消耗性能;
subList(int startIndex, int endIndex):获取子List,包头不包尾,本质是创建一个新的List;
containsAll(List<T> subList):List中是否包含subList中的全部元素;
retainAll(List<T> otherList):List与otherList的交集,注意该操作不创建新的List,而是在原来的List上,将不在otherList中的元素删除;
set(int index, T t):将List中索引为index的元素设置为t;
isEmpty():判断List长度是否为0;
clear():将List的所有元素清空;
toArray():将List转换为数组,注意是返回Object数组;
toArray(T list.get(i)):将List转换为数组,但返回的是类型与List的类型参数相同的数组;
11.6 迭代器

不考虑容器类型,对容器插入元素并将它们再次取回。
迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员无需关心该序列的底层实现。
迭代器也被称为轻量级对象,因为其创建代价小。

Java中的Iterator,它只能单向移动,具有如下方法:

iterator():返回一个Iterator对象,它将准备好返回序列的第一个元素;
next():返回序列的下一个元素;
hasNext():判断序列是否含有下一个元素;
remove():将迭代器新近返回的元素删除;

迭代器将遍历序列的操作与序列底层的结构分离,统一了对容器的访问方式。

11.6.1 ListIterator

ListIterator是Iterator的一个子类,只能用于各种List的访问。ListIterator可以双向移动,可以产生当前位置的前一个和后一个元素的索引,并且可以使用set()方法,将最近访问过的元素进行替换。此外,还可以通过listIterator(int index)的方法,获得一个一开始就指向index位置的ListIterator。

11.7 LinkedList

LinkedList的增删为O(1),对元素的随机访问为O(n);ArrayList的增删为O(n),对元素的随机访问为O(1)。

getFirst()/element()/peek():获取第一个元素,poll()在列表为空时返回null,另外两个抛出NoSuchElementException异常;  removeFirst()/remove()/poll():移除并返回第一个元素,差异与上面类似;
addFirst()/add()/offer()/addLast():将某个元素添加的末尾;
removeLast():移除并返回最后一个元素。
11.8 Stack

栈是指先进后出的容器,也被称为叠加栈,最典型的例子是自助餐托盘。
可以使用LinkedList来实现:

package com.mzm.chapter11;

import java.util.LinkedList;

/**
 * 
 */
public class Stack<T> {

    private LinkedList<T> storage = new LinkedList<>();

    public void push(T v){
        storage.addFirst(v);
    }

    public T peek(){
        return storage.getFirst();
    }

    public T pop(){
        return storage.removeFirst();
    }

    public boolean empty(){
        return storage.isEmpty();
    }

    public String toString(){
        return storage.toString();
    }
}
11.9 Set

Set不保存重复元素。
查找是Set最重要的操作,一般是使用HashSet。

HashSet、TreeSet和LinkedHashSet的区别在于,HashSet使用散列函数来维护元素的顺序,TreeSet是将元素存储在红黑树数据结构中,LinkedHashSet使用链表来维护元素的插入顺序。

方法包括:add()、addAll()、contains()、addAll()、containsAll()、remove()、removeAll()。
11.10 Map

Map是将对象映射到另一个对象。

11.11 Queue

队列是一个先进先出的容器。
LinkedList提供了方法以支持队列的行为,并且实现了Queue接口,所以可以使用Queue引用指向LinkedList对象。

11.11.1 PriorityQueue

队列规则:在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则。
先进先出声明的是下一个元素应该是等待时间最长的元素。
优先级队列声明下一个弹出元素是最需要的元素(具有最高优先级)。
PriorityQueue在调用offer()方法插入元素时,默认是根据自然顺序排列的,可以传入自定义的Comparator来修改顺序。

package com.mzm.chapter11;

import java.util.*;

/**
 * Created by 蒙卓明 on 2017/10/14.
 */
public class PriorityQueueDemo {

    public static void main(String[] args){
        //默认是升序排列
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        Random rand = new Random(47);
        for(int i = 0; i < 10; i++){
            priorityQueue.offer(rand.nextInt(i + 10));
        }
        QueueDemo.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<>(ints);
        QueueDemo.printQ(priorityQueue);

        //指定降序
        priorityQueue = new PriorityQueue<>(ints.size(), Collections.reverseOrder());
        priorityQueue.addAll(ints);
        QueueDemo.printQ(priorityQueue);

        String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
        List<String> strings = Arrays.asList(fact.split(""));
        PriorityQueue<String> stringPQ = new PriorityQueue<>(strings);
        QueueDemo.printQ(stringPQ);

        //指定降序
        stringPQ = new PriorityQueue<>(strings.size(), Collections.reverseOrder());
        stringPQ.addAll(strings);
        QueueDemo.printQ(stringPQ);

        Set<Character> charSet = new HashSet<>();
        for(char c : fact.toCharArray()){
            charSet.add(c);
        }
        PriorityQueue<Character> characterPQ = new PriorityQueue<>();
        QueueDemo.printQ(characterPQ);
    }
}
11.12 Collection和Iterator

如果要针对所有Collection类型提供共同的方法,最好是通过迭代器的方式。实现Collection接口,需要实现该接口的所有方法,较为麻烦,虽然AbstractCollection提供了Collection的默认实现,即使是继承该类,也必须提供迭代器的实现,何况已经继承了其他类的情况,所以最好是使用迭代器。

package com.mzm.chapter11;

import java.util.*;

/**
 * Iterator比Collection要好一些
 * 
 */
public class InterfaceVsIterator {

    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 display(Collection<Pet> pets){
        for(Pet p : pets){
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
    }

    public static void main(String[] args){
        List<Pet> petList = Pet.arrayList(8);
        Set<Pet> petSet = new HashSet<>(petList);
        Map<String, Pet> petMap = new LinkedHashMap<>();
        String[] names = {"Ralph", "Eric", "Robin", "Lacey", "Britney", "Sam", "Spot", "Fluffy"};
        for(int i = 0; i < 8; i++){
            petMap.put(names[i], petList.get(i));
        }

        display(petList);
        display(petSet);
        display(petList.iterator());
        display(petSet.iterator());

        System.out.println(petMap);
        System.out.println(petMap.keySet());
        display(petMap.values());
        display(petMap.values().iterator());
    }
}
package com.mzm.chapter11;

import java.util.AbstractCollection;
import java.util.Iterator;

/**
 * AbstractCollection提供了Collection的默认实现,但即使继承该类,也同样要实现iterator()方法来提供迭代器
 * 不如直接使用迭代器
 * 
 */
public class CollectionSequence extends AbstractCollection<Pet> {

    private Pet[] pets = Pet.arrayList(8).toArray(new Pet[0]);

    @Override
    public Iterator<Pet> iterator() {
        return new Iterator<Pet>() {

            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < pets.length;
            }

            @Override
            public Pet next() {
                return pets[index++];
            }

            public void remove(){
                throw new UnsupportedOperationException();
            }
        };
    }

    @Override
    public int size() {
        return pets.length;
    }

    public static void main(String[] args){
        CollectionSequence c = new CollectionSequence();
        InterfaceVsIterator.display(c);
        InterfaceVsIterator.display(c.iterator());
    }
}
package com.mzm.chapter11;

import java.util.Iterator;

/**
 * 当这个类继承了其他类时,不能再继承AbstractCollection了
 * 
 */
public class NonCollectionSequence extends PetSequence{

    public Iterator<Pet> iterator(){
        return new Iterator<Pet>() {

            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < pets.length;
            }

            @Override
            public Pet next() {
                return pets[index++];
            }

            public void remove(){
                throw new UnsupportedOperationException();
            }
        };
    }
}

class PetSequence{
    protected Pet[] pets = Pet.arrayList(8).toArray(new Pet[0]);
}
11.13 Foreach与迭代器

所有的Collection都能使用foreach语法遍历,因为其实现了Iterable接口,该接口包含一个能够产生Iterator的iterator()的方法。

注意数组没有实现Iterable接口。

11.13.1 适配器方法惯用法

如果需要在foreach中进行反序遍历,可以使用适配器设计模式。

package com.mzm.chapter11;

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

/**
 * 使用适配器设计模式在foreach中添加反向遍历
 * 
 */
public class AdapterMethodIdiom {

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

        //反向遍历
        for(String s : ral.reverse()){
            System.out.print(s + " ");
        }
    }
}

class ReversibleArrayList<T> extends ArrayList<T>{

    public ReversibleArrayList(Collection<T> c){
        super(c);
    }

    public Iterable<T> reverse(){

        return new Iterable<T>() {

            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>() {

                    int current = size() - 1;

                    @Override
                    public boolean hasNext() {
                        return current > -1;
                    }

                    @Override
                    public T next() {
                        return get(current--);
                    }

                    public void remove(){
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值