Collections
◆所以的集合类都实现了collection接口。
ArrayList
◆集合中存放的是对象的引用而不是对象本身。
◆ArrayList是一个动态增长的数组,当我们调用无参构造函数时,默认会分配一个长度为10的数组,当空间不足时会动态增长为原来的1.5倍加1,然后将旧的数组复制到新的数组当中,当新的数组无法容纳更多的元素时,重复该过程。
◆可以用ArrayList
(int initialCapacity)
来初始化数组的容量,容量过大时,可以用trimToSize()方法挤压到刚好适合的大小,也可以用ensureCapacity
(int minCapacity)
来调整大小。
◆ArrayList是一个List接口的可变数组的实现,实现了List接口里面的所有方法,并且允许它的所有成员为null。它可以粗略的认为和Vector相当,但ArrayList不是同步的,Vector是同步的。
◆ArrayList底层维护的是一个
Private transient object[] elementData
|
常用的方法
package com.anllin.collections;
import java.util.ArrayList;
public class ArrayListTest
{
public static void main(String[] args)
{
// 当这样初始化时,会产生一个容量 为10的类型为Object[]的elementData数组。ArrayList的内部实现方式。
// 当容量超过10时,会自动增长为(10*3)/2+1
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");
list.add("Welcome");
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
list.remove("Welcome");
list.add("World");
list.add("aaa");
list.add("bbb");
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
System.out.println(list.indexOf("World"));
System.out.println(list.indexOf("aaa"));
list.clear();
System.out.println(list.size());
}
}
|
◆对于ArrayList来说,当你没有使用泛型时,默认里面的成员类型都为Object类型,当你把int,String等类型放进去后,取出来时要进行相应的类型转化,否则程序运行时会报错。
package com.anllin.collections;
import java.util.ArrayList;
public class ArrayListTest3
{
public static void main(String[] args)
{
ArrayList list = new ArrayList();
list.add("anllin");
list.add(new Integer(23));
list.add(new Boolean(false));
list.add(new Long(1379446));
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
int age = ((Integer)list.get(1)).intValue();
System.out.println(age);
}
}
|
◆
对于原生数据类型,不能直接把它添加到ArrayList中,要使用相应的包装类,使用泛型则可以避免这种问题。当调用toArray方法时,也不能直接用原生数据类型的包装类型进行转化,因为数组时存放的是对象,可能存的是不同的包装类型。如下:
package com.anllin.collections;
import java.util.ArrayList;
public class ArrayListTest4
{
public static void main(String[] args)
{
ArrayList list = new ArrayList();
list.add(new Integer(1));
list.add(new Integer(2));
list.add(new Integer(3));
list.add(new Integer(4));
list.add(new Integer(5));
list.add(new Integer(6));
list.add(new Integer(7));
int sum = 0;
for (int i = 0; i < list.size(); i++)
{
int val = ((Integer)list.get(i)).intValue();
sum += val;
}
System.out.println(sum);
//不能直接用Integer[] 转换,因为list.toArray()产生的是一个Object[],数组里的每个元素不一定是Integer类型的。
Object[] arr = list.toArray();
for (int i = 0; i < arr.length; i++)
{
int val = ((Integer)arr[i]).intValue();
System.out.println(val);
}
}
}
|
◆如果你在集合中存放了自定义的类,那么打印时会调用Object.toString()方法,如果你想要打印自己想要的结果,就要重写toString()方法。
package com.anllin.collections;
import java.util.ArrayList;
public class ArrayListTest2
{
public static void main(String[] args)
{
ArrayList list = new ArrayList();
list.add(new Point(1,1));
list.add(new Point(2,2));
list.add(new Point(3,3));
list.add(new Point(4,4));
//自定义的类要输入自己想要的格式信息,必须重写toString()方法
System.out.println(list);
}
}
class Point
{
/**
* Properties
*/
int x;
int y;
/**
* Constructor
* @param x
* @param y
*/
public Point(int x,int y)
{
this.x = x;
this.y = y;
}
/**
* @category Method ref Object.toString() is overrided.
*/
public String toString()
{
return "x = " + this.x + ", y = " + this.y;
}
}
|
◆对于ArrayList的删除操作,需要将后续的元素往前移动,代价比较高。
◆如果要经常对元素进行查找,用ArrayList比较好,如果要经常对元素插入或删除,用LinkedList比较好。
LinkedList
◆
LinkedList也实现了List接口,所以操作上和ArrayList相似,不同的是,它也实现了deque,queue接口,提供了get,insert,remove的其他一些细粒度的操作,我们可以用这些方法轻松的实现stack,queue和double-ended queue。
◆
底层维护的是一个双链表,双链表中维护的是一个个Entry<E>结点类。
private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
|
基本的操作:
public class LinkedListTest
{
public static void main(String[] args)
{
LinkedList list = new LinkedList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for(Iterator iterator = list.iterator(); iterator.hasNext();)
{
System.out.println(iterator.next());
}
}
}
|
◆单链表结点类
public class Node
{
Node next;
String data;
public Node(String data)
{
this.data = data;
}
public String toString()
{
return this.data.toString();
}
}
|
◆单链表的一些基本操作
public class NodeTest
{
public static void main(String[] args)
{
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
//add nodes generate a cycle single Liked List.
node1.next = node2;
node2.next = node3;
node3.next = node1;
System.out.println(node1.next.next);
System.out.println("----------------");
Node node4 = new Node("node4");
//insert node4 into between node1 and node 2;
node1.next = node4;
node4.next = node2;
System.out.println(node1.next.next);
System.out.println("----------------");
//remove the node4
node1.next = node2;
node4.next = null;
System.out.println(node1.next.next);
System.out.println("----------------");
}
}
|
◆双链表结点类
public class DbNode
{
DbNode next;
String data;
DbNode priv;
public DbNode(String data)
{
this.data = data;
}
public String toString()
{
return this.data.toString();
}
}
|
◆双链表的操作
public class DbNodeTest
{
public static void main(String[] args)
{
DbNode node1 = new DbNode("node1");
DbNode node2 = new DbNode("node2");
DbNode node3 = new DbNode("node3");
DbNode node4 = new DbNode("node4");
//add nodes generates a cycle double Linked List;
node1.next = node2;
node1.priv = node3;
node2.next = node3;
node2.priv = node1;
node3.next = node1;
node3.priv = node2;
System.out.println(node1.priv.priv.priv);
System.out.println("------------------");
//insert node4 between node1 and node2
node1.next = node4;
node4.priv = node1;
node4.next = node2;
node2.priv = node4;
System.out.println(node1.priv.priv.priv);
System.out.println("------------------");
//remove the node4
node1.next = node2;
node2.priv = node1;
System.out.println(node1.priv.priv.priv);
System.out.println("------------------");
}
}
|
◆用LinkedList实现Stack
public class MyStack
{
private LinkedList list;
public MyStack()
{
list = new LinkedList();
}
public void push(Object obj)
{
if(null != obj)
{
list.addLast(obj);
}
}
public void pop()
{
list.removeLast();
}
public Object peek()
{
return list.getLast();
}
public boolean isEmpty()
{
return list.isEmpty();
}
public int size()
{
return list.size();
}
public String toString()
{
return this.list.toString();
}
}
|
◆测试stack
public class StackTest
{
public static void main(String[] args)
{
MyStack stack = new MyStack();
stack.push("aaa");
stack.push("bbb");
stack.push("ccc");
System.out.println(stack.peek());
System.out.println(stack);
stack.pop();
System.out.println(stack);
System.out.println(stack.isEmpty());
}
}
|
◆用LinkedList实现Queue
public class MyQueue
{
private LinkedList list;
public MyQueue()
{
list = new LinkedList();
}
public void inqueue(Object obj)
{
if(null != obj)
{
list.addLast(obj);
}
}
public Object dequeue()
{
return list.removeFirst();
}
public boolean isEmpty()
{
return list.isEmpty();
}
public int size()
{
return list.size();
}
public String toString()
{
return this.list.toString();
}
}
|
◆测试Queue
public class QueueTest
{
public static void main(String[] args)
{
MyQueue queue = new MyQueue();
queue.inqueue("aaa");
queue.inqueue("bbb");
queue.inqueue("ccc");
System.out.println(queue);
queue.dequeue();
System.out.println(queue);
System.out.println(queue.isEmpty());
}
}
|
HashSet
◆HashSet实现了Set接口,Set是一个不包含重复成员值的集合。只有要增加的成员在集合中满足为null时才会被增加成功(也就是说没有相同的值在集合中),所以通常要使自定义的类的对象添加时不重复的话,需要重写equals和hashCode方法。
◆Add 方法的判断条件为(e==null ? e2==null : e.equals(e2))
◆HashSet底层维护的是HashMap的KeySet集合,KeySet符合Set接口的特征。KeySet用一个Entry[]来实现,程序会根据key所给的hashCode值来计算它的位置,如果该位置无元素,则将它插入,有元素就再散列,直到找到没有元素的位置。HashSet会根据初始容量和负载因子来计算数组的容量,快满时会扩容。
public class HashSetTest
{
public static void main(String[] args)
{
//HashSet底层是维护一个HashMap对象,所有的操作都通过这个对象来完成。
//HashSet是一个添加不包含重复值的无序集合。
HashSet set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
set.add("f");
set.add("g");
set.add("h");
//这行不会添加成功,因为String类重写了hashCode方法。
System.out.println(set.add("a"));
for(Iterator tr = set.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
}
}
|
◆
对于HashSet来说,如果添加的对象的hashCode相同,则不会被添加,否则调用equals方法判断增加的值是否在集合中已经存在,如果为true不添加,否则添加。
◆
当我们重写Object类的equals方法时,必须重写hashCode方法,反之亦然。
public class HashSetTest2
{
public static void main(String[] args)
{
//HashSet是否会重复添加相同的元素的关键是,hashCode是否相同。
HashSet set = new HashSet();
Person person = new Person("aaa");
set.add(person);
set.add(new Person("bbb"));
set.add(new Person("ccc"));
set.add(new Person("ddd"));
set.add(person);//不会被添加 ,因为person是同一对象,hashCode相同。
set.add(new Person("aaa"));//这里被添加 了,因为这个对象的hashCode与前一个对象的hashCode不同。
//为了达到new Person("aaa")时,名称相同则不添加的目的,我们需要重写Object的equals和hashCode方法。
//重写后,则set.add(new Person("aaa"));就不会被执行了。
for (Iterator tr = set.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
for (Iterator tr = set.iterator(); tr.hasNext();)
{
System.out.println(tr.next().hashCode());
}
}
}
|
public class HashSetTest3
{
public static void main(String[] args)
{
HashSet set = new HashSet();
set.add(new Integer(4));
set.add(new Integer(2));
set.add(new Integer(3));
set.add(new Integer(1));
//这行不会添加成功,因为它本身重写了equals和hashCode方法。
//只有hashCode不同时才会被添加成功。
boolean flag = set.add(new Integer(4));
System.out.println(flag);
for(Iterator tr = set.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
}
}
|
TreeSet
◆TreeSet实现了SortedSet接口,这个接口提供了一个的有序的成员集合,它会按照自然升序排序或按照重写的comparator类的 compare方法进行排序。
TreeSet还实现了Set接口,所以也不能添加重复的成员,条件是hashCode不同时。
◆
所有插入到SortedSet集合中的成员,必须实现comparable接口或重写comparator类的 compare方法。
◆
TreeSet底层可以简单的看成是一个TreeMap的KeySet集合。
public class TreeSetTest
{
public static void main(String[] args)
{
//TreeSet是一个可以排序的集合,当要使用不同的排序规则时,可以自定义一个Comparator类,然后当参数传递。
//TreeSet不能包含重复的成员,条件是hashCode不同。
//TreeSet底层维护的其实是一个TreeMap对象。
TreeSet set = new TreeSet();
set.add("c");
set.add("a");
set.add("e");
set.add("d");
set.add("f");
set.add("a");
System.out.println(set);
for(Iterator iter = set.iterator(); iter.hasNext();)
{
System.out.println(iter.next());
}
}
}
|
◆对于自定义的类,是没有排序规则的,所以在使用TreeSet添加时,必须先定制好排序规则。否则会报ClassCastException
public class TreeSetTest2
{
public static void main(String[] args)
{
TreeSet set = new TreeSet(new StudentScoreComparator());
Student s1 = new Student(10);
Student s2 = new Student(20);
Student s3 = new Student(30);
Student s4 = new Student(40);
Student s5 = new Student(50);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
for(Iterator iter = set.iterator(); iter.hasNext();)
{
System.out.println(iter.next());
}
}
}
//自定义排序的规则,让所有的学生按考试成绩进行排序
class StudentScoreComparator implements Comparator
{
public int compare(Object arg0, Object arg1)
{
Student s1 = (Student)arg0;
Student s2 = (Student)arg1;
return s2.score-s1.score;
}
}
public class Student
{
int score;
public Student(int score)
{
this.score = score;
}
public String toString()
{
return "Student [score=" + score + "]";
}
}
|
◆对于系统提供的类型,一般已经指定好了排序规则,但是我们可以改变这个规则。
public class TreeSetTest3
{
public static void main(String[] args)
{
TreeSet set = new TreeSet(new StringComparator());
set.add("c");
set.add("a");
set.add("e");
set.add("d");
set.add("f");
set.add("b");
System.out.println(set);
}
}
//自定义排序规则让字符串按降序排列
class StringComparator implements Comparator
{
public int compare(Object arg0, Object arg1)
{
String str1 = (String)arg0;
String str2 = (String)arg1;
return str2.compareTo(str1);
}
}
|
Collections类
常用方法
public class CollectionTest
{
public static void main(String[] args)
{
LinkedList list = new LinkedList();
list.add(new Integer(8));
list.add(new Integer(-20));
list.add(new Integer(20));
list.add(new Integer(-8));
//用Collections.reverseOrder()可以实现对原有集合的反序排序。
//Collections是一个用于操作集合的类,Collection是一个接口,两者不一样
Comparator r = Collections.reverseOrder();
Collections.sort(list,r);
for(Iterator iter = list.iterator(); iter.hasNext();)
{
System.out.println(iter.next());
}
System.out.println("----------------");
//把原有集合的顺序打乱,每次运行都会产生不同的结果。
Collections.shuffle(list);
for(Iterator iter = list.iterator(); iter.hasNext();)
{
System.out.println(iter.next());
}
System.out.println("----------------");
//取得集合中的最大值 和最小值
System.out.println(Collections.min(list));
System.out.println(Collections.max(list));
}
}
|
HashMap
◆HashMap是一个基于Map接口实现的散列表,集合中包含的是一个个键值对的对象,不能包含重复的键,一个键至少要映射一个值,值是可以重复的。它相当于Dictionary类。
◆Map接口提供了三种集合的视图,分别是Keys,values和key-value mappings
◆HashMap底层采有数组(Entry[])加单链表(Entry->Entry)的形式实现。
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
|
基本操作:
public class HashMapTest
{
public static void main(String[] args)
{
//HashMap是一个键值 对映射的集合,底层维护的是一个Entry<k,v>对象。
//HashMap不包含重复的键,但是可以包含重复的值 ,一个键可以对应多个值 ,但是一个值 不能对应 多个健 。
//HashMap是一个无序的集合。
HashMap map = new HashMap();
map.put("1","aaa");
map.put("2","bbb");
map.put("3","ccc");
map.put("4","ddd");
map.put("5","aaa");
//因为keys是不能重复的,所以它的类型是set
Set keys = map.keySet();
for(Iterator tr = keys.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
System.out.println("---------------");
//因为values是可以重复的,所以它的类型是Collection
Collection values = map.values();
for(Iterator tr = values.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
System.out.println("---------------");
map.remove("5");
for(Iterator tr = keys.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
}
}
|
◆当使用自定义类时,如果不想在集合中添加重复的对象,可以重写equals和hashCode方法。当键相同时,后面的值会覆盖掉前面的值。
public class HashMapTest2
{
public static void main(String[] args)
{
//当使用定义类时,如果不想出现重复的对象,要重写equals和hashCode方法。
//换句话说,就是HashMap健不重复的关键是hashCode是否相同,而不是对象是否相同。
HashMap map = new HashMap();
map.put(new Person("Bob"),"1");
map.put(new Person("John"),"2");
map.put(new Person("Mike"),"3");
map.put(new Person("Kate"),"4");
//这行在插入时,会将已存在键的值替换掉。而不是象set那样不让它插入。
map.put(new Person("John"),"5");
Set keys = map.keySet();
for(Iterator tr = keys.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
System.out.println("---------------");
Collection values = map.values();
for(Iterator tr = values.iterator(); tr.hasNext();)
{
System.out.println(tr.next());
}
System.out.println("---------------");
}
}
|
◆在命令行中输入单词,统计每个单词出现的次数
public class HashMapTest4
{
public static void main(String[] args)
{
HashMap map = new HashMap();
for(int i = 0; i<args.length; i++)
{
if(map.containsKey(args[i]))
{
Integer value = (Integer)map.get(args[i]);
value = new Integer(value.intValue()+ 1);
map.put(args[i],value);
}
else
{
map.put(args[i],new Integer(1));
}
}
Set keys = map.keySet();
System.out.println(map);
for(Iterator iter = keys.iterator(); iter.hasNext();)
{
String key = (String)iter.next();
int val = ((Integer)map.get(key)).intValue();
System.out.println(key + "=" + val);
}
}
}
|
◆返回一个键值对视图
public class HashMapTest5
{
public static void main(String[] args)
{
HashMap map = new HashMap();
map.put("a","aa");
map.put("d","dd");
map.put("c","cc");
map.put("b","bb");
//返回一个包含健值对的集合。类型是Map.Entry<k,v>.
Set set = map.entrySet();
for(Iterator iter = set.iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry)iter.next();
String key = (String)entry.getKey();
String val = (String)entry.getValue();
System.out.println(key + ":" + val);
}
}
}
|
TreeMap
◆TreeMap是一个基于NavigableMap
接口实现的一个红黑树集合,可以按自然顺序或给定一个比较器进行排序。
◆一些基本的操作
public class TreeMapTest
{
public static void main(String[] args)
{
//TreeMap是一个有序的集合,不能包含重复的键,当插入相同键时,值会被替换,但是键只有一个。
//TreeMap底层维护的是一个Map.Entry<k,v>对象。
//当不想使用默认的排序方式时,可以使用Comparator创建自己的排序规则 。
TreeMap map = new TreeMap();
map.put("a","aa");
map.put("b","bb");
map.put("c","cc");
map.put("d","dd");
map.put("e","ee");
map.put("e","ff");
Set set = map.entrySet();
for(Iterator iter = set.iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry)iter.next();
String key = (String)entry.getKey();
String val = (String)entry.getValue();
System.out.println(key + ":" + val);
}
}
}
|
◆使用TreeMap的一个简单的例子
/**
* 题目:随机生成50个数字(整数),每个数字的范围是[10,50],统计每个数字出
现的次数以及出现次数最多的数字与它的个数,最后将每个数字及其出现次数打印
出来,如果某个数字出现次数为0,则不要打印它。打印时按照数字的升序排列。
* @author Administrator
*
*/
public class TreeMapTest3
{
public static void main(String[] args)
{
//存储数字及其出现的次数(key为数字,value为出现次数)。
TreeMap map = new TreeMap();
Random rand = new Random();
for(int i = 0; i<50; i++)
{
Integer key = new Integer(rand.nextInt(41)+ 10);
if(map.containsKey(key))
{
Integer val = (Integer)map.get(key);
val = new Integer(val.intValue()+ 1);
map.put(key,val);
}
else
{
map.put(key,new Integer(1));
}
}
Set set = map.entrySet();
Collection vals= map.values();
int max = ((Integer)Collections.max(vals)).intValue();
//存储出现次数最多的数字
List list = new ArrayList();
for(Iterator iter = set.iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry)iter.next();
Integer key = (Integer)entry.getKey();
Integer val = (Integer)entry.getValue();
if(max == val.intValue())
{
list.add(key);
}
System.out.println(key + ":" + val);
}
System.out.println("----------------");
System.out.println("出现次数最多的是" + max + "次");
for(Iterator iter = list.iterator(); iter.hasNext();)
{
Integer val = (Integer)iter.next();
System.out.println(val);
}
}
}
|
◆当要想在TreeMap中添加自定义的类的对象时,要实现一个比较器,告诉TreeMap按什么排序。
public class TreeMapTest2
{
public static void main(String[] args)
{
TreeMap map = new TreeMap(new NameComparator());
map.put(new Person("Bob"),"1");
map.put(new Person("John"),"2");
map.put(new Person("Mike"),"3");
map.put(new Person("Kite"),"4");
map.put(new Person("Judy"),"5");
map.put(new Person("Judy"),"6");
Set set = map.entrySet();
for(Iterator iter = set.iterator(); iter.hasNext();)
{
Map.Entry entry = (Map.Entry)iter.next();
String key = ((Person)entry.getKey()).name;
String val = (String)entry.getValue();
System.out.println(key + ":" + val);
}
}
}
//实现自己的比较器
class NameComparator implements Comparator
{
public int compare(Object arg0, Object arg1)
{
Person p1 = (Person)arg0;
Person p2 = (Person)arg1;
return p2.name.compareTo(p1.name);
}
}
|
以前版本遗留下来的一些集合类
Vector
◆Vector与ArrayList相似,但是Vector是同步的。Vector底层实现也与ArrayList近似,只不是扩容是为原来容量的2倍。使用上没有多大的区别,现在已很少使用。
public class VectorTest
{
public static void main(String[] args)
{
Vector vector = new Vector();
vector.add("aaa");
vector.add("bbb");
vector.add("ccc");
vector.add("ccc");
for(int i = 0; i<vector.size(); i++)
{
System.out.println(vector.get(i));
}
}
}
|
Properties
◆Properties代表一个属性的持久化集合,它可以被保存到一个流中,也可以从一个流中加载。实现了Map<Object,Object>接口,继承自Hashtable<Object,Object>类。
◆可以调用load(Reader)
/ store(Writer, String)
方法来将Properties对象存放到文件中。也可以调用 loadFromXML(InputStream)
and storeToXML(OutputStream, String, String)
来存取到一个xml文件中。
◆获取系统中的属性并打印出来
public class PropertiesTest
{
public static void main(String[] args)
{
Properties p = System.getProperties();
Set set = p.keySet();
for(Iterator iter = set.iterator(); iter.hasNext();)
{
String key = (String)iter.next();
String val = p.getProperty(key);
System.out.println(key + " : " + val);
}
}
}
|