容器类
在《java编程思想》一书中,容器类本是在持有对象那一章节里面的,这里我特意给提出来了,因为内容代码比较多,与其放一起显得太臃肿,倒不如这样来的清爽些。
List
List承诺可以将元素维护在特定的序列中,List接口在Collection的基础上添加了大量的方法,使得可以在List中插入和移除元素。
有两种类型的List:
- 基本的ArrayList,它擅长于随机访问元素,但是在中间插入和移除元素时比较慢。
- LinkedList,它通过代价比较低的在List中间进行插入和删除操作,提供了优化的顺序访问。但是它在随机访问方面比较差。
下面先介绍
ArrayList:
对于ArrayList的方法可以先看下代码:
class Word{
String w;
Word(String w){
this.w=w;
}
public String toString(){
return w;
}
}
class WordD extends Word{
WordD(String w) {
super(w);
// TODO Auto-generated constructor stub
}
public String toString(){
return w+"D";
}
}
public class ListTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Word> ws = new ArrayList<Word>();
Word a = new Word("A");
Word b = new Word("B");
Word c = new Word("C");
Word d = new WordD("D");
Word e = new Word("E");
//增
ws.add(a);
ws.add(b);
ws.add(c);
ws.add(d);
System.out.println("1:"+ws);
//删
System.out.println("2:"+"e:"+ws.remove(e)+" b:"+ws.remove(b));
System.out.println("3:"+ws);
//插
ws.add(2, e);
System.out.println("4:"+ws);
//查
System.out.println("5:"+ws.get(0)+":"+ws.indexOf(ws.get(0)));
//截取
List<Word> sub = ws.subList(1, 3);
System.out.println("6:"+sub);
System.out.println("7:"+ws.containsAll(sub));
//清空
ws.clear();
System.out.println("8:"+ws);
}
}
运行结果:
1:[A, B, C, DD]
2:e:false b:true
3:[A, C, DD]
4:[A, C, E, DD]
5:A:0
6:[C, E]
7:true
8:[]
看了代码应该对ArrayList有一个大致的了解,下面仔细说下:
- add方法:在list中添加对象,可以是子类的对象。可以在参数中设置添加的位置,作为插入来使用。
- remove方法:将要移除的对象的引用传递给remove方法,就可以在List中移除该对象。删除成功返回true,要是想删除的对象不在List中,则返回false。
- get方法:根据索引获得对应的在List中的对象。
- indexOf方法:获得对象在List中的索引位置。
- sublist方法:在List中截取一段,有两个参数,一个起始位置的索引,另一个是结束位置的索引,需要注意的是,截取出来的内容包括起始位置,但是不包括结束位置。
- clear方法,清空List。
常用的方法基本就这些,还有一些其他的方法,在了解基本的这些后,也很容易看出来怎么用,比如addAll方法,显然就是add方法的变形,参数为一组对象就可以了。
接下来介绍
LinkedList
LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作时比ArrayList更高效,但是在随机访问方面更逊一筹。
Linked还添加了可以使其用作栈、队列或双端队列的方法。
这些方法中有些彼此之间只是名称的差异,或者存在些许差异。例如,getFirst和element完全一样,都是返回表头,而不移除它,如果为空,则跑出异常,而peek只是稍有差异,为空时返回null。
下面展示各方法的差异性:
class Word{
String w;
Word(String w){
this.w=w;
}
public String toString(){
return w;
}
}
class WordD extends Word{
WordD(String w) {
super(w);
// TODO Auto-generated constructor stub
}
public String toString(){
return w+"D";
}
}
public class ListTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
LinkedList<Word> ws = new LinkedList<Word>();
Word a = new Word("A");
Word b = new Word("B");
Word c = new Word("C");
Word d = new WordD("D");
Word e = new Word("E");
ws.add(a);
ws.add(b);
ws.add(c);
ws.add(d);
System.out.println("getFirst:"+ws.getFirst());
System.out.println("element:"+ws.element());
System.out.println("peek:"+ws.peek());
System.out.println("remove:"+ws.remove());
System.out.println("removeFirst:"+ws.removeFirst());
System.out.println("poll:"+ws.poll());
System.out.println("ws:"+ws);
ws.addFirst(e);
System.out.println("addFirst:"+ws);
ws.offer(a);
System.out.println("offer:"+ws);
ws.add(c);
System.out.println("add:"+ws);
}
}
输出结果:
getFirst:A
element:A
peek:A
remove:A
removeFirst:B
poll:C
ws:[DD]
addFirst:[E, DD]
offer:[E, DD, A]
add:[E, DD, A, C]
后注,本来打算每个容器类都弄新弄一个章节,但是感觉后面的好多东西和前面的有重叠,还是放在一起好了。
Stack
Stack也就是栈,对于有一定基础的人应该不陌生,它秉持的是后进先出(LIFO)原则。
Stack有两个基本操作,入栈push,出栈pop,peek获得栈首但是不出栈,你会发现,这几个方法在LInkedList中都有,因此可以直接将LinkedList作为栈使用,不过,有时候为了让代码更加的清晰,我们还是会使用Stack。
下面是个简单的小例子:
public class StackTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Stack<String>stack=new Stack<String>();
for(String s: "A B C D E F G".split(" ")){
stack.push(s);
}
while(!stack.isEmpty()){
System.out.println(stack.pop());
}
}
}
输出为:
G
F
E
D
C
B
A
Set
Set不保存重复的元素,set中最常被使用的是测试归属性,你可以轻而易举的询问某个对象是否在Set中,正因如此,查找就成为了Set中最重要的操作了。
Set具有与Collection完全一样的接口,实际上Set就是Collection,只是行为不同。(这是继承和多态思想的典型应用:表现不同的行为)。
下面使用HashSet做一个简单的实例:
public class SetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Random rand = new Random(47);
Set<Integer> set = new HashSet<Integer>();
for(int i=0;i<30;i++){
set.add(rand.nextInt(30));
}
System.out.println(set);
}
}
输出结果为:
[0, 1, 2, 3, 5, 7, 8, 10, 11, 12, 13, 14, 16, 18, 19, 20, 21, 25, 26, 28, 29]
可以看出来重复的是不会出现的,但是感觉出来的结果像是有序的,这样想就错了,我们改下随机的范围,就可以看出来:
public class SetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Random rand = new Random(47);
Set<Integer> set = new HashSet<Integer>();
for(int i=0;i<30;i++){
set.add(rand.nextInt(500));
}
System.out.println(set);
}
}
其结果是:
[128, 193, 258, 322, 200, 11, 75, 140, 204, 461, 206, 207, 20, 22, 278, 342, 89, 410, 288, 416, 361, 429, 368, 498, 51, 309, 55, 383]
因为出于速度的考虑,HashSet使用了散列,散列在后面再介绍。HashSet所维护的顺序与TreeSet和LinkedHashSet都不同因为它们的实现具有不同的元素存储方式。TreeSet将元素存储在红黑树数据结构中,所以,想对结果排序,可以使用TreeSet。
下面就要介绍set最重要的用法,测试归属性,同样,我写了个例子,因为通过代码更加直观。
public class SetTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Random rand = new Random(47);
Set<String> set = new HashSet<String>();
Collections.addAll(set, "A B C D E F G".split(" "));
System.out.println("A:"+set.contains("A"));//包含
System.out.println("H:"+set.contains("H"));//不包含
Set<String> set2 = new HashSet<String>();
Collections.addAll(set2, "E F G".split(" "));
System.out.println("set2:"+set.contains(set2));//普通包含
System.out.println("set2:"+set.containsAll(set2));//包含全部
set.remove("E");
System.out.println("set2:"+set.containsAll(set2));//部分不一样
set.removeAll(set2);
System.out.println("set:"+set);
}
}
其结果如下:
A:true
H:false
set2:false
set2:true
set2:false
set:[A, B, C, D]
Map
Map用来存储键值对,也就是给一个“键”就可以获得对应的“值”。键值对可以很方便的取出想要的对象,下面就是一个例子,取出来对象,统计数量:
public class MapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<String,Integer> m = new HashMap<String,Integer>();
for(String s:"hello word hello java".split(" ")){
Integer i= m.get(s);
m.put(s, i==null?1:i+1);
}
System.out.println(m);
}
}
结果:
{java=1, hello=2, word=1}
同时map还可以和其他的容器结合使用:
public class MapTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<String,List<String>> m = new HashMap<String,List<String>>();
m.put("A", Arrays.asList("Apple","Angle"));
m.put("B", Arrays.asList("Baby","Banana"));
m.put("C", Arrays.asList("Come","Candy","China"));
System.out.println(m);
System.out.println(m.get("A"));
System.out.println(m.get("A").get(1));
}
}
输出:
{A=[Apple, Angle], B=[Baby, Banana], C=[Come, Candy, China]}
[Apple, Angle]
Angle
Queue
Queue队列,按照先进先出原则存储数据,这个和栈Stack基本一样,都是通过LInkedList向上转型成Queue。下面一个简单的例子,便于理解:
public class QueueTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Queue<Integer> q= new LinkedList<Integer>();
for(int i = 0 ; i < 10 ; i++){
q.offer(i);
}
System.out.println(q);
System.out.println(q.poll());
}
}
输出:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0