java集合体系
集合:
数学里的概念把若干数据放在一起,看成一个整体;而在面向对象的世界里,为了方便对对象的操作,体现了其如何以哪种方式储存(数据结构),集合是储存对象的 容器!
1、相比与数组:
数组:是JAVA语言内置的数据类型, 它是一个线性的序列,所以它可以快速的访问其他的元素。 但是速度是要有代价的,当你创建了一个数组之后,它的容量就固定了 如果发现有越界现象,会报RuntimeException异常错误 数组与集合的一点区别: 1:数组是固定长度的;集合可变长度的(自由添加)。 2:数组可以存储基本数据类型,也可以存储引用数据类型; 集合只能存储引用数据类型。(对象的引用) 3:数组存储的元素必须是同一个数据类型; 集合存储的对象可以是不同数据类型。
2、集合体系:
1. Collection 接口:
有了集合就要对其操作,那么具体操作有哪些?为此,抽取各个容器之共性,将其封装为一个接口,可以从面向对象的概念来理解;
接口方法看帮助文档:Collection家族中 两个主要下属,List,Set,(同为接口)
2. Iterator 接口: (迭代器)
因为不同容器对于对象的储存细节不同,但是有两点还是相同的:判断是否有,取出来,为此也抽取为接口用来规定这种取出集合的方式;各个集合对接口的实现,其遍历时对外提供 iterator()方法;此返回 iterator 接口的一个直接实现,
如: abstractList 中,内部类实现了接口 Iterator
public Iterator<E> iterator() {
return new Itr(); // 内部类 实现了Iterator
}
3. list接口:
有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复
具体实现类:
(1) ArrayList:【查询性】
底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快
(2) LinkedList:【增删性】
底层的数据结构是链表,线程不同步,增删元素的速度非常快。
(3) Vector:
底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢
注意:
对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。
所以建议元素都要复写equals方法,建立元素对象自己的比较相同的条件依据。
List 的示例:
package com.collection; import java.util.*; public class MethodOfList { public static void main(String args[]) { method1(); method2(); method3(); methdoOfLinkedList(); selfHasEquals(); } public static void method1(){ ArrayList al = new ArrayList(); al.add("abc0"); al.add("abc1"); al.add("abc3"); al.add(0, "abc5"); //表明是从 index = 0 时存取的: al.set(2, "替代"); _iterator(al); /* abc5 abc0 替代 abc3 */ } private static void method2(){ ArrayList al = new ArrayList(); al.add("abc0"); al.add("abc1"); al.add("abc3"); ArrayList all = new ArrayList(); all.add("abc0"); all.add("abc1"); all.add("abc5"); all.retainAll(al); // 交集 _iterator(all); /* abc0 abc1 */ } private static void method3(){ ArrayList al = new ArrayList(); al.add("abc0"); al.add("abc1"); al.add("abc2"); al.add("abc3"); al.add("abc1"); al.remove("abc1"); //只会remove()最先找到的 _iterator(al); System.out.println("4:"+al.get(3)); /* 4:abc1 由此方法我们可以用for循环来迭代,不用迭代器, 但是时间比迭代器慢多了, List集合因为角标有了自己的获取元素的方式, */ for(int i = 0; i < al.size(); i++) { // System.out.println(al.toString()); System.out.println(al.get(i)) ; if(i==0) al.add("临死添加"); if(i==1) al.set(2, "零食修改"); } /*abc0 abc2 零食修改 abc1 临死添加 */ } private static void _iterator(Collection coll) { /* 当然:如果嫌弃方法不够的话,可以获得 ListIterator (List集合特有的迭代器, 该列表迭代器接口具备了对元素的增、删、改、查的动作) */ for(Iterator iterator = coll.iterator(); iterator.hasNext() ;) { /*String s = (String)iterator.next(); System.out.println(s);*/ /* 假如add(2); ----装箱为Integer,不能转为String jdk1.5增加了泛型,在编译时期动态查询引用数据类型, 达到与实际主观add的效果() 就是集合内类型一致 */ System.out.println(iterator.next()); } System.out.println(); } public static void methdoOfLinkedList(){ LinkedList<String> ld = new LinkedList<String>(); ld.add("1"); ld.add("2"); ld.add("3"); ld.add(null); ld.addFirst("addfirst"); ld.addLast("不就是add么?"); _iterator(ld); /* 新增方法: 获得: getFirst() getLast() ; 获取链表中的第一个元素。如果链表为空, 抛出NoSuchElementException; peekFirst(); 获取链表中的第一个元素。如果链表为空,返回null。 peekLast(); public E getFirst() { if (size==0) throw new NoSuchElementException(); return header.next.element; } 移除: removeFirst() NoSuchElementException; removeLast(); pollFirst();获取链表中的第一个元素,但是会删除链表中的第一个元素。 如果链表为空,返回null。 pollLast(); */ ld.removeAll(ld); //全部移除 _iterator(ld); // System.out.println(ld.getFirst()); System.out.println(ld.peek()); // null } public static void selfHasEquals() { ArrayList<Str> al = new ArrayList<Str>(); Str temp = new Str("2"); al.add(new Str("1")); al.add(new Str("2")); //如果没有覆写equals,,,是不会找到,返回false System.out.println(al.contains(temp)); } } class Str{ String s ; Str(String s){ this.s = s; } /*public boolean equals(Object t){ return this.s.equals(((Str)t).s); }*/ }
4. Set接口:
无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性
(1) HashSet:【唯一性】
底层数据结构是哈希表,线程是不同步的。无序,高效;对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。
为什么由这两个实现呢??:
查类库: HashSet<E> private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); ----> public boolean add(E e) { return map.put(e, PRESENT)==null; } ---> HashMap<K,V> ----public V put(K key, V value) public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } 由上我们也可以知道,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。
(2) TreeSet: 【 排序性】
用于对Set集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性。如果元素不具备比较性,在运行时会发生ClassCastException异常。
Set 的示例:
package com.collection; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class MethodOfSet { public static void main(String args[]) { testMethodOfHashSet(); testMethodOfTreeSet(); testTime(); } private static void testMethodOfHashSet() { HashSet<Object> hs = new HashSet<Object>(); hs.add("a"); hs.add("b"); hs.add("a"); _iterator(hs); /* b a 体现无序性 和 唯一性 */ } private static void testMethodOfTreeSet() { TreeSet<Object> ts = new TreeSet<Object>(); ts.add("x"); ts.add("z"); ts.add("y"); ts.add("y"); _iterator(ts); /* x y z*/ } private static void _iterator(Set<Object> hs) { for(Iterator<Object> i = hs.iterator(); i.hasNext();) { System.out.println(i.next()); } System.out.println(); } private static void testTime() { long start ,end ; int temp ; Set<Integer> st = new HashSet<Integer>(); for(int i = 0; i < 1008611;i++) { st.add(i); } start = System.currentTimeMillis(); for(Iterator<Integer> i = st.iterator();i.hasNext();) { temp = i.next(); } end = System.currentTimeMillis(); System.out.println("---iterator:"+(end-start)); start = System.currentTimeMillis(); for(Integer t:st) { temp = t ; } end = System.currentTimeMillis(); System.out.println("---增强 for:"+(end-start)); /* ---iterator:25 ---增强 for:24 二者不相上下: */ } }
5、map接口:
public interface Map<K,V>
所有键值对构成的集合,对此种集合集成为一个接口,便于扩展,Map集合存储和Collection有着很大不同:
- Collection一次存一个元素;Map一次存一对元素(一对元素也可以看做一个整体,封装为对象,)。
- Collection是单列集合;Map是双列集合。
- Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。
总之,要保证map集合中键的唯一性。
----------------------
Map.Entry<K,V>
一对键值本身看做一个对象,由于有获得键,获得值,两种共性,因此抽离出此接口;getKey() ;getValue() :setValue(V value)
map的实现类中,含有内部实现类:
Entry: static class Entry<K,V> implements Map.Entry<K,V>
so ,entry是访问键值关系的入口,是map的入口,访问的是map中的键值对,
但由于内部类无法导入,故使用接口回调
转换:
map中的所有Entry 转化 为 entrySet (返回:set<Map.Entry<k,v>>
也可以将所有键值转为: Set keySet();
Map 的示例:
package com.collection; import java.util.*; public class MethodOfMap { public static void main(String args[]) { methodOfHashMap(); methodOfTreeMap(); Example(); } private static void methodOfHashMap() { Map<Integer,String> m = new HashMap<Integer,String>(); m.put(1, "a"); m.put(2, "a"); m.put(3, "a"); m.put(2, "b"); // //出map集合中所有元素 Set<Map.Entry<Integer, String>> s1 = m.entrySet(); for(Iterator<Map.Entry<Integer, String>> i = s1.iterator(); i.hasNext(); ) { Map.Entry<Integer, String> e = i.next(); System.out.println(e.getKey()+" : "+e.getValue()); e.setValue(e.getKey()+""+e.getValue()); //根据键值来修改: } System.out.println(); Set<Integer> s2 = m.keySet(); for(Iterator<Integer> i = s2.iterator(); i.hasNext(); ) { Object k = i.next(); Object v = m.get(k); System.out.println(k+" : "+v); } /* 1 : a 2 : b 3 : a 1 : 1a 2 : 2b 3 : 3a */ } private static void methodOfTreeMap() { Map<Integer,String> m = new TreeMap<Integer,String>(); m.put(51, "v"); m.put(72, "e"); m.put(33, "l"); m.put(42, "o"); Set<Integer> s = m.keySet(); Iterator<Integer> i = s.iterator(); while(i.hasNext()){ Integer k = i.next(); String v = m.get(k); System.out.println(k+"-"+v); } /* 33-l 42-o 51-v 72-e*/ } public static void Example() { /* 一字符串:String str="1,1,0"; 数字使用逗号隔开,统计出每个数字出现的次数。使用map<数字,次数>;*/ String target = "10,16,10,16,17,11,16,10,16,10,17,11,16,13,13,11,16,10"; String s[] = target.split(","); Map<String ,Integer> m = new HashMap<String ,Integer>(); for(int i = 0 ;i <s.length ;i++) { if(m.containsKey(s[i])) { m.put(s[i],m.get(s[i])+1); } else{ m.put(s[i], 1); } } for(Iterator<Map.Entry<String ,Integer>> i = m.entrySet().iterator() ;i.hasNext();) { Map.Entry<String, Integer> me = i.next(); System.out.println(me.getKey()+":"+me.getValue()); /* 10:5 17:2 16:6 13:2 11:3 */ } } }