Java基础:持有对象

来源:互联网 发布:东方财富dk指标源码 编辑:程序博客网 时间:2024/05/18 00:57

前言

通常,程序总是根据运行时才知道的某些条件去创建新对象。在此之前,不会知道所需对象的数量,甚至不知道确切的类型。为了解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。

正题

Java使用类库提供了一套相当完善的容器类,其中基本的类型是List、Set、Queue和Map。这些对象类型也称为集合类,但由于Java的类库使用了Collection这个名字来指定该类库的一个特殊子集,容器提供了完善的方法来保存对象,可以使用这些工具来解决数量惊人的问题。

Java容器类库的用途是“保存对象”,并将其划分为两个不同的概念:

1)Collection。一个独立元素的序列,这些元素服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。

2)Map。一组成对的“键值对”对象,允许你使用键来查找值。映射表允许我们使用另一个对象来查找某个对象,它被称为“关联数组”,因为它将某些对象与另外一些对象关联在了一起,或者被称为“字典”,因为你使用键对象来查找值对象,就像在字典中使用单词来定义一样。Map是强大的编程工具。

Collection接口概括了序列的概念——一种存放一组对象的方式。

添加一组元素

在java.util包中的Arrays和Collections类中都有很多实用的 方法,可以在一个Collection中添加一组元素。Arrays.asList()方法接受一个数组或一个用逗号分隔的元素列表(使用可变参数),并将其转化为一个List对象,将元素添加到Collection中。

public class AddGroup {public static void main(String[] args) {Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));Integer[] moreInt = {6,7,8};collection.addAll(Arrays.asList(moreInt));}}
Collection的构造器可以接受另一个Collection,用它来将自身初始化,因此你可以使用Arrays.List来为这个构造器产生输入。你也可以直接使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。

List

List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。有两种类型的List:

1)基本的ArrayList,它擅长随机访问元素,但是在List的中间插入和移除元素是比较慢。

2)LinkedList,它通过代价较低的在List中间进行的插入和删除的操作,提供了优化的顺序访问。LinkedList在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

LinkedList

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(在List中间插入和删除)时比ArrayList更高效,但在随机访问操作方面却要逊色一些。

LinkedList还添加了可以使其用作栈、队列和双端队列的方法。

public class AddGroup {public static void main(String[] args) {LinkedList<Integer> linkedList = new LinkedList<Integer>(Arrays.asList(1,2,3,4,5,6));System.out.println(linkedList.getFirst());System.out.println(linkedList.element());System.out.println(linkedList.removeFirst());}}
这些方法之中有些彼此间只是名称的差异或者只存在些许差异,以使得这些名字在特定用法的上下文环境中更加适用。例如,getFirst()和element()完全一样,他们都返回列表头(第一个元素),而并不移除它。

Stack

“栈”通常是指“后进先出”(LIFO)容器。有时栈也被称为叠加栈,因为最后“压入”栈的元素,第一个“弹出”栈,经常用来类比栈的事物是装有弹簧的储放器中的自助餐托盘,最后放入的托盘总是最先拿出来使用的。

LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。

Set

Set  不保存重复的元素。如果你试图将相同对象的多个实例添加到Set中,那么它就会阻止这种重复现象。Set最常被使用的是测试归属性,你可以很容易的询问某个对象是否在某个Set中。正因如此,查找就成为Set最重要的操作,因此你通常都会选择一个HashSet的实现,它专门对快速查找进行了优化。

Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只是行为不同。

HashSet使用了散列,HashSet所维护的顺序与TreeSet或LinkedHashSet都不同,因为它们的实现具有不同的元素存储方式。TreeSet将元素存储在红—黑树数据结构中,而HashSet使用的是散列函数。LinkedHashSet因为查询速度的原因也使用了散列,但是看起来它使用了链表来维护元素的插入顺序。

public class AddGroup {public static void main(String[] args) {Random random = new Random(30);TreeSet<Integer> treeSet = new TreeSet<Integer>();HashSet<Integer> hashSet = new HashSet<Integer>();LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<Integer>();for(int i = 0; i < 15; i++){int t = random.nextInt(30);treeSet.add(t);hashSet.add(t);linkedHashSet.add(t);}System.out.println(treeSet);System.out.println(hashSet);System.out.println(linkedHashSet);}}
输出结果:

[3, 4, 5, 8, 14, 16, 17, 18, 21, 22, 26][17, 16, 3, 18, 21, 4, 5, 22, 8, 26, 14][26, 18, 5, 4, 16, 8, 22, 17, 21, 14, 3]
如果想对结果排序,一种方式是使用TreeSet来替代HashSet;

Map

将对象映射到其他对象的能力是一种解决编程问题的杀手锏。

Queue

队列是一个典型的先进先出(FIFO)的容器,即从容器的一端放入事物,从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。

LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现,通过将LinkedList向上转型为Queue。

public class QueueDemo {public static void main(String[] args) {Queue<Character> queue = new LinkedList<Character>();for(char c : "China".toCharArray()){queue.offer(c);}System.out.println(queue);}}
输出结果:[C, h, i, n, a]

PriorityQueue

先进先出描述了最典型的队列规则。队列规则是指在给定一组队列的元素的情况下,确定下一个弹出队列的元素的规则。先进先出声明的是下一个元素应该是等待时间最长的元素。

优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。PriorityQueue添加到Java SE5中,是为了提供这种行为的一种自动实现。

当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。PriorityQueue可以确保当你调用peek()、pool()和remove()方法时,获取元素将是队列中优先级最高的元素。

public class PriorityQueueDemo {public static void main(String[] args) {List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1,1, 2, 3, 9, 14, 18, 21, 23, 25);PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>(ints);}}

输出:[1, 1, 3, 18, 2, 9, 18, 23, 22, 14, 3, 9, 14, 20, 21, 25, 25]

可以看到,重复是允许的,最小的值拥有最高的优先级。

迭代器

任何容器类,都必须有某种方式可以插入元素并将它们再次取回。毕竟,持有事物是容器最基本的工作。对于List,add()是插入元素的方法之一,而get()是取出元素的方法之一。

迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。因此,经常可以见到对迭代器有些奇怪的限制;例如,Java的Iterator只能单向移动,这个Iterator只能用来:

1)使用方法Iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。

2)使用next()获取序列中的下一个元素。

3)使用hasNext()检查序列中是否还有元素。

4)使用remove将迭代器新近返回的元素删除。

public class SimpleIterator {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>(Arrays.asList(12, 3, 7, 28));Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {Integer i = iterator.next();}}}
ListIterator

ListIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问。尽管Iterator只能向前移动,但是ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素索引,并且可以使用set()方法替换它访问过的最后一个元素。你可以通过调用ListIterator()方法产生一个指向List开始处的ListIterator,并且还可以通过listIterator(n)方法创建一个一开始就指向列表索引为n元素处的ListIterator。

public class ListIteration {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>(Arrays.asList(12, 3, 7, 28));ListIterator<Integer> it = list.listIterator();while (it.hasNext()) {System.out.println(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex());}it = list.listIterator(1);while (it.hasNext()) {System.out.println(it.next());}}}
输出结果:

12, 1, 03, 2, 17, 3, 228, 4, 33728
ForEach与迭代器

到目前为止,foreach语法主要用于数组,但是它也可以应用于任何Collection对象。你实际上已经看到过很多实用ArrayList时用到它的实例:

public class ForeachCollection {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 + "' ");}}}
输出结果:'Take' 'the' 'long' 'way' 'home'

由于cs是一个Collection,所以这段代码能够与foreach一起工作是所有Collection对象的特性。之所以能够工作,是因为Java SE5引入了新的被称为Iterable的接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterable的类,都可以将它用于foreach语句中。

public class IterableClass implements Iterable<String> {private String[] words = ("welcome to china").split(" ");@Overridepublic Iterator<String> iterator() {return new Iterator<String>() {private int index = 0;@Overridepublic void remove() { }@Overridepublic String next() {return words[index++];}@Overridepublic boolean hasNext() {return index < words.length;}};}public static void main(String[] args) {for(String s : new IterableClass()){System.out.print(s + " ");}}}

main字节码:

  public static void main(java.lang.String[]);    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=3, locals=3, args_size=1         0: new           #8                  // class com/access/IterableClass         3: dup         4: invokespecial #9                  // Method "<init>":()V         7: invokevirtual #10                 // Method iterator:()Ljava/util/Iterator;        10: astore_1        11: aload_1        12: invokeinterface #11,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z        17: ifeq          58        20: aload_1        21: invokeinterface #12,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;        26: checkcast     #13                 // class java/lang/String        29: astore_2        30: getstatic     #14                 // Field java/lang/System.out:Ljava/io/PrintStream;        33: new           #15                 // class java/lang/StringBuilder        36: dup        37: invokespecial #16                 // Method java/lang/StringBuilder."<init>":()V        40: aload_2        41: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;        44: ldc           #4                  // String        46: invokevirtual #17                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;        49: invokevirtual #18                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;        52: invokevirtual #19                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V        55: goto          11        58: return

从字节码可以看出最终会把foreach转化为Iterator对象方法的调用。

iterator()方法返回的是实现了Iterator<String>的匿名内部类的实现,该匿名内部类可以遍历数组中的所有单词。

在Java SE5中,大量类都是Iterable类型,主要包括所有的Collection类(但是不包括各种Map)。


下面的实例展示了各种不同的类在方法上的差异:

public class ComtainerMethodDifferences {    private static Set<String> object = methodSet(Object.class);    static {        object.add("clone");    }    public static Set<String> methodSet(Class<?> type)    {        TreeSet<String> set = new TreeSet<>();        for (Method method : type.getMethods())        {            set.add(method.getName());        }        return set;    }    public static Set<String> difference(Class<?> superSet, Class<?> subSet)    {        Set<String> set = new HashSet<>(methodSet(superSet));        set.removeAll(methodSet(subSet));        set.removeAll(object);        System.out.println(superSet.getSimpleName() + " extends " + subSet.getSimpleName() + " adds " + set);        interfaces(superSet);        return set;    }    public static List<String> interfaces(Class<?> type)    {        List<String> set = new ArrayList<>();        for (Class<?> clazz : type.getInterfaces())        {            set.add(clazz.getSimpleName());        }        System.out.println("interfaces in " + type.getSimpleName() + ":" + set);        return set;    }    public static void main(String args[]) {        System.out.println("Collection : " + methodSet(Collection.class));        difference(Set.class, Collection.class);        difference(HashSet.class, Set.class);        difference(LinkedHashSet.class, HashSet.class);        difference(TreeSet.class, Set.class);        difference(List.class,Collection.class);        difference(LinkedList.class,List.class);        difference(ArrayList.class,List.class);        difference(Queue.class,Collection.class);        difference(PriorityQueue.class,Queue.class);        System.out.println("Map : " + methodSet(Map.class));        difference(HashMap.class,Map.class);        difference(LinkedHashMap.class,HashMap.class);        difference(TreeMap.class,Map.class);    }}

输出结果:

Collection : [add, addAll, clear, contains, containsAll, equals, forEach, hashCode, isEmpty, iterator, parallelStream, remove, removeAll, removeIf, retainAll, size, spliterator, stream, toArray]Set extends Collection adds []interfaces in Set:[Collection]HashSet extends Set adds []interfaces in HashSet:[Set, Cloneable, Serializable]LinkedHashSet extends HashSet adds []interfaces in LinkedHashSet:[Set, Cloneable, Serializable]TreeSet extends Set adds [headSet, descendingIterator, descendingSet, pollLast, subSet, floor, tailSet, ceiling, last, lower, comparator, pollFirst, first, higher]interfaces in TreeSet:[NavigableSet, Cloneable, Serializable]List extends Collection adds [replaceAll, get, indexOf, subList, set, sort, lastIndexOf, listIterator]interfaces in List:[Collection]LinkedList extends List adds [offerFirst, poll, getLast, offer, getFirst, removeFirst, element, removeLastOccurrence, peekFirst, peekLast, push, pollFirst, removeFirstOccurrence, descendingIterator, pollLast, removeLast, pop, addLast, peek, offerLast, addFirst]interfaces in LinkedList:[List, Deque, Cloneable, Serializable]ArrayList extends List adds [trimToSize, ensureCapacity]interfaces in ArrayList:[List, RandomAccess, Cloneable, Serializable]Queue extends Collection adds [poll, peek, offer, element]interfaces in Queue:[Collection]PriorityQueue extends Queue adds [comparator]interfaces in PriorityQueue:[Serializable]Map : [clear, compute, computeIfAbsent, computeIfPresent, containsKey, containsValue, entrySet, equals, forEach, get, getOrDefault, hashCode, isEmpty, keySet, merge, put, putAll, putIfAbsent, remove, replace, replaceAll, size, values]HashMap extends Map adds []interfaces in HashMap:[Map, Cloneable, Serializable]LinkedHashMap extends HashMap adds []interfaces in LinkedHashMap:[Map]TreeMap extends Map adds [descendingKeySet, navigableKeySet, higherEntry, higherKey, floorKey, subMap, ceilingKey, pollLastEntry, firstKey, lowerKey, headMap, tailMap, lowerEntry, ceilingEntry, descendingMap, pollFirstEntry, lastKey, firstEntry, floorEntry, comparator, lastEntry]interfaces in TreeMap:[NavigableMap, Cloneable, Serializable]

可以看到除了TreeSet之外所有Set都拥有与Collection完全一样的接口。List和Collection存在着明显的不同,尽管List所要求的方法都在Collection中。另一方面,在Queue接口中的方法都是独立地;在创建具有Queue功能实现时,不需要使用Collection方法。最后Map和Collection之间的唯一重叠就是Map可以使用entrySet()和values()方法来产生Collection。

原创粉丝点击