11 持有对象

持有对象

为什么需要容器

有时候需要在程序运行的时候创建对象,这时候就无法通过一开始去new了,因此需要一个容器来动态的去保存在运行时候创建的对象,array类型是我们熟知的容器,数组将数字与对象联系起来:它保存类型明确的对象,查询对象时,不需要对结果做类型转换;它可以是多维的,可以保存基本类型的数据;但是,数组一旦生成,其容量不能改变。因此JAVA使用更多的集合类来处理这种情况。

添加一组元素

一般都是使用add方法向容器中一个一个的添加元素

如何一次性添加多个元素?

Collection(父类)中提供了接受其他容器来初始化它的构造器,只是这样的速度比较慢。
Collection(父类)还有一个addAll的方法,同样也是接受其他容器的参数一次性添加。

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

上面两种方法都是接受另一个容器对象,可能我希望还可以参数是数组,甚至是 一串以逗号分开的数字。

那么如何将这串数组,或者数组转为容器呢?

util包里有一个Arrays的工具包,它有一个asList的方法,可以将数组,或者一串以逗号分开的数字,转成列表:

1
2
3
Integer[] moreInts = { 6, 7, 8, 9, 10 };
Arrays.asList(moreInts);
Arrays.asList(16, 17, 18, 19, 20);

还有一个Conllections的工具类,可以进一步的减少添加一组数组对象的中间过程:

1
2
3
4
Collection<Integer> collection = new ArrayList<Integer>();
Integer[] moreInts = { 6, 7, 8, 9, 10 };
Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);

推荐使用Collections来添加元素,为什么?

使用Collections結合空容器添加元素是最好的办法,因为直接使用Arrays.asList会有两个问题:

  1. 使用Arrays.asList的时候,需要注意一个问题:它转换后的容器是可以修改元素值,但是不能修改元素的长度的,为啥?
    原因是Arrays.asList转换的容器基本底层形式是数组,它不能调整大小。
    并且如果这个容器的元素和顺序被修改,直接会影响到原数组。

  2. 使用Arrays.asList的时候,还需要注意另外的一个问题:它会对列表的结果类型进行最佳猜测,不管我们指定它是怎样的类型,如果有冲突,会报错。
    案例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Snow {}
    class Powder extends Snow {}
    class Light extends Powder {}
    class Heavy extends Powder {}
    class Crusty extends Snow {}
    class Slush extends Snow {}

    List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());//编译报错
    List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());//编译成功

如何使用Collections防止出现这个问题?
直接上源码:

1
2
List<Snow> snow3 = new ArrayList<Snow>();     
Collections.addAll(snow3, new Light(), new Heavy());

输入容器

在打印数组的使用,会打印数组的一个引用,因此需要使用Arrays.toString方法先将数组转为字符串。

容器呢?

抱歉,让你失望了。
容器很好的重写了toString方法。
因此直接打印即可输出里面的元素。

Map

HashMap:快速访问
TreeMap:“键”保持插入顺序
LinkedMap:保持插入顺序,并且提供快速访问。

迭代器

使用ListIterator可以实现双向迭代,需要注意的一点是迭代器的指向是两个元素之前的位置。

栈是描述后进先出的,栈是用Linklist实现的。

队列

队列描述的是先进先出的元素弹出规则。
队列在并发编程里很重要,并且LinkedList实现了Quene接口。

优先级队列描述的是按优先级的元素弹出规则

如何定义这个优先级?
结合Comparator来定义,有一个构造器如下:

1
PriorityQueue(int initialCapacity, Comparator<? super E> comparator);

Collection和Iterator

Collection是什么?

Collection接口是所有容器的共有接口,这个接口的表示是一个可以持有对象的容器。
他有一个AbstractCollection的子类,实现了Collection的绝大部分方法,
Iterator接口是迭代器模式,表示支持迭代。

Collection支持迭代吗?

1
public interface Collection<E> extends Iterable<E> {}

由上可见,是支持的。

为什么实现Iterable就支持迭代?

因为Iterable接口中有一个返回Iterator接口的方法:

1
Iterator<T> iterator();

Iterator接口有何用?

这个接口中定义了next(),hasNext(),remove()三种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Iterator<E> {
boolean hasNext();

E next();

default void remove() {
throw new UnsupportedOperationException("remove");
}

default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

这是一种迭代器模式,可以让所有实现该接口的对象有一种遍历自己内部“元素”而无需暴露具体实现细节的方法。

为什么Collection不实现Iterator,而是实现Iterable?

Iterable是Iterator的一个封装,目前看来有两个好处:

  1. Iterator中是由“指针”来指向迭代的元素,这样的一个问题是,我下一次一个新的迭代无法从头开始。
  2. Iterable可以使用foreach操作来简化。

如何让新对象支持迭代?

要么实现Collection,要么实现Iterable。
不建议实现Collection,因为他需要实现的方法较多,可以继承AbstractCollection:只是还是需要实现size()和iterator()两个方法。

foreach和迭代器

什么情况下可以使用foreach?

实现了Iterable接口就可以。
基本上所有的Collection都可以,除了Map之外。

那Map如何迭代?

1
2
3
4
5
6
7
public class EnvironmentVariables {   
public static void main(String[] args) {
for(Map.Entry entry: map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}

entrySet获得Entry类型的Set,然后遍历这个Set即可。

foreach的迭代规则是如何的?

foreach()实际上就是:

1
2
3
for(Iterable i;i.hasNext();){
Object o = i.next();
}

因此,规则是由Iterable中的iterator()来制定的。

如何制定多个规则?

分析一下这个问题的本质:

  1. 这个类是现有的,我们不希望去更改它的源代码
  2. 本身有可能已经存在了一个规则,这时候不希望覆盖它。
  3. 实现的接口名和方法名一样
  4. 多个规则表示多重继承(接口)

1点指明需要使用适配模式,使用继承方法的适配模式即可。
2,3,4点都指明应该使用内部类,更好的是匿名内部类。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ReversibleArrayList<T> extends ArrayList<T> {   
public ReversibleArrayList(Collection<T> c) {
super(c);
}
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1;
public boolean hasNext() {
return current > -1;
}
public T next() {
return get(current--);
}
public void remove() {
// Not implemented
//throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral =
new ReversibleArrayList<String>(Arrays.asList("To be or not to be".split(" ")));
// Grabs the ordinary iterator via iterator():
for(String s : ral)
System.out.print(s + " ");
System.out.println();
// Hand it the Iterable of your choice
for(String s : ral.reversed())
System.out.print(s + " ");
}
}
/* Output:
To be or not to be
be to not or be To
*///:~

本文标题:11 持有对象

文章作者:Sun

发布时间:2018年10月23日 - 00:10

最后更新:2018年12月12日 - 16:12

原始链接:https://sunyi720.github.io/2018/10/23/THING IN JAVA/11 持有对象/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。