集合
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); } } } |