Java整理(三)集合框架和数据结构

Java整理(三)集合框架和数据结构

Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:

集合框架(Collection)

枚举(Enumeration

位集合(BitSet)

向量(Vector)

队列(Queue

栈(Stack

字典(Dictionary

哈希表(Hashtable

属性(Properties)

集合框架(Collection)

集合包最常用的有Collection和Map两个接口的实现类,Colleciton用于存放多个单对象,Map用于存放Key-Value形式的键值对。

Collection中最常用的又分为两种类型的接口:List和Set,两者最明显的差别为List支持放入重复的元素,而Set不支持。

List最常用的实现类有:ArrayList、LinkedList、Vector及Stack;Set接口常用的实现类有:HashSet、TreeSet。

常见集合框架介绍

Collection

接口

List接口

ArrayList实现类

LinkedList实现类

Vector实现类

Stack实现类

Queue接口

Set接口

SortedSet接口

HashSet实现类

LinkedHashSet实现类

TreeSet实现类

Map接口

SortedMap接口

 

HashMap实现类

LinkedHashMap实现类

TreeMap实现类

 

WeakHashMap实现类

 

IdentityHashMap实现类

 

Hashtable实现类

Properties实现类

 

集合框架定义了一些接口。本节提供了每个接口的概述:

序号

接口描述

1

Collection 接口

Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。

2

List 接口

List接口是一个有序的Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的小标)来访问List中的元素,而且允许有相同的元素。

3

Set

Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。

4

SortedSet
继承于Set保存有序的集合。

5

Map
将唯一的键映射到值。

6

Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。

7

SortedMap
继承于Map,使Key保持在升序排列。

8

Enumeration
这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。

ArrayList类

ArrayList基于数组方式实现,默认构造器通过调用ArrayList(int)来完成创建,传入的值为10,实例化了一个Object数组,并将此数组赋给了当前实例的elementData属性,此Object数组的大小即为传入的initialCapacity,因此调用空构造器的情况下会创建一个大小为10的Object数组。

插入对象:add(E)

    基于已有元素数量加1作为名叫minCapacity的变量,比较此值和Object数组的大小,若大于数组值,那么先将当前Object数组值赋给一个数组对象,接着产生一个新的数组容量值。此值的计算方法为当前数组值*1.5+1,如得出的容量值仍然小于minCapacity,那么就以minCapacity作为新的容量值,调用Arrays.copyOf来生成新的数组对象

    还提供了add(int,E)这样的方法将元素直接插入指定的int位置上,将目前index及其后的数据都往后挪一位,然后才能将指定的index位置的赋值为传入的对象,这种方式要多付出一次复制数组的代价。还提供了addAll

删除对象:remove(E)

   这里调用了faseRemove方法index后的对象往前复制一位,并将数组中的最后一个元素的值设置为null,即释放了对此对象的引用。还提供了remove(int)方法来删除指定位置的对象,remove(int)的实现比remove(E)多了一个数组范围的检测,但少了对象位置的查找,因此性能会更好。

获取单个对象:get(int)

遍历对象:iterator()

判断对象是否存在:contains(E)

 总结:

1,ArrayList基于数组方式实现,无容量的限制;

2,ArrayList在执行插入元素时可能要扩容,在删除元素时并不会减小数组的容量(如希望相应的缩小数组容量,可以调用ArrayList的trimToSize()),在查找元素时要遍历数组,对于非null的元素采取equals的方式寻找;

3,ArrayList是非线程安全的。可以使用CopyOnWriteArrayList保证线程安全。

LinkedList类

LinkedList基于双向链表机制,所谓双向链表机制,就是集合中的每个元素都知道其前一个元素及其后一个元素的位置。在LinkedList中,以一个内部的Entry类来代表集合中的元素,元素的值赋给element属性,Entry中的next属性指向元素的后一个元素,Entry中的previous属性指向元素的前一个元素,基于这样的机制可以快速实现集合中元素的移动。

总结:

1,LinkedList基于双向链表机制实现;

2,LinkedList在插入元素时,须创建一个新的Entry对象,并切换相应元素的前后元素的引用;在查找元素时,须遍历链表;在删除元素时,要遍历链表,找到要删除的元素,然后从链表上将此元素删除即可,此时原有的前后元素改变引用连在一起;

3,LinkedList是非线程安全的。

HashSet类

默认构造创建一个HashMap对象,HashSet是基于HashMap实现的。

add(E):调用HashMapput方法来完成此操作,将需要增加的元素作为Map中的keyvalue则传入一个之前已创建的Object对象。

remove(E):调用HashMap的remove(E)方法完成此操作。

contains(E):HashMap的containsKey

iterator():调用HashMap的keySet的iterator方法。

HashSet不支持通过get(int)获取指定位置的元素,只能自行通过iterator方法来获取。

总结:

1,HashSet基于HashMap实现,无容量限制;

2,HashSet是非线程安全的。

TreeSetHashSet的主要不同在于TreeSet对于排序的支持,TreeSet基于TreeMap实现

HashMap类

HashMap空构造,将loadFactor设为默认的0.75,threshold设置为12,并创建一个大小为16的Entry对象数组。

基于数组+链表的结合体(链表散列)实现,当链表长度大于8时,则转换为红黑树实现,减少查找时间复杂度,将key-value看成一个整体,存放于Entity[]数组,put的时候根据key hash后的hashcode和数组length-1按位与的结果值判断放在数组的哪个位置,如果该数组位置上若已经存放其他元素,则在这个位置上的元素以链表的形式存放。如果该位置上没有元素则直接存放。

当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅只是根据key来计算并决定每个Entry的存储位置。我们完全可以把Map集合中的value当成key的附属,当系统决定了key的存储位置之后,value随之保存在那里即可。get取值也是根据key的hashCode确定在数组的位置,在根据key的equals确定在链表处的位置。

1 while (capacity < initialCapacity)

2     capacity <<= 1;

以上代码保证了初始化时HashMap的容量总是2的n次方,即底层数组的长度总是为2的n次方。它通过h & (table.length -1) 来得到该对象的保存位,若length为奇数值,则与运算产生相同结果,便会形成链表,尽可能的少出现链表才能提升hashMap的效率,所以这是hashMap速度上的优化。

扩容resize():

当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。

负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

HashMap的实现中,通过threshold字段来判断HashMap的最大容量。threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。

initialCapacity*2,成倍扩大容量,HashMap(int initialCapacity,float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。不设定参数,则初始容量值为16,默认的负载因子为0.75,不宜过大也不宜过小,过大影响效率,过小浪费空间。扩容后需要重新计算每个元素在数组中的位置,是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

HashTable数据结构的原理大致一样,区别在于put、get时加了同步关键字,而且HashTable不可存放null值。

在高并发时可以使用ConcurrentHashMap,其内部使用锁分段技术,维持这锁Segment的数组,在数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。

总结:

1,HashMap采用数组方式存储key、value构成的Entry对象,无容量限制;

2,HashMap基于key hash寻找Entry对象存放到数组的位置,对于hash冲突采用链表的方式解决;

3,HashMap在插入元素时可能会扩大数组的容量,在扩大容量时须要重新计算hash,并复制对象到新的数组中;

4,HashMap是非线程安全的。

TreeMap基于红黑树的实现,因此它要求一定要有key比较的方法,要么传入Comparator实现,要么key对象实现Comparable借口。在put操作时,基于红黑树的方式遍历,基于comparator来比较key应放在树的左边还是右边,如找到相等的key,则直接替换掉value。

遍历 ArrayList

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");
        list.add("HAHAHAHA");
        //第一种遍历方法使用foreach遍历List
        for (String str : list) {
            //也可以改写for(int i=0;i<list.size();i++)这种形式
            System.out.println(str);
        }
        //第二种遍历,把链表变为数组相关的内容进行遍历
        String[] strArray = new String[list.size()];
        list.toArray(strArray);
        for (int i = 0; i < strArray.length; i++) {
            //这里也可以改写为  foreach(String str:strArray)这种形式
            System.out.println(strArray[i]);
        }
        //第三种遍历 使用迭代器进行相关遍历
        Iterator<String> ite = list.iterator();
        while (ite.hasNext()) {
            //判断下一个元素之后有值
            System.out.println(ite.next());
        }
    }
}

 

遍历 Map

public class Main{
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("1", "value1");
        map.put("2", "value2");
        map.put("3", "value3");
        //第一种:普遍使用,二次取值
        System.out.println("通过Map.keySet遍历keyvalue");
        for (String key : map.keySet()) {
            System.out.println("key= "+ key + " and value= " + map.get(key));
        }
        //第二种
        System.out.println("通过Map.entrySet使用iterator遍历keyvalue");
        Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
        //第三种:推荐,尤其是容量大时
        System.out.println("通过Map.entrySet遍历keyvalue");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
        }
        //第四种
        System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
        for (String v : map.values()) {
            System.out.println("value= " + v);
        }
    }
}

 

传统集合特设类

Enumeration接口

Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。

这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。

常用方法

boolean hasMoreElements( )//测试此枚举是否包含更多的元素。

Object nextElement( )//如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

Bitset类

一个Bitset类创建一种特殊类型的数组来保存位值。

构造方法:

BitSet()

BitSet(int size)//初始化为0

方法摘要

 void

and(BitSet set)
          对此目标位 set 和参数位 set 执行逻辑与操作。

 void

andNot(BitSet set)
          清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。

 int

cardinality()
          返回此 BitSet 中设置为 true 的位数。

 void

clear()
          将此 BitSet 中的所有位设置为 false。

 void

clear(int bitIndex)
          将索引指定处的位设置为 false。

 void

clear(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。

 Object

clone()
          复制此 BitSet,生成一个与之相等的新 BitSet。

 boolean

equals(Object obj)
          将此对象与指定的对象进行比较。

 void

flip(int bitIndex)
          将指定索引处的位设置为其当前值的补码。

 void

flip(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。

 boolean

get(int bitIndex)
          返回指定索引处的位值。

 BitSet

get(int fromIndex, int toIndex)
          返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。

 int

hashCode()
          返回此位 set 的哈希码值。

 boolean

intersects(BitSet set)
          如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 ture。

 boolean

isEmpty()
          如果此 BitSet 中没有包含任何设置为 true 的位,则返回 ture。

 int

length()
          返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1。

 int

nextClearBit(int fromIndex)
          返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。

 int

nextSetBit(int fromIndex)
          返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。

 void

or(BitSet set)
          对此位 set 和位 set 参数执行逻辑或操作。

 void

set(int bitIndex)
          将指定索引处的位设置为 true。

 void

set(int bitIndex, boolean value)
          将指定索引处的位设置为指定的值。

 void

set(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。

 void

set(int fromIndex, int toIndex, boolean value)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。

 int

size()
          返回此 BitSet 表示位值时实际使用空间的位数。

 String

toString()
          返回此位 set 的字符串表示形式。

 void

xor(BitSet set)
          对此位 set 和位 set 参数执行逻辑异或操作。

 

Vector类

Vector类实现了一个动态数组。和ArrayList和相似,但是两者是不同的:

1、Vector是同步访问的。

2、Vector包含了许多传统的方法,这些方法不属于集合框架。

其add、remove、get(int)方法都加了synchronized关键字,默认创建一个大小为10的Object数组,并将capacityIncrement设置为0。容量扩充策略:如果capacityIncrement大于0,则将Object数组的大小扩大为现有size加上capacityIncrement的值;如果capacity等于或小于0,则将Object数组的大小扩大为现有size的两倍,这种容量的控制策略比ArrayList更为可控。

Vector是基于Synchronized实现的线程安全的ArrayList,但在插入元素时容量扩充的机制和ArrayList稍有不同,并可通过传入capacityIncrement来控制容量的扩充。

Queue类

队列(queue)是一种常用的数据结构,可以将队列看做是一种特殊的线性表,该结构遵循的先进先出原则。Java中,LinkedList实现了Queue接口,因为LinkedList进行插入、删除操作效率较高

相关常用方法:

boolean offer(E e):将元素追加到队列末尾,若添加成功则返回true。

E poll():从队首删除并返回该元素。

E peek():返回队首元素,但是不删除

Queue<String> queue = new LinkedList<String>();
queue.offer("a");//追加元素
queue.offer("b");
queue.offer("c");
queue.add("1");//offer
System.out.println(queue);//输出[a, b, c, 1]
String poll = queue.poll();//从队首取出元素并删除
queue.remove();//删除队首,同poll
String peek = queue.peek();//从队首取出元素但是不删除
queue.element();//peek

双向队列(Deque),是Queue的一个子接口,双向队列是指该队列两端的元素既能入队(offer)也能出队(poll),如果将Deque限制为只能从一端入队和出队,则可实现栈的数据结构。对于栈而言,有入栈(push)和出栈(pop),遵循先进后出原则

常用方法如下:

void push(E e):将给定元素”压入”栈中。存入的元素会在栈首。即:栈的第一个元素

E pop():将栈首元素删除并返回。

Deque<String> deque = new LinkedList<String>();
deque.push("a");
deque.push("b");
deque.push("c");
deque.add("1");//add在队尾加入元素
deque.push("2");//push类似于压入栈
deque.addFirst("x");//头加入,类似于push
deque.addLast("y");//尾加入,类似于add
deque.contains("1");//是否包含该元素true/false
deque.element();//栈首元素,不出栈
deque.offerFirst("(");//队首插入,返回boolean
deque.offerLast(")");//队尾插入,返回boolean
System.out.println(deque);//输出[(, x, 2, c, b, a, 1, y, )]
String str = deque.peek();//获取栈首元素后,元素不会出栈
str=deque.pop();//获取栈首元素,元素出栈

 

Stack类

栈是Vector的一个子类,它实现了一个标准的后进先出的栈。

堆栈只定义了默认构造函数,用来创建一个空栈。

Stack继承于Vector,在其基础上实现了Stack所要求的后进先出(LIFO)的弹出与压入操作,其提供了push、pop、peek三个主要的方法:

ush操作通过调用Vector中的addElement来完成;

pop操作通过调用peek来获取元素,并同时删除数组中的最后一个元素;

peek操作通过获取当前Object数组的大小,并获取数组上的最后一个元素。

Dictionary类

Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。

此类已过时。新的实现应该实现 Map 接口,而不是扩展此类。

方法摘要

abstract  Enumeration<V>

elements()
          返回此 dictionary 中值的枚举。

abstract  V

get(Object key)
          返回此 dictionary 中该键所映射到的值。

abstract  boolean

isEmpty()
          测试此 dictionary 是否不存在从键到值的映射。

abstract  Enumeration<K>

keys()
          返回此 dictionary 中的键的枚举。

abstract  V

put(K key, V value)
          将指定 key 映射到此 dictionary 中指定 value。

abstract  V

remove(Object key)
          从此 dictionary 中移除 key (及其相应的 value)。

abstract  int

size()
          返回此 dictionary 中条目(不同键)的数量。

 

Hashtable类

Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。

然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。非同步环境下推荐使用HashMap,同步环境下推荐使用ConcurrentHashMap

像HashMap一样,Hashtable在哈希表中存储键/值对。

构造方法摘要

Hashtable()
          用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表。

Hashtable(int initialCapacity)
          用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表。

Hashtable(int initialCapacity, float loadFactor)
          用指定初始容量和指定加载因子构造一个新的空哈希表。

Hashtable(Map<? extends K,? extends V> t)
          构造一个与给定的 Map 具有相同映射关系的新哈希表。

 

方法摘要

 void

clear()
          将此哈希表清空,使其不包含任何键。

 Object

clone()
          创建此哈希表的浅表副本。

 boolean

contains(Object value)
          测试此映射表中是否存在与指定值关联的键。

 boolean

containsKey(Object key)
          测试指定对象是否为此哈希表中的键。

 boolean

containsValue(Object value)
          如果此 Hashtable 将一个或多个键映射到此值,则返回 true。

 Enumeration<V>

elements()
          返回此哈希表中的值的枚举。

 Set<Map.Entry<K,V>>

entrySet()
          返回此映射中包含的键的 Set 视图。

 boolean

equals(Object o)
          按照 Map 接口的定义,比较指定 Object 与此 Map 是否相等。

 V

get(Object key)
          返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。

 int

hashCode()
          按照 Map 接口的定义,返回此 Map 的哈希码值。

 boolean

isEmpty()
          测试此哈希表是否没有键映射到值。

 Enumeration<K>

keys()
          返回此哈希表中的键的枚举。

 Set<K>

keySet()
          返回此映射中包含的键的 Set 视图。

 V

put(K key, V value)
          将指定 key 映射到此哈希表中的指定 value。

 void

putAll(Map<? extends K,? extends V> t)
          将指定映射的所有映射关系复制到此哈希表中,这些映射关系将替换此哈希表拥有的、针对当前指定映射中所有键的所有映射关系。

protected  void

rehash()
          增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。

 V

remove(Object key)
          从哈希表中移除该键及其相应的值。

 int

size()
          返回此哈希表中的键的数量。

 String

toString()
          返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。

 Collection<V>

values()
          返回此映射中包含的键的 Collection 视图。

 

Properties类

Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。

字段摘要

 

protected  Properties

defaults
          一个属性列表,包含属性列表中所有未找到值的键的默认值。

 

构造方法摘要

Properties()
          创建一个无默认值的空属性列表。

Properties(Properties defaults)
          创建一个带有指定默认值的空属性列表。

方法摘要

 String

getProperty(String key)
          用指定的键在此属性列表中搜索属性。

 String

getProperty(String key, String defaultValue)
          用指定的键在属性列表中搜索属性。

 void

list(PrintStream out)
          将属性列表输出到指定的输出流。

 void

list(PrintWriter out)
          将属性列表输出到指定的输出流。

 void

load(InputStream inStream)
          从输入流中读取属性列表(键和元素对)。

 void

load(Reader reader)
          按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

 void

loadFromXML(InputStream in)
          将指定输入流中由 XML 文档所表示的所有属性加载到此属性表中。

 Enumeration<?>

propertyNames()
          返回属性列表中所有键的枚举,如果在主属性列表中未找到同名的键,则包括默认属性列表中不同的键。

 void

save(OutputStream out, String comments)
          已过时。 如果在保存属性列表时发生 I/O 错误,则此方法不抛出 IOException。保存属性列表的首选方法是通过 store(OutputStream out, String comments) 方法或 storeToXML(OutputStream os, String comment) 方法来进行。

 Object

setProperty(String key, String value)
          调用 Hashtable 的方法 put。

 void

store(OutputStream out, String comments)
          以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。

 void

store(Writer writer, String comments)
          以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

 void

storeToXML(OutputStream os, String comment)
          发出一个表示此表中包含的所有属性的 XML 文档。

 void

storeToXML(OutputStream os, String comment, String encoding)
          使用指定的编码发出一个表示此表中包含的所有属性的 XML 文档。

 Set<String>

stringPropertyNames()
          返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。

 

Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:

枚举(Enumeration)

位集合(BitSet)

向量(Vector)

栈(Stack

字典(Dictionary

哈希表(Hashtable)

属性(Properties)

集合框架(Collection)

集合框架(Collection)

集合包最常用的有Collection和Map两个接口的实现类,Colleciton用于存放多个单对象,Map用于存放Key-Value形式的键值对。

Collection中最常用的又分为两种类型的接口:List和Set,两者最明显的差别为List支持放入重复的元素,而Set不支持。

List最常用的实现类有:ArrayList、LinkedList、Vector及Stack;Set接口常用的实现类有:HashSet、TreeSet。

常见集合框架介绍

Collection

接口

List接口

ArrayList实现类

LinkedList实现类

Vector实现类

Stack实现类

Queue接口

Set接口

SortedSet接口

HashSet实现类

LinkedHashSet实现类

TreeSet实现类

Map接口

SortedMap接口

 

HashMap实现类

LinkedHashMap实现类

TreeMap实现类

 

WeakHashMap实现类

 

IdentityHashMap实现类

 

Hashtable实现类

Properties实现类

 

集合框架定义了一些接口。本节提供了每个接口的概述:

序号

接口描述

1

Collection 接口

Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。

2

List 接口

List接口是一个有序的Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的小标)来访问List中的元素,而且允许有相同的元素。

3

Set

Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。

4

SortedSet
继承于Set保存有序的集合。

5

Map
将唯一的键映射到值。

6

Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。

7

SortedMap
继承于Map,使Key保持在升序排列。

8

Enumeration
这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。

 

ArrayList类

ArrayList基于数组方式实现,默认构造器通过调用ArrayList(int)来完成创建,传入的值为10,实例化了一个Object数组,并将此数组赋给了当前实例的elementData属性,此Object数组的大小即为传入的initialCapacity,因此调用空构造器的情况下会创建一个大小为10的Object数组。

插入对象:add(E)

    基于已有元素数量加1作为名叫minCapacity的变量,比较此值和Object数组的大小,若大于数组值,那么先将当前Object数组值赋给一个数组对象,接着产生一个新的数组容量值。此值的计算方法为当前数组值*1.5+1,如得出的容量值仍然小于minCapacity,那么就以minCapacity作为新的容量值,调用Arrays.copyOf来生成新的数组对象。

    还提供了add(int,E)这样的方法将元素直接插入指定的int位置上,将目前index及其后的数据都往后挪一位,然后才能将指定的index位置的赋值为传入的对象,这种方式要多付出一次复制数组的代价。还提供了addAll

删除对象:remove(E)

   这里调用了faseRemove方法将index后的对象往前复制一位,并将数组中的最后一个元素的值设置为null,即释放了对此对象的引用。 还提供了remove(int)方法来删除指定位置的对象,remove(int)的实现比remove(E)多了一个数组范围的检测,但少了对象位置的查找,因此性能会更好。

获取单个对象:get(int)

遍历对象:iterator()

判断对象是否存在:contains(E)

 总结:

1,ArrayList基于数组方式实现,无容量的限制;

2,ArrayList在执行插入元素时可能要扩容,在删除元素时并不会减小数组的容量(如希望相应的缩小数组容量,可以调用ArrayList的trimToSize()),在查找元素时要遍历数组,对于非null的元素采取equals的方式寻找;

3,ArrayList是非线程安全的。

LinkedList类

LinkedList基于双向链表机制,所谓双向链表机制,就是集合中的每个元素都知道其前一个元素及其后一个元素的位置。在LinkedList中,以一个内部的Entry类来代表集合中的元素,元素的值赋给element属性,Entry中的next属性指向元素的后一个元素,Entry中的previous属性指向元素的前一个元素,基于这样的机制可以快速实现集合中元素的移动。

总结:

1,LinkedList基于双向链表机制实现;

2,LinkedList在插入元素时,须创建一个新的Entry对象,并切换相应元素的前后元素的引用;在查找元素时,须遍历链表;在删除元素时,要遍历链表,找到要删除的元素,然后从链表上将此元素删除即可,此时原有的前后元素改变引用连在一起;

3,LinkedList是非线程安全的。

HashSet类

默认构造创建一个HashMap对象

add(E):调用HashMap的put方法来完成此操作,将需要增加的元素作为Map中的key,value则传入一个之前已创建的Object对象。

remove(E):调用HashMap的remove(E)方法完成此操作。

contains(E):HashMap的containsKey

iterator():调用HashMap的keySet的iterator方法。

HashSet不支持通过get(int)获取指定位置的元素,只能自行通过iterator方法来获取。

总结:

1,HashSet基于HashMap实现,无容量限制;

2,HashSet是非线程安全的。

 

TreeSet和HashSet的主要不同在于TreeSet对于排序的支持,TreeSet基于TreeMap实现。

HashMap类

HashMap空构造,将loadFactor设为默认的0.75,threshold设置为12,并创建一个大小为16的Entry对象数组。

基于数组+链表的结合体(链表散列)实现,将key-value看成一个整体,存放于Entity[]数组,put的时候根据key hash后的hashcode和数组length-1按位与的结果值判断放在数组的哪个位置,如果该数组位置上若已经存放其他元素,则在这个位置上的元素以链表的形式存放。如果该位置上没有元素则直接存放。

当系统决定存储HashMap中的key-value对时,完全没有考虑Entry中的value,仅仅只是根据key来计算并决定每个Entry的存储位置。我们完全可以把Map集合中的value当成key的附属,当系统决定了key的存储位置之后,value随之保存在那里即可。get取值也是根据key的hashCode确定在数组的位置,在根据key的equals确定在链表处的位置。

1 while (capacity < initialCapacity)

2     capacity <<= 1;

以上代码保证了初始化时HashMap的容量总是2的n次方,即底层数组的长度总是为2的n次方。它通过h & (table.length -1) 来得到该对象的保存位,若length为奇数值,则与运算产生相同结果,便会形成链表,尽可能的少出现链表才能提升hashMap的效率,所以这是hashMap速度上的优化。

扩容resize():

当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。

负载因子衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。负载因子越大,对空间的利用更充分,然而后果是查找效率的降低;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。

HashMap的实现中,通过threshold字段来判断HashMap的最大容量。threshold就是在此loadFactor和capacity对应下允许的最大元素数目,超过这个数目就重新resize,以降低实际的负载因子。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。

initialCapacity*2,成倍扩大容量,HashMap(int initialCapacity,float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。不设定参数,则初始容量值为16,默认的负载因子为0.75,不宜过大也不宜过小,过大影响效率,过小浪费空间。扩容后需要重新计算每个元素在数组中的位置,是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。

HashTable数据结构的原理大致一样,区别在于put、get时加了同步关键字,而且HashTable不可存放null值。

在高并发时可以使用ConcurrentHashMap,其内部使用锁分段技术,维持这锁Segment的数组,在数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。

总结:

1,HashMap采用数组方式存储key、value构成的Entry对象,无容量限制;

2,HashMap基于key hash寻找Entry对象存放到数组的位置,对于hash冲突采用链表的方式解决;

3,HashMap在插入元素时可能会扩大数组的容量,在扩大容量时须要重新计算hash,并复制对象到新的数组中;

4,HashMap是非线程安全的。

 

TreeMap基于红黑树的实现,因此它要求一定要有key比较的方法,要么传入Comparator实现,要么key对象实现Comparable借口。在put操作时,基于红黑树的方式遍历,基于comparator来比较key应放在树的左边还是右边,如找到相等的key,则直接替换掉value。

遍历 ArrayList

import java.util.*;

public class Test{

 public static void main(String[] args) {

    List<String> list=new ArrayList<String>();

    list.add("Hello");

    list.add("World");

    list.add("HAHAHAHA");

    //第一种遍历方法使用foreach遍历List

    for (String str : list) {           //也可以改写for(inti=0;i<list.size();i++)这种形式

       System.out.println(str);

    }

    //第二种遍历,把链表变为数组相关的内容进行遍历

    String[] strArray=new String[list.size()];

    list.toArray(strArray);

    for(int i=0;i<strArray.length;i++) //这里也可以改写为  foreach(String str:strArray)这种形式

    {

       System.out.println(strArray[i]);

    }

   //第三种遍历 使用迭代器进行相关遍历

    Iterator<String> ite=list.iterator();

    while(ite.hasNext())//判断下一个元素之后有值

    {

        System.out.println(ite.next());

    }

 }

}

遍历 Map

import java.util.*;

public class Test{

    public static void main(String[] args) {

     Map<String, String> map = new HashMap<String, String>();

     map.put("1", "value1");

     map.put("2", "value2");

     map.put("3", "value3");

     //第一种:普遍使用,二次取值

     System.out.println("通过Map.keySet遍历key和value:");

     for (String key : map.keySet()) {

      System.out.println("key= "+ key + " and value= " +map.get(key));

     }

     //第二种

     System.out.println("通过Map.entrySet使用iterator遍历key和value:");

     Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();

     while (it.hasNext()) {

      Map.Entry<String, String> entry = it.next();

      System.out.println("key= " + entry.getKey() + " andvalue= " + entry.getValue());

     }

     //第三种:推荐,尤其是容量大时

     System.out.println("通过Map.entrySet遍历key和value");

     for (Map.Entry<String, String> entry : map.entrySet()) {

      System.out.println("key= " + entry.getKey() + " andvalue= " + entry.getValue());

     }

     //第四种

     System.out.println("通过Map.values()遍历所有的value,但不能遍历key");

     for (String v : map.values()) {

      System.out.println("value= " + v);

     }

    }

}

 

传统集合特设类

Java Enumeration接口

Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。

这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。

常用方法

boolean hasMoreElements( )//测试此枚举是否包含更多的元素。

Object nextElement( )//如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

Java Bitset类

一个Bitset类创建一种特殊类型的数组来保存位值。

构造方法:

BitSet()

BitSet(int size)//初始化为0

方法摘要

 void

and(BitSet set)
          对此目标位 set 和参数位 set 执行逻辑与操作。

 void

andNot(BitSet set)
          清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。

 int

cardinality()
          返回此 BitSet 中设置为 true 的位数。

 void

clear()
          将此 BitSet 中的所有位设置为 false。

 void

clear(int bitIndex)
          将索引指定处的位设置为 false。

 void

clear(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。

 Object

clone()
          复制此 BitSet,生成一个与之相等的新 BitSet。

 boolean

equals(Object obj)
          将此对象与指定的对象进行比较。

 void

flip(int bitIndex)
          将指定索引处的位设置为其当前值的补码。

 void

flip(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。

 boolean

get(int bitIndex)
          返回指定索引处的位值。

 BitSet

get(int fromIndex, int toIndex)
          返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。

 int

hashCode()
          返回此位 set 的哈希码值。

 boolean

intersects(BitSet set)
          如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 ture。

 boolean

isEmpty()
          如果此 BitSet 中没有包含任何设置为 true 的位,则返回 ture。

 int

length()
          返回此 BitSet 的“逻辑大小”:BitSet 中最高设置位的索引加 1。

 int

nextClearBit(int fromIndex)
          返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。

 int

nextSetBit(int fromIndex)
          返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。

 void

or(BitSet set)
          对此位 set 和位 set 参数执行逻辑或操作。

 void

set(int bitIndex)
          将指定索引处的位设置为 true。

 void

set(int bitIndex, boolean value)
          将指定索引处的位设置为指定的值。

 void

set(int fromIndex, int toIndex)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。

 void

set(int fromIndex, int toIndex, boolean value)
          将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。

 int

size()
          返回此 BitSet 表示位值时实际使用空间的位数。

 String

toString()
          返回此位 set 的字符串表示形式。

 void

xor(BitSet set)
          对此位 set 和位 set 参数执行逻辑异或操作。

 

Java Vector类

Vector类实现了一个动态数组。和ArrayList和相似,但是两者是不同的:

1、Vector是同步访问的。

2、Vector包含了许多传统的方法,这些方法不属于集合框架。

其add、remove、get(int)方法都加了synchronized关键字,默认创建一个大小为10的Object数组,并将capacityIncrement设置为0。容量扩充策略:如果capacityIncrement大于0,则将Object数组的大小扩大为现有size加上capacityIncrement的值;如果capacity等于或小于0,则将Object数组的大小扩大为现有size的两倍,这种容量的控制策略比ArrayList更为可控。

Vector是基于Synchronized实现的线程安全的ArrayList,但在插入元素时容量扩充的机制和ArrayList稍有不同,并可通过传入capacityIncrement来控制容量的扩充。

Java Queue类

队列(queue)是一种常用的数据结构,可以将队列看做是一种特殊的线性表,该结构遵循的先进先出原则。Java中,LinkedList实现了Queue接口,因为LinkedList进行插入、删除操作效率较高

相关常用方法:

boolean offer(E e):将元素追加到队列末尾,若添加成功则返回true。

E poll():从队首删除并返回该元素。

E peek():返回队首元素,但是不删除

Queue<String> queue = new LinkedList<String>();
queue.offer("a");//追加元素
queue.offer("b");
queue.offer("c");
queue.add("1");//offer
System.out.println(queue);//输出[a, b, c, 1]
String poll = queue.poll();//从队首取出元素并删除
queue.remove();//删除队首,同poll
String peek = queue.peek();//从队首取出元素但是不删除
queue.element();//peek

双向队列(Deque),是Queue的一个子接口,双向队列是指该队列两端的元素既能入队(offer)也能出队(poll),如果将Deque限制为只能从一端入队和出队,则可实现栈的数据结构。对于栈而言,有入栈(push)和出栈(pop),遵循先进后出原则

常用方法如下:

void push(E e):将给定元素”压入”栈中。存入的元素会在栈首。即:栈的第一个元素

E pop():将栈首元素删除并返回。

Deque<String> deque = new LinkedList<String>();
deque.push("a");
deque.push("b");
deque.push("c");
deque.add("1");//add在队尾加入元素
deque.push("2");//push类似于压入栈
deque.addFirst("x");//头加入,类似于push
deque.addLast("y");//尾加入,类似于add
deque.contains("1");//是否包含该元素true/false
deque.element();//栈首元素,不出栈
deque.offerFirst("(");//队首插入,返回boolean
deque.offerLast(")");//队尾插入,返回boolean
System.out.println(deque);//输出[(, x, 2, c, b, a, 1, y, )]
String str = deque.peek();//获取栈首元素后,元素不会出栈
str=deque.pop();//获取栈首元素,元素出栈

 

Java Stack类

栈是Vector的一个子类,它实现了一个标准的后进先出的栈。

堆栈只定义了默认构造函数,用来创建一个空栈。

Stack继承于Vector,在其基础上实现了Stack所要求的后进先出(LIFO)的弹出与压入操作,其提供了push、pop、peek三个主要的方法:

ush操作通过调用Vector中的addElement来完成;

pop操作通过调用peek来获取元素,并同时删除数组中的最后一个元素;

peek操作通过获取当前Object数组的大小,并获取数组上的最后一个元素。

Java Dictionary类

Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。

注:此类已过时。新的实现应该实现 Map 接口,而不是扩展此类。

方法摘要

abstract  Enumeration<V>

elements()
          返回此 dictionary 中值的枚举。

abstract  V

get(Object key)
          返回此 dictionary 中该键所映射到的值。

abstract  boolean

isEmpty()
          测试此 dictionary 是否不存在从键到值的映射。

abstract  Enumeration<K>

keys()
          返回此 dictionary 中的键的枚举。

abstract  V

put(K key, V value)
          将指定 key 映射到此 dictionary 中指定 value。

abstract  V

remove(Object key)
          从此 dictionary 中移除 key (及其相应的 value)。

abstract  int

size()
          返回此 dictionary 中条目(不同键)的数量。

 

Java Hashtable类

Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。

然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。

像HashMap一样,Hashtable在哈希表中存储键/值对。

构造方法摘要

Hashtable()
          用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表。

Hashtable(int initialCapacity)
          用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表。

Hashtable(int initialCapacity, float loadFactor)
          用指定初始容量和指定加载因子构造一个新的空哈希表。

Hashtable(Map<? extends K,? extends V> t)
          构造一个与给定的 Map 具有相同映射关系的新哈希表。

 

方法摘要

 void

clear()
          将此哈希表清空,使其不包含任何键。

 Object

clone()
          创建此哈希表的浅表副本。

 boolean

contains(Object value)
          测试此映射表中是否存在与指定值关联的键。

 boolean

containsKey(Object key)
          测试指定对象是否为此哈希表中的键。

 boolean

containsValue(Object value)
          如果此 Hashtable 将一个或多个键映射到此值,则返回 true。

 Enumeration<V>

elements()
          返回此哈希表中的值的枚举。

 Set<Map.Entry<K,V>>

entrySet()
          返回此映射中包含的键的 Set 视图。

 boolean

equals(Object o)
          按照 Map 接口的定义,比较指定 Object 与此 Map 是否相等。

 V

get(Object key)
          返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。

 int

hashCode()
          按照 Map 接口的定义,返回此 Map 的哈希码值。

 boolean

isEmpty()
          测试此哈希表是否没有键映射到值。

 Enumeration<K>

keys()
          返回此哈希表中的键的枚举。

 Set<K>

keySet()
          返回此映射中包含的键的 Set 视图。

 V

put(K key, V value)
          将指定 key 映射到此哈希表中的指定 value。

 void

putAll(Map<? extends K,? extends V> t)
          将指定映射的所有映射关系复制到此哈希表中,这些映射关系将替换此哈希表拥有的、针对当前指定映射中所有键的所有映射关系。

protected  void

rehash()
          增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。

 V

remove(Object key)
          从哈希表中移除该键及其相应的值。

 int

size()
          返回此哈希表中的键的数量。

 String

toString()
          返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。

 Collection<V>

values()
          返回此映射中包含的键的 Collection 视图。

 

Java Properties类

Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。

字段摘要

 

protected  Properties

defaults
          一个属性列表,包含属性列表中所有未找到值的键的默认值。

 

构造方法摘要

Properties()
          创建一个无默认值的空属性列表。

Properties(Properties defaults)
          创建一个带有指定默认值的空属性列表。

方法摘要

 String

getProperty(String key)
          用指定的键在此属性列表中搜索属性。

 String

getProperty(String key, String defaultValue)
          用指定的键在属性列表中搜索属性。

 void

list(PrintStream out)
          将属性列表输出到指定的输出流。

 void

list(PrintWriter out)
          将属性列表输出到指定的输出流。

 void

load(InputStream inStream)
          从输入流中读取属性列表(键和元素对)。

 void

load(Reader reader)
          按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。

 void

loadFromXML(InputStream in)
          将指定输入流中由 XML 文档所表示的所有属性加载到此属性表中。

 Enumeration<?>

propertyNames()
          返回属性列表中所有键的枚举,如果在主属性列表中未找到同名的键,则包括默认属性列表中不同的键。

 void

save(OutputStream out, String comments)
          已过时。 如果在保存属性列表时发生 I/O 错误,则此方法不抛出 IOException。保存属性列表的首选方法是通过 store(OutputStream out, String comments) 方法或 storeToXML(OutputStream os, String comment) 方法来进行。

 Object

setProperty(String key, String value)
          调用 Hashtable 的方法 put。

 void

store(OutputStream out, String comments)
          以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。

 void

store(Writer writer, String comments)
          以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。

 void

storeToXML(OutputStream os, String comment)
          发出一个表示此表中包含的所有属性的 XML 文档。

 void

storeToXML(OutputStream os, String comment, String encoding)
          使用指定的编码发出一个表示此表中包含的所有属性的 XML 文档。

 Set<String>

stringPropertyNames()
          返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值