您的位置:首页 > 编程语言 > Java开发

Java 集合框架梳理

2017-11-23 22:48 357 查看

java 集合框架

   主要有两大分支: Map接口
Map主要实现数据结构有序线程安全父类其他特性
HashMap哈希表随机存储线程不安全AbstractMap访问速度快
LinkedHashMap链表有序存储线程安全HashMap遍历比HashMap慢
Hashtable哈希表随机存储线程安全HashMap写入较慢
TreeMap有序存储线程不安全AbstractMap默认是按键值的升序排序,指定排序的比较器
Collection接口
List接口主要实现数据结构线程安全其他特性
Vector数组线程安全增删和查询都很慢
Stack数组线程安全后进先出
ArrayList数组线程不安全查询速度快
LinkedList链表线程不安全增删元素的速度很快
Set接口主要实现数据结构线程安全其他特性
HashSet哈希表线程不安全查询速度最快集合
LinkedHashSet链表线程不安全线程不安全
TreeSet线程不安全有序的
EnumSet不确定线程不安全枚举的专用Set

Map接口

  Map没有继承Collection接口,提供3种集的视图;  其内容可以被当作一组key的集KeySet,一组value的集Values,和一组key-value映射EntrySet;  要遍历一个Map可以从这三个视图入手,根据不同的需要使用相应的视图。
视图对应集其他特性
Set
<
K
>
KeySet
key的集待完善
Clloection
<
V
>
Values
value的集待完善
Set
<
Map.Entry
<
K,V
>
>
EntrySet
key-value映射集待完善

Map接口主要实现类

Hashtable

  public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
......
}
  Hashtable实现Map接口继承Dictionary,实现一个key-value映射的哈希表。  任何非空(non-null)的对象都可作为key或者value。  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。构造方法  它有三个构造方法:  构造方法1:入参为初始容量(initialCapacity)和装填因子(loadFactor)
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);

if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
initHashSeedAsNeeded(initialCapacity);
}
  构造方法2:入参为初始容量(initialCapacity)
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
  构造方法3:无参的构造方法
public Hashtable() {
this(11, 0.75f);
}
  构造方法3生成的hashtable容量为11,装填因子为0.75,也就是当该hashtable的数据达到容量的3/4[11×0.75=8]的时候会进行扩容同时进行再散列(rehash()),代码主要是put方法中执行这个动作:  其中 : threshold=initialCapacity * loadFactor(在第一个构造方法中)
  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  ......
  ......
  //第一个构造方法中
  threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}

// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
4000
}

modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();

tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
}

// Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}
  扩容:会将容量增致原容量的两倍,新容量为:11*2+1=23
//rehash()方法中
int newCapacity = (oldCapacity << 1) + 1;
同步机制  Hashtable是线程安全的,关键方法都是同步的:
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
 圈中的都是同步的方法

小结  Hashtable 的实例有两个参数影响其性能:初始容量initial capacity 和加载因子load factor。  容量与加载因子在HashMap中总结。  初始容量和加载因子这两个参数只是对该实现的提示。  关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。  由所有类的“collection 视图方法”返回的 collection 的 iterator 方法返回的迭代器都是快速失败 的:在创建 Iterator 之后,如果从结构上对 Hashtable 进行修改,除非通过 Iterator 自身的 remove 方法;  否则在任何时间以任何方式对其进行修改,Iterator 都将抛出ConcurrentModificationException。  因此,面对并发的修改,Iterator 很快就会完全失败,而不冒在将来某个不确定的时间发生任意不确定行为的风险。由 Hashtable 的键和元素方法返回的 Enumeration 不 是快速失败的。

HashMap类

public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
......
}
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。构造方法  HashMap有跟Hashtable一样的三个构造方法,不同的是不带参数的那个:
//static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}  
  HashMap初始容量为16,加载因子也为0.75。  迭代集合视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)的和成比例;  如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低),  容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。  加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。  当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。同步机制  HashMap不是同步的。  如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。 
结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与实例已经包含的键关联的值不是结构上的修改。
  这一般通过对自然封装该映射的对象进行同步操作来完成,HashMap不存在这样的方法,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。  在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
Map map = Collections.synchronizedMap(new HashMap(...));
  由所有此类的“集合视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 或 add方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。  因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间任意发生不确定行为的风险。  注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出。编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。递四方速递HashMap的遍历  Map的实现基本上都是这样遍历的,使用的是Map提供的三个视图:1. entrySet().iterator()
Map map = new HashMap();
for (int i = 0; i < 1000000; i++) {
map.put(i, i);
}

Iterator iterator = map.entrySet().iterator();
long begin = System.currentTimeMillis();
while (iterator.hasNext()) {
Entry next = (Entry) iterator.next();
Object key = next.getKey();
Object value = next.getValue();
// System.out.println(">>>>>key>>>>>" + next.getKey() + ">>>>>value>>>" +
// next.getValue());
}
long end = System.currentTimeMillis();
System.out.println("---myHashMap1 - cost---" + (end - begin));
keySet()
Map map = new HashMap();
for (int i = 0; i < 1000000; i++) {
map.put(i, i);
}

long begin = System.currentTimeMillis();
for (Object obj : map.keySet()) {
Object key = obj;
Object value = map.get(key);
// System.out.println(">>>>>key>>>>>" + key + ">>>>>value>>>" + map.get(key));
}
long end = System.currentTimeMillis();
System.out.println("---myHashMap2 - cost---" + (end - begin));
entrySet()
Map map = new HashMap();
for (int i = 0; i < 1000000; i++) {
map.put(i, i);
}

long begin = System.currentTimeMillis();
for (Object object : map.entrySet()) {
Map.Entry<Integer, Integer> entry = (Entry<Integer, Integer>) object;
Integer key = entry.getKey();
Integer value = entry.getValue();
// System.out.println(">>>>>key>>>>>" + entry.getKey() + ">>>>>value>>>" +
// entry.getValue());
}
long end = System.currentTimeMillis();
System.out.println("---myHashMap3 - cost---" + (end - begin));
  结果:
---myHashMap1 - cost---20
---myHashMap2 - cost---22
---myHashMap3 - cost---15
put方法  HashMap的put方法:
 public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)//如果key为空的情况
return putForNullKey(value);
int hash = hash(key);//计算key的hash值
int i = indexFor(hash, table.length); //计算该hash值在table中的下标
 for (Entry<K,V> e = table[i]; e != null; e = e.next) {//对table[i]存放的链表进行遍历
Object k;
//判断该条链上是否有hash值相同的(key相同)
//若存在相同,则直接覆盖value,返回旧value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;//把新的值赋予给对应键值
e.recordAccess(this);//空方法,留待实现
return oldValue;//返回相同键值的对应的旧的值
}
}
modCount++;//结构性更改的次数+1
addEntry(hash, key, value, i); //把当前key,value添加到table[i]的链表中
return null;//没有相同的键值返回
  put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值, HashMap 的结构是一个table加上在相应位置的Entry的链表。
public Object put(Object key, Object value) {
Object k = maskNull(key);
  put方法中这段是判断键值是否为空,如果为空,它会返回一个static Object 作为键值,这就是HashMap允许空键值的原因。
int hash = hash(k);
int i = indexFor(hash, table.length);
  hash 就是通过 key 这个Object的 hashcode 进行 hash计算,正确的返回索引,然后通过 indexFor 获得在Object table的索引值。对于hash操作,最重要也是最困难的就是如何确定hash的位置:
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>>
fa64
; 4);
}
 计算该hash值在table中的下标:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 :
//"length must be a non-zero power of 2";
return h & (length-1);
}
  对于HashMap的table而言,数据分布需要均匀(最好每项都只有一个元素,这样就可以直接找到),不能太紧也不能太松,太紧会导致查询速度慢,太松则浪费空间。计算hash值后,怎么才能保证table元素分布均与呢?我们会想到取模,但是由于取模的消耗较大,而HashMap是通过&运算符(按位与操作)来实现的:h & (length-1)  在构造函数中存在:capacity <<= 1,这样做总是能够保证HashMap的底层数组长度为2的n次方。当length为2的n次方时,h&(length - 1)就相当于对length取模,而且速度比直接取模快得多,这是HashMap在速度上的一个优化。至于为什么是2的n次方下面解释。  我们回到indexFor方法,该方法仅有一条语句:h&(length - 1),这句话除了上面的取模运算外还有一个非常重要的责任:均匀分布table数据和充分利用空间。  有个length为16(2^n)和15,h为5、6、7的小实验length15发生的碰撞是非常多的,1,3,5,7,9,11,13都没有存放数据,空间减少,进一步增加碰撞几率,这样就会导致查询速度慢,当length-1 = 14时,二进制的最后一位是0,在&操作时,一个为0,无论另一个为1还是0,最终&操作结果都是0,这就造成了结果的二进制的最后一位都是0,这就导致了所有数据都存储在2的倍数位上,所以说,所以说当length = 2^n时,不同的hash值发生碰撞的概率比较小,这样就会使得数据在table数组中分布较均匀,查询速度也较快。   计算了hash值,并用该hash值来求得哈希表中的索引值之后,如何把该key-value插入到该索引的链表中:  调用 addEntry(hash, key, value, i) 方法:
//addEntry方法
void addEntry(int hash, K key, V value, int bucketIndex) {
//如果size大于极限容量,将要进行重建内部数据结构操作,之后的容量是原来的两倍,并且重新设置hash值和hash值在table中的索引值
if ((size >= threshold) && (null != table[bucketIndex])) {
//threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
//threshold 是实际容纳的量
resize(2 * table.length);//容量扩至初始容量的两倍
hash = (null != key) ? hash(key) : 0;
//这一步就是对null的处理,如果key为null,hash值为0,也就是会插入到哈希表的表头table[0]的位置
bucketIndex = indexFor(hash, table.length);
}
//真正创建Entry节点的操作
createEntry(hash, key, value, bucketIndex);
}
//createEntry方法
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++;
}
首先取得bucketIndex位置的Entry头结点,并创建新节点,把该新节点插入到链表中的头部,该新节点的next指针指向原来的头结点这里有两点需要注意:  一、链的产生  这是一个非常优雅的设计。系统总是将新的Entry对象添加到bucketIndex处。如果bucketIndex处已经有了对象,那么新添加的Entry对象将指向原有的Entry对象,形成一条Entry链,但是若bucketIndex处没有Entry对象,也就是e==null,那么新添加的Entry对象指向null,也就不会产生Entry链了。  二、扩容问题  还记得HashMap中的一个变量吗,threshold,这是容器的容量极限,还有一个变量size,这是指HashMap中键值对的数量,也就是node的数量
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
什么时候发生扩容?  当不断添加key-value,size大于了容量极限threshold时,会发生扩容  如何扩容?  扩容发生在resize方法中,也就是扩大数组(桶)的数量,如何扩容参考:http://blog.csdn.net/jeffleo/article/details/63684953汇总  HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了AbstractMap类,里面的 Iterator 主要都是其内部类HashIterator 和其他几个 iterator 类实现,还有一个很重要的继承了Map.Entry 的 Entry 内部类;  Entry 内部类,它包含了hash,value,key 和next 这四个属性,很重要。1. 传入key和value,判断key是否为null,如果为null,则调用putForNullKey,以null作为key存储到哈希表中;2. 然后计算key的hash值,根据hash值搜索在哈希表table中的索引位置,若当前索引位置不为null,则对该位置的Entry链表进行遍历,如果链中存在该key,则用传入的value覆盖掉旧的value,同时把旧的value返回,结束;3. 否则调用addEntry,用key-value创建一个新的节点,并把该节点插入到该索引对应的链表的头部

LinkedHashMap类

  LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同。  LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析)。  LinkedHashMap同样是非线程安全的,只在单线程环境下使用。  在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

WeakHashMap类

  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

Map实现类总结

由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。HashMap和Hashtable (table是小写)a.HashMap不是线程安全的;HashTable是线程安全的,其线程安全是通过Sychronize实现。b.由于上述原因,HashMap效率高于HashTable。 c.HashMap的键可以为null,HashTable不可以。d.多线程环境下,通常也不是用HashTable,因为效率低。HashMap配合Collections工具类使用实现线程安全。同时还有ConcurrentHashMap可以选择,该类的线程安全是通过Lock的方式实现的,所以效率高于Hashtable。

Collection接口

Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。所有实现Collection接口的类都必须提供两个标准的构造函数:第一个:无参数的构造函数,用于创建一个空的Collection;第二个:带Collection参数的构造函数,用于创建一个新的Collection,新的Collection与传入的Collection有相同的元素;该构造函数允许用户复制一个Collection。

遍历Collection中的元素

  Collection接口继承了Iterable接口,不论Collection的实际类型如何,它都支持一个iterator()的方法,  该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个element。  典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子    while(it.hasNext()) {      Object obj = it.next(); // 得到下一个元素    }
  由Collection接口派生的接口主要有:List、Set和Queue。

List接口

  List是有序的c,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。和下面要提到的Set不同,List允许有相同的元素。  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

List接口主要实现类

LinkedList类  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。  一种解决方法是在创建List时构造一个同步的List:    List list = Collections.synchronizedList(new LinkedList(…));ArrayList类  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增 加,但是增长算法 并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。Vector类  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
ArrayList arrayList = new ArrayList(100);arrayList.ensureCapacity(200);Vector vector = new Vector(100);vector.ensureCapacity(200);
Stack 类  Stack继承自Vector,线程安全什么的跟Vector都差不多,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。有几个地方需要注意:第一:add()和push(),stack是将最后一个element作为栈顶的,所以这两个方法对stack而言是没什么区别的,但是,它们的返回值不一样,add()返回boolean,就是添加成功了没有;push()返回的是你添加的元素。为了可读性以及将它跟栈有一丢丢联系,推荐使用push。第二:peek()和pop(),这两个方法都能得到栈顶元素,区别是peek()只是读取,对原栈没有什么影响;pop(),从字面上就能理解,出栈,所以原栈的栈顶元素就没了。

Set接口

  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。  Set集合类的特点就是可以去重,它们的内部实现都是基于Map的,用的是Map的key,所以知道为什么可以去重复了吧。  比较是基于hascode()方法和equals()方法的,所以必要情况下需要重写这两个方法。  既然要去重,那么就需要比较,既然要比较,那么就需要了解怎么比较的,不然它将1等于2了,你怎么办?

Set接口主要实现类

HashSetHashSet是散列表的一个实现,存储是无序的,读取也是无序的;装填因子:load factor=0.75装填元素超过了容量的%75时,会进行再散列。看下面例子:
        Set s = new HashSet();String[] a = "a,b,c,d,e,f,g".split(",");System.out.println(">>myHashSet1>>start");for(String aa : a)s.add(aa);Iterator i = s.iterator();while(i.hasNext())System.out.println(">>>"+i.next());System.out.println(">>myHashSet1>>end");
运行结果:
>>myHashSet1>>start>>>f>>>g>>>d>>>e>>>b>>>c>>>a>>myHashSet1>>end
可以看到当一个数组被有顺序的存进一个HashSet后,存储是随机的。TreeSet这是一个树结构的Set,存的时候是以树结构(红黑树)存储的,遍历时拿到的是排好序了的。
        Set s = new TreeSet();String[] a = "d,c,g,f,a,b,e".split(",");for(String aa:a)s.add(aa);Iterator i = s.iterator();while(i.hasNext())System.out.println(">>>>"+i.next());
运行结果:
>>>>a>>>>b>>>>c>>>>d>>>>e>>>>f>>>>g

归纳

可以重复的 是无序的 不能重复的 是有序的ArrayList,LinkedList一个无序,一个有序;HashSet,TreeSet一个无序,一个有序;HashMap,LinkedHasmMap,一个无序,一个有序;Vector和HashTable,Stack是线程安全的,但是效率低;线程不安全的类都可以配合Collections得到线程安全的类。ArrayList , LinkedList , Vector1.它们的关系和区别。ArrayList和Vector本质都是用数组实现的,而LinkList是用双链表实现的;所以,Arraylist和Vector在查找效率上比较高,增删效率比较低;LinkedList则正好相反。ArrayList是线程不安全的,Vector是线程安全的,效率肯定没有ArrayList高了。实际中一般也不怎么用Vector,可以自己做线程同步,也可以用Collections配合ArrayList实现线程同步。2.前面多次提到扩容的代价很高,所以如果能确定容量的大致范围就可以在创建实例的时候指定,注意,这个仅限于ArrayList和Vector。
其他功能实现a.排序List的排序的话就是使用Collections的sort方法,构造Comparator或者让List中的对象实现Comparaable都可以,这里就不贴代码了。b.去重第一种:用Iterator遍历,遍历出来的放到一个临时List中,放之前用contains判断一下。第二种:利用set的不可重复性,只需三步走。
//第一步:用HashSet的特性去重HashSet tempSet = new HashSet(arrayList);//第二步:将arrayList清除tempSet.clear();//第三步:将去重后的重新赋给ListarrayList.addAll(tempSet);
http://blog.csdn.net/HHcoco/article/details/53117525
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  集合框架