----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
Collection接口
基本操作:添加元素:add,addAll
删除元素:remove,removeAll
求交集:retainAll
遍历用:Iterator接口
public interface Iterator{
public boolean hasNext();
public Object next();
public void remove();
}
-->Set接口:没有重复项目的集合
实现了Collection接口
Set使用equals()方法来检查元素的等同性
-->HashSet——基于hash表的集合
向HashSet中存数据的时候首先判断hashCode是否相同,不相同则存储,相同则调用equals()方法,相同就不存,不同才会存储
******因此添加到HashSet的类需要覆盖hashCode()方法和equals()方法**********
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看
HashSet 的源代码,可以看到如下代码:
public class HashSet<E> extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
// 使用 HashMap 的 key 保存 HashSet 中所有元素
private transient HashMap<E,Object> map;
// 定义一个虚拟的 Object 对象作为 HashMap 的 value
private static final Object PRESENT = new Object();
...
// 初始化 HashSet,底层会初始化一个 HashMap
public HashSet() {
map = new HashMap<E,Object>();
}
// 以指定的 initialCapacity、loadFactor 创建 HashSet
// 其实就是以相应的参数创建 HashMap
public HashSet(int initialCapacity, float loadFactor)
{
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity)
{
map = new HashMap<E,Object>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy)
{
map = new LinkedHashMap<E,Object>(initialCapacity , loadFactor);
}
// 调用 map 的 keySet 来返回所有的 key
public Iterator<E> iterator()
{
return map.keySet().iterator();
}
// 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数
public int size()
{
return map.size();
}
// 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,
// 当 HashMap 为空时,对应的 HashSet 也为空
public boolean isEmpty()
{
return map.isEmpty();
}
// 调用 HashMap 的 containsKey 判断是否包含指定 key
//HashSet 的所有元素就是通过 HashMap 的 key 来保存的
public boolean contains(Object o)
{
return map.containsKey(o);
}
// 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap
public boolean add(E e)
{
return map.put(e, PRESENT) == null;
}
// 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素
public boolean remove(Object o)
{
return map.remove(o)==PRESENT;
}
// 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
public void clear()
{
map.clear();
}
...
}
-->LinkedHashSet——相对HashSet有顺序
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
-->TreeSet——基于平衡树的数据结构
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。如使用String、Integer中的CompareTo方法进行比较。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
自然排序情况下,一个TreeSet中只允许存放同一类型的多个元素,这里要求不是自定义的类,否则会抛出ClassCastException。
当试图将对象强制转换为不是实例的子类时,抛出该异常。如试图将String类型的对象转换为Integer类型的对象。
因为TreeSet实现了Set接口,同样要求集合元素不重复,所以一样要复写hashCode()和equals()方法。
除了自然排序的情况下,存入TreeSet的类都要实现Comparator接口,复写compareTo()方法,除非仅在该集合中存入该自定义类的一个对象
-->List——有顺序,可以重复
-->ArrayList—VS—Vector:数组表
Vector:线程安全,但效率低
需要增加爱长度时Vector增长为原来一倍,ArrayList则仅增加一半。
ArrayList就是所谓的动态数组,其底层依然是Array
与Array相比:
(1)ArrayList是Array的复杂版本
ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。
(2)内部的Object类型的影响
对于一般的引用类型来说,这部分的影响不是很大,但是对于值类型来说,往ArrayList里面添加和修改元素,都会引起装箱和拆箱的操作,频繁的操作可能会影响一部分效率。
但是恰恰对于大多数人,多数的应用都是使用值类型的数组。
消除这个影响是没有办法的,除非你不用它,否则就要承担一部分的效率损失,不过这部分的损失不会很大。
(3)数组扩容
这是对ArrayList效率影响比较大的一个因素。
每当执行Add、AddRange、Insert、InsertRange等添加元素的方法,都会检查内部数组的容量是否不够了,如果是,它就会以当前容量的两倍来重新构建一个数组,将旧元素Copy到新数组中,然后丢弃旧数组,在这个临界点的扩容操作,应该来说是比较影响效率的。
(4)频繁的调用IndexOf、Contains等方法(Sort、BinarySearch等方法经过优化,不在此列)引起的效率损失
首先,我们要明确一点,ArrayList是动态数组,它不包括通过Key或者Value快速访问的算法,所以实际上调用IndexOf、Contains等方法是执行的简单的循环来查找元素,所以频繁的调用此类方法并不比你自己写循环并且稍作优化来的快,如果有这方面的要求,建议使用Hashtable或SortedList等键值对的集合。
-->LinkedList——双向链表
LinkedList是通过节点直接彼此连接来实现的。每一个节点都包含前一个节点的引用,后一个节点的引用和节点存储的值。当一个新节点插入时,只需要修改其中保持先后关系的节点的引用即可,当删除记录时也一样。这样就带来以下有缺点:
操作其中对象的速度快 只需要改变连接,新的节点可以在内存中的任何地方
不能随即访问 虽然存在get()方法,但是这个方法是通过遍历接点来定位的所以速度慢。
ArrayList相比于LinkedList
前者是顺序存储结构,后者是链式存储。所以前者便于查找,但插入和删除很慢;二后者查找相对较慢,但增删较快
Map接口
用于关键字/数值对
Map中通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value
这和数组其实挺相像的:数组用下表做索引,Map则用对象
数组只能存一种数据类型,Map可以存多种
-->HashMap:基于hash表的映射表
HashMap底层其实用到了hashCode,需要复写存入HashMap对象的hashCode()和equals()方法,存取的时候,先查找hashCode(),而后再根据key值遍历该hashCode下(实际上不同的hashCode可能存储在同一个顺序结构下)的table,利用key.equals()方法判断key是否相等,以便找到相应的value。
其实也就是判断Entry中的key是否与要查找的key相等,在HashMap中有一个静态内部了类Entry,有key,value,next三个重要属性
-->TreeMap:基于平衡树的映射表
通过key值排序,将Entry作为节点存入Tree,拿到key值可以在Entry中得到value
-->LinkedHashMap:
相比于HashMap增加了链表,可以按照存入顺序读取
Collection VS Collections
Collection是java.util下的接口,是set和list集合结构的父接口
Collections是java.util下的类,包含各种有关集合操作的静态方法
ArrayList VS Vector
Vector是线程同步的,ArrayList不是
需要增加长度时Vector增加一倍,ArrayList增加一半
HashMap VS Hashtable
Hashtable是线程安全的
HashMap可以让空值成为一个条目的key或value
Hahtable是基于陈旧的Dictionary类,而HashMap则是Java 1.2引进的Map接口的实现