第11章 持有对象

  1. Arrays.asList( )方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。但是该List对象的物理实现依然是数组,因此不能对其进行add( )或者delete( )操作,因为数组尺寸不可变。同样,所有对list对象的操作都是在原来数组上进行的。例如,使用Collections.sort( )方法对list对象进行排序,会对原来的数组造成影响:
Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = Arrays.asList(array);
Collections.sort(list1);
System.out.println("list1 after soft: " + list1); // list1 after soft: [1, 2, 3, 5]
System.out.println("array: " + Arrays.toString(array)); // array: [1, 2, 3, 5]

使用List包装后就不会出现上述问题:

Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = new ArrayList<>(Arrays.asList(array));
Collections.sort(list1);
System.out.println("list1 after soft: " + list1); // list1 after soft: [1, 2, 3, 5]
System.out.println("array: " + Arrays.toString(array)); // array: [1, 3, 5, 2]

但是使用一组元素初始化一个Collection的首选方式一般是:首先构建一个空Collection,然后调用Collections.addAll( )方法将数组中所有元素加入。

Integer[] array = {1, 3, 5, 2};
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, array);
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println("list2: " + list2); // list2: [1, 3, 5, 2, 2, 7, 9, 10]
  1. 数组的打印必须调用Arrays.toString( )方法,但是容器的打印无需任何帮助,直接打印就可产生可读性很好的结果。
  2. List中有一个subList( )方法,可以从较大的List中截取一个片断:
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println(list2.subList(1, 4)); // [7, 9, 10]
System.out.println("list2: "+ list2); // list2: [2, 7, 9, 10]

retainAll( )方法由一个List1调用,另一个List2作为参数传入,最终在List1中保留两者的交集,其余元素删除。特别注意判断元素是否相同要依靠equals方法,如果List储存的是我们自己创建的对象,同时又未重写equals方法,那么比较的是两个对象的内存地址。

Integer[] array = {1, 3, 5, 2};
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
Collections.addAll(list1, array);
Collections.addAll(list2, 2, 7, 9, 10);
System.out.println("list1: " + list1);
System.out.println("list2: " + list2);
list2.retainAll(list1);
System.out.println("list2: " + list2);
/*
list1: [1, 3, 5, 2]
list2: [2, 7, 9, 10]
list2: [2]
*/

List中存在一个重载方法addAll( )可以在指定索引处插入Collection,而Collection的addAll( )方法只能将其追加到末尾。

  1. ListIterator是一个更加强大的Iterator的子类型,它只能用于List类的访问。相比于Iterator只能向前移动,它可以双向移动。
    可以向前遍历:
public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>(5);
        List<Integer> list2 = new ArrayList<>(5);
        for (int i = 0; i < 5; i++) {
            list1.add(i);
        }
        System.out.println("list1:" + list1); // list1:[0, 1, 2, 3, 4]
        ListIterator<Integer> it = list1.listIterator(5); // 将初始指针置于末尾
        while (it.hasPrevious()) {
            list2.add(it.previous());
            it.set(0); // 调用set()方法替换当前指针访问的值
        }
        System.out.println("list2:" + list2); // list2:[4, 3, 2, 1, 0]
        System.out.println("list1:" + list1); // list1:[0, 0, 0, 0, 0]
    }
}
  1. 使用字符串调用charAt(index)方法可返回索引为index处的字符。
  2. Map使用get(key)方法返回key所对应的value,若容器中不存在该key,则返回null。
  3. LinkedList提供了方法支持队列的行为,并且实现了Queue接口,因此它可以作为Queue使用。
Queue<Integer> queue = new LinkedList<>();

LinkedList提供的Queue相关的方法有:

queue.offer(1); // 将一个元素插入队尾,或者返回false
queue.peek(); // 在不移除的情况下返回队头,队列为空返回null
queue.element(); // 在不移除的情况下返回队头,队列为空抛出NoSuchElementException异常
queue.poll(); // 移除并返回队头,队列为空返回null
queue.remove(); // 移除并返回队头,队列为空抛出NoSuchElementException异常
  1. 到目前为止,foreach语法主要用于数组,但也可以用于任何Collection对象。事实上,只要是实现了Iterable接口的类,都可以使用foreach进行遍历。
class IterableClass implements Iterable<String> {
    private String[] words = "I am a Chinese I love China !".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++];
            }
        };
    }

    public static void main(String[] args) {
        IterableClass iterableClass = new IterableClass();
        for (String s : iterableClass) {
            System.out.print(s + " "); // I am a Chinese I love China ! 
        }
    }
}

上面为IterableClass类实现了Iterable接口,并重写Iterator方法返回了一个前向迭代器用于foreach,下面为其添加一个反向迭代器,使得foreach可以反向输出。

class IterableClass implements Iterable<String> {
    private String[] words = "I am a Chinese I love China !".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++];
            }
        };
    }

	// 反向迭代器,使用Iterable包装。
    public Iterable<String> reverse() {
        return new Iterable<String>() {
            @Override
            public Iterator<String> iterator() {
                return new Iterator<String>() {
                    int index = words.length - 1;

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

                    @Override
                    public String next() {
                        return words[index--];
                    }
                };
            }
        };
    }

    public static void main(String[] args) {
        IterableClass iterableClass = new IterableClass();
        for (String s : iterableClass) {
            System.out.print(s + " "); // I am a Chinese I love China ! 
        }

        System.out.println();

        for (String s : iterableClass.reverse()) {
            System.out.print(s + " "); // ! China love I Chinese a am I 
        }
    }
}

我们看到Iterator其实已经有很多处理集合元素相关的方法了,为什么还需要抽象一层呢?很多集合不直接实现Iterator接口,而是实现Iterable?
因为Iterator接口的核心方法next()或者hasNext() 是依赖于迭代器的当前迭代位置的。 如果Collection直接实现Iterator接口,势必导致集合对象中包含当前迭代位置的数据(指针)。当集合在不同方法间被传递时,由于当前迭代位置不可预置,那么next()方法的结果会变成不可预知。除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。
但即使这样,Collection也只能同时存在一个当前迭代位置。
而Iterable则不然,每次调用都会返回一个从头开始计数的迭代器。多个迭代器是互不干扰的。
另外,这种选择也是解耦合的一种,我们可以将集合实例对象与具体的遍历方法解耦,实例对象只需调用Iterator方法返回一个Iterator对象,其他的事交给这个对象即可。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值