持有对象
1 | Collection<Integer> c1 = new ArrayList<Integer>(Arrays.asList(1,2,3,4)); |
与HashSet一样,HashMap也提供了最快的查找技术,也没有按照任何明显的顺序来保存其元素。TreeMap按照比较结果升序保存键,而LinkedHashMap则按照插入顺序保存键,同时保留了HashMap的查询速度。
List
ArrayList 访问快,读写较慢
LinkedList 访问较慢,读写快,特征集比ArrayList大。
方法
- 当使用
remove()
或者removeAll()
方法使用参数是值时,会依赖于equals()方法toArray()
方法可以将任意一个Connlection
转换为一个数组,无参数将返回一个Object[]
,如果你传递目标类型的数据,它将产生指定类型的数据,参数大小不会限制数组长度。
1 | Collection<Integer> c1 = new ArrayList<Integer>(Arrays.asList(1,2,3,4)); |
LinkedList有使其作为 栈、队列、双端队列 的方法
迭代器
迭代器是一个对象,它的工作是遍历并选择系列中对象,客户端程序员不必指定或关系该系列的底层结构。此外,迭代器常常被成为轻量级对象:创建它的代价小。因此,经常可以见到对迭代器有些奇怪的限制,例如,Java的Iterator只能单向移动,这个Iterator只能用于:
- 使用方法iterator()要求返回一个Iterator。Iterator将准备好返回序列的第一个元素。
- 使用next()获得序列中的下一个元素。
- 使用hasNext()检查序列中是否还有元素。
- 使用remove()将迭代器新近返回的元素删除。
ListIterator
ListIterator是一个更加强大的Iteraor的子类型,它只能用于各种List类的访问。尽管Iterator只能向前移动,但是ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问的最后一个元素。
1 | List<String> list = new ArrayList<String>(Arrays.asList("a","b","c")); |
栈
“栈”通常是指“后进先出”(LIFO)
1 | //使用LinkedList能够直接实现栈的所有功能方法,尽管已经有了java.util.Stack。 |
Set
Set不保存重复的元素。查找是Set中最重要的操作,因此你通常都会选择一个HashSet的实现,它专门对快速查找进行了优化。
Set具有与Collection完全一样的接口,因此没有任何额外的功能,只是行为不同。Set是基于对象的值来确定归属性的
HashSet使用的是散列函数
TreeSet使用的是红-黑数
LinkedHashSet因为查询速度原因也使用了散列,链表维护插入顺序
//书中232页写的LinkedHashList我并在jdk8中看见,只有LinkedHashSet和LinkedHashMap,应该是笔误
Map
将对象映射到其它对象的能力是一种解决编程问题的杀手锏。
1 | //统计随机数的次数 |
Queue
队列是一个典型的先进先出(FIFO)的容器。
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。Queue queue = new LinkedList()
PriorityQueue 优先级队列
它可以确保你调用 peek() 、poll()、remove() 方法时,获取的元素将是优先级最高的。
foreach 与 迭代器
Iterator接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterator的类,都可以将它用于foreach语句中。
大量的类都是Iterator的类型,包括所有的Collection类(但是不包括各种Map,但是由entrySet()产生的Map.Entry的元素构成的是Set,keySet()也是返回Set)。
数组到 Iterator 不会把基本类型自动转换成包装类。必须手动转换。
总结
- 容器不能持有基本类型,但是自动包装机制会仔细地执行基本类型到到容器中所持有的包装类型之间的双向转换。
- 新程序中不应该使用过时的Vector、Hashtable、Stack.
- List可以生成listIterator也可以生成Iterator
- 除了TreeSet之外的所有Set都拥有与Colleaction完全一样的接口。
字符串
1 | public class Concatenation{ |
1 | //反编译如下 简化部分注解 |
从反编译可以看到,编译器自动引入了java.lang.StringBuilder类。因为它更加高效。
编译器创建了StringBuilder对象,用以构造String,并为每个字符串调用一次1次StringBuilder的append()方法,总计四次。最后调用toString()方法生成结果,并存为s2(使用的命令是astore_2)
但是不要一味地想着编译器帮你优化就是最好的
1 | public String func1(String[] str){ |
反编译如下
1 | public static java.lang.String func1(java.lang.String[]); |
所以可以看到func1 每次循环都会新建一个StringBuilder,而func2只会新建一个StringBuilder,你还可以在代码中显示的指定其大小,以避免多次重新分配缓冲。(缓冲区(buffer),它是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的。用以缓解冲击。)
如果你向走捷径,例如append(a+":"+c)
,那编译器就会为你创建另一个StringBuilder对象去处理括号内的字符串操作。
Java中每个类从根本上都是继承自Object。因此容器类都有toString()方法,并且重写该方法。所以在重写toString方法时一定要注意编译器的无意识的递归,当你在toString中使用this操作的时候,当你需要转换成String类型时,编译器会自动调用this.toString()方法