为什么要引入数据容器 

        在书写程序的时候,我们常常需要对大量的对象引用进行管理。为了实现有效的归类管理,我们常常将同类的引用放置在同一数据容器中。由于数据容器中存放了我们随时可能需要使用到的对象引用,所以一般的数据容器要都要能能提供方便的查询、遍历、修改等基本接口功能。早期的OOP语言都通过数组的方式来实现对引用集的集中管理和维护。

        但是数组方式下,数组大小需要提前被确定,并不允许修改大小,导致其作为一种灵活的数据容器的能力的功能大为下降。为了方便的利用数据容器进行引用的管理,Java中提供了丰富的数据容器以满足程序员多样化的需求。


分类

        java结合类主要有两个接口派生出如下图:

常用集合主要特点及实现方式

(1)set

1)(元素不重复),是没有顺序的。比较元素是否相同是用equls实现的

注意:equals的底层是用==实现的,==比较的是指向值的引用。而引用类型重写了equals方法比较的是具体的值。

2)set的底层是用map实现的,hashSet由hashMap实现

原因:set的值是不可重复的,而map的key值是不可重复的,所以set将值保存到了map的key中,而value存的一个object对象。

3)底层实现源码,以添加元素为例

<span style="font-family: Arial, Helvetica, sans-serif;">public boolean add(E e) {</span>
        return map.put(e, PRESENT)==null;
    }

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        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;
    }

void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }

void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

过程:a)添加元素时先判断存放se中数据的obeject数组(transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;)是否存在,不存在新建一个

    b)判断key值是否存在,不存在返回错误

    c)计算hashcode,遍历已经存在的数据比较hashcode和key值是否存在,若存在新的值覆盖原来的值,否则新加到数组中

   d)向数组中添加数据时判断是否能添加(一般超过75%就不可以),可以就添加。不可以就新建一个

(1)arrayList

1)不是线程安全的,vector是线程安全的。两者都是以数组的形式保存数据的

2)用法

	public void testFixArrayList(){
		 //add()和remove()方法在失败的时候会抛出异常(不推荐)
		//它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
	        Queue<String> queue = new LinkedList<String>();
	        //添加元素
	        queue.offer("a");
	        queue.offer("b");
	        queue.offer("c");
	        queue.offer("d");
	        queue.offer("e");
	        for(String q : queue){
	            System.out.println(q);
	        }
	        System.out.println("===");
	        System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
	        for(String q : queue){
	            System.out.println(q);
	        }
	        System.out.println("===");
	        System.out.println("element="+queue.element()); //返回第一个元素 ,但不删除
	        for(String q : queue){
	            System.out.println(q);
	        }
	        System.out.println("===");
	        System.out.println("peek="+queue.peek()); //返回第一个元素 ,但不删除
	        for(String q : queue){
	            System.out.println(q);
	        }
		}
3)放进去是什么类型的取出来强制转换成什么类型,否则会报错

	public void testarray(){
		ArrayList list = new ArrayList();
		list.add("hehda");
		list.add(new Integer(1));
		
		String a = (String)list.get(0);
		Integer b = (Integer)list.get(1);
		
		System.out.println(a);
		System.out.println(b);
		}
4) 存放元素的就是一个 object 数组。没生成一个新的数组长度为原来的1.5+1倍,之后将原来数组复制到新的数组中

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐