0xCAFEBABE

talk is cheap show me the code

0%

java集合框架

by lmm

  • 集合框架的概述

​ 集合,数组都是对多个数据进行存储操作的结构,简称Java容器

​ 说明:此时的存储,主要指的是内存里面的存储,不涉及到持久化的存储

  • 数组在存储多个数据方面的特点:

​ 一旦初始化之后,其长度就确定了

​ 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了

​ 比如:String[] arr;int[] arr1;Object[] arr2

  • 数组在存储多个数据方面的缺点:

​ 一旦初始化以后,其长度就不可修改

​ 数组中提供的方法非常有限,对于添加,删除,插入数据等操作,非常不便,同时效率不高

​ 获取数组中实际元素的个数的需求,数组没有现成的属性和方法可用

​ 数组存储数据的特点:有序,可重复性,对于无序,不可重复的需求,不能满足

Java集合可分为Collection 和 Map两种体系

  • Collection接口:单列集合,定义了存取一组对象的方法的集合

​ List接口:元素有序,可重复的集合 — > 动态数组

​ List实现类:ArrayList,LinkedList,Vector

​ Set接口: 元素无序,不可重复的集合

​ Set实现类:HashSet,LinkedHashSet,TreeSet

  • Map接口:双列集合,保存具有映射关系”key - value对”的集合

​ Map实现类:HashMap,LinkedHashMap,TreeMap,Hashtable,Properties

Collection接口常用的方法

  • contains (Object obj):1.判断当前集合中是否包含obj,我们在判断时会调用obj对象所在类的equals()

​ 2.向Collection接口实现类的对象中添加数据obj时,要求obj所在的类重写

​ equals()

  • remove(Object obj):从当前集合中移除obj元素

  • removeAll():从当前集合中移除所有元素

  • retainAll():交集:获取当前集合和其他集合的交集,并返回给当前集合

  • 集合 —- > 数组: toArray()

  • 数组 —- > 集合:调用Arrays类中的asList()方法

1
List<String> list = Arrays.asList(new String[]{"AA","BB","CC"});

集合元素的遍历

Iterator:迭代器,用于Collection遍历

1
2
3
4
5
6
Collectio coll = new ArrayList();
Iterator iterator = coll.iterator();
while(iterator.hasNext()){//hasNext()判断集合中是否还有下一个元素
//next():指针下移,将下移以后集合位置上的元素返回
System.put.println(iterator.next());
}
  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前

  • Iterator中的remove()方法,可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()

1
2
3
4
5
6
7
Iterator iterator = coll.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
if("".equals(obj)){
iterator.remove();
}
}

foreach循环

  • jdk5.0 新增了foreach循环,用于遍历集合,数组
1
2
3
4
5
6
7
8
9
10
11
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//for(集合元素的类型 局部变量 : 集合对象)
//内部任然调用了迭代器
for(Object obj : coll){
System.out.println(obj)
}
1
2
3
4
5
int[] arr = new int[]{1,2,3,4,5,6}
//for(int i=0;i<arr.length;i++)
for(int i :arr){
System.out.pritln(i);
}

List接口

面试题:ArrayList,LinkdList,Vector三者的异同?

同:三个类都是现实了List接口,存储数据的特点相同:存储有序的,可重复的数

​ 据

不同:

ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储

LinkedList:对于频繁的插入,删除操作,使用此类效率比ArrayList高;底层使用双向链存储

Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

  • ArrayList源码分析:jdk 7情况下
1
2
3
4
5
6
7
8
9
10
11
public transient Object[] elementData;//使用Object存储
//空参构造器
public ArrayList(){
this(10);
}
public ArrayList(int initalCapacity){
super();
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity:"+initialCapacity);
this.elementData = new Object[initialCapacity];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public boolean add<E e>{
ensureCapacityInteral(minCapacity:size+1);//确认容量是否够Increments modCount
elementData[size++] = e;
return true;
}
public void ensureCapacityInternal(int minCapacity){
modCount++;
if(minCapacity - elementData.length > 0) {//elementData.length:本身底层数组比较
grow(minCapacity);
}
}
private void grow(int minCapacity){
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//oldCapacity >> 1oldCapacity >> 向右移一位
if(newCapacity - minCapacity < 0){
newCapacity = minCapacity;
}
if(newCapacity - MAX_ARRAY_SIZE>0){
newCapacity = hugeCapacity(minCapacity);
}
elementData = Arrays.copyOf(elementData,newCapacity);
}
1
ArrayList list = new ArrayList();//底层创建了长度是10的Object[] 数组 elementData

默认情况下,扩容为原来的容量的1.5倍,同时需要将原有的数组中的数据复制到新的数组中

结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity);

  • jdk 8中ArrayList 的变化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public ArrayList(){
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
}
private static final Object[]DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public boolean add<E e>{
ensureCapacityInteral(minCapacity:size+1);//确认容量是否够Increments modCount
elementData[size++] = e;
return true;
}
public void ensureCapacityInternal(int minCapacity){
if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA ){
minCapacity = Math.max(DEFAULT_CAPACITY,minCapacity);
//DEFAULT_CAPACITY:10
}
ensureExplicitCapacity(int minCapacity);
}
private void ensureExplicitCapacity(int minCapacity){
modCount++;
if(minCapacity - elementData.length > 0){
grow(minCapacity);
}
}

1.ArrayList list = new ArrayList();底层Object[] elementData 初始化为{},并没有创建长度为10的数组

2.第一次调用add()才创建长度为10的数组

3.小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存

  • LinkList 源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
transient Node<E> first;
transient Node<E> last;//Node存储数据的基本单位
//内部类:
private static class Node<E>{
E item;//添加的核心数据
Node<E> next;
Node<E> prev;
Node(Node<E> prev,E element,Node<E> next){
this.item = element;
this.next = next;
this.prev = prev;
}
}
public boolean add(E e){
linkLast(e);
return ture;
}
void linkLast(E e){
final Node<E> l = last;
final Node<E> newNode = new Node<>(1, e, null);//添加e
last = newNode;
if(l == null){
frist = newNode;
}else{
l.next = newNode;
size++;
nodCount++;
}
}
  • Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组长度的2倍。

List接口中常用的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ArrayList list = new ArrayList;
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",age:12));
list.add(456);
//添加数据 void add(int index,Object ele):在index位置插入ele元素
list.add(1,"BB");
//Object get(int index):获取指定index位置的元素
list.get(0);//123
//int indexOf(Object obj):返回obj在集合中首次出现的位置
list.indexOf(123);//1
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
//Object remove(int index):移除指定index位置的元素,并返回此元素
//Object set(int index,Object ele):设置指定index位置的元素为ele
list.set(1."cc");
//list subList(int fromIndex, int toIndex):返回fromIndex到toIndex位置的左闭右开区间
  • 总结:常用的方法

增:add(Object obj) , 删:remoove(int index)/ remove(Object obj),改:set(int index, Object ele) ,改:set(int index,Object ele) ,查 :get(int index) , 插:add(int index.Object ele) 长度:size()

遍历:1.Iterator迭代器 2.增强for循环 3.普通的循环

1
2
3
4
5
6
7
8
//方式一:
Ieterator iterator = list.iterator();
while(iterator.hasNext()){
}
//方式二:
for(Object obj : list){
}
//方式三:普通for循环

set接口:无序的,不可重复的数据

  • HashSet:作为Set接口的主要实现类:线程不安全的;可以存储null值

  • LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序排列

  • TreeSet:可以按照添加对象的指定属性,进行排序

  • Set接口中没有额外定义新的方法使用的都是Collection中声明过的方法

  • 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定

  • 不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个

  • HsahSet添加元素的过程:

​ 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计

算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中存放

的位置(即为:索引位置),判断数组此位置上是否已经有元素:

​ 如果此位置上没有其他元素,则元素a添加成功 —- >情况1

​ 如果此位置上有其他元素b(或以链表形式存在的多个元素) ,则比较元素a与元素b的hash值:

​ 如果hash值不相同,则元素a添加成功。— >情况2

​ 如果hash值相同,进而需要调用元素a所在类的equlas()方法:

​ equals()返回true,则元素a添加失败

​ equals()返回false,则元素a添加成功 —>情况3

对于添加成功的情况和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储

jdk 7:元素a放到数组中,指向原来的元素

jdk 8: 原来的元素在数组,指向添加的元素

总结:七上八下

1
2
3
4
5
6
7
8
9
10
11
12
public boolean equals(Object o){
if(this == o) return ture;
if(o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if(age != user.age) return false;
return name !=null? name.equals(user.name) : user.name == null;
}
public int hashCode(){
int result = name != null ? name.hashCode() : 0;
result = 31*result + age;
return result;
}

以Eclipse/IDEA为例,在自定义类中可以调用工具自动重写equals和hashCode.* 问题 :为什么用Eclipse/IDEA复写hashCode方法,有31这个数字?*

  • 选择系数的时候要选择尽量大的系数。因为如果计算出来hash地址越大,所谓”冲突”就越少,查找起来效率也会提高(减少冲突)

  • 31只占用5bits,相乘造成数据溢出的概率较小

  • 31可以由i*31==(i<<5)-1来表示,现在很多虚拟机里面都有相关优化。(提高算法效率)

  • 31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除(减少冲突)

要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()

Map

双列数据 ,存储 key - value 对的数据 —- 类似于高中函数:y=f(x)

HashMap:作为Map的主要实现类;线程不安全,效率高;存储null的key和value

​ jdk7:数组+链表

​ jdk8:数组+链表+红黑树

LinkedHashMap:HashMap的子类,保证在遍历map元素时,可以按照添加的顺序实现遍历。

​ 原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap

TreeMap:保证按照添加的key-value对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序,底层使用红黑树

Hashtable:作为古老的实现类;线程安全的,效率低

Properties:常用来处置配置文件。key和value都是String类型

二,Map结构的理解

Map中的key:无序的,不可重复的,使用Set存储所有的key — > key所在类要重写equals()和hashCode()

Map中的value:无序的,可重复的,使用Collection存储所有的value

一个键值对:key-value构成了Entry对象

Map中的entry:无序的,不可重复的,使用Set存储所有的entry

  • HashMap的底层实现原理?以jdk7为例说明

HashMap map = new HashMap();

在实例化以后,底层创建了长度是16的一维数组Entry[ ] table.

… 可能已经执行过多次put …

map.put(key1,value1):

首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置

如果此位置上的数据为空,此时的key - value添加成功。 —情况1

如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在一个或多个数据的哈希值:

​ 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功 — 情况2

​ 如果key1的哈希值和已经存在的某一个数据(key2 - value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)

​ 如果equals()返回false:此时key1-value1添加成功。— 情况3

​ 如果equals()返回true:使用value1替换value2

补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储

在不断添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来

  • jdk8 相较于jdk 7在底层实现方面的不同:

1.new HashMap():底层没有创建一个长度为16的数组

2.jdk 8底层的数组是:Node[] ,而非Entry[ ]

3.首次调用put()方法时,底层创建长度为16的数组

4.jdk7底层结构只有:数组+链表,jdk8中底层结构:数组 + 链表+红黑树

当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度>64时,

此时次索引位置上的所有数据改为使用红黑树存储

  • HashMap在JDK7的源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public HashMap(){
//DEFAJLT_INITAL_CAPACITY:创建的数组长度,默认16;DEFAULT_LOAD_FACTOR:加载因子
this(DEFAJLT_INITAL_CAPACITY,DEFAULT_LOAD_FACTOR);
}
//this --- >
public HashMap(int initialCapacity,float loadFactor){
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity:"+initialCapacity);
if(initialCapacity > MAXITMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if(loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor:" +loadFactor);
int capacity = 1;
while(capacity < initialCapacity)//initialCapacity:默认16
capacity <<= 1;
this.loadFactor = loadFactor;//loadFactor:加载因子,0.75
threshold = (int)Math.min(capacity * loadFactor,MAXIMUM_CAPACITY +1);// threshold:临界值,12
table = new Entry[capacity];
useAltHashing = sun.misc.VM.isBooted() && (capacity >= Holder.ALTEBNATIVE_HASHING_THRESHOLD);
init();
}
//HashMap的增put源码分析
public V put (K key ,V value){
if(key == null)
return putForNullKey(value);
int hash = hsah(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;
}
//put 中 int hash = hash(key);中hash分析 和 indexFor分析,addEntry分析
final int hash(Object k){
int h = 0;
if(useAltHashing){
if(k instanceof String){
return sun.misc.Hashing.stringHash32((String));
}
h = hashSeed;
}
h ^ = k.hashCode();
h ^=(h >>> 20) ^ (h>>>12);
return h ^ (h >>> 7)^(h >>> 4);
}
static int indexFor(int h,int length){
return h & (length - 1);
}
void addEntry(int hash, K key,V value,int bucketIndex){// bucketIndex要存放的位置
if((size >= threshold) && (null != table[bucketIndex])){
resize(newCapacity:2 * table.length);
hash = (null != key) ? hash(key) :0;
bucketIndex = indexFor(hash,table.length);
}
createEntry(hash, key,value,bucketIndex);
}
//createEntry(hash, key,value,bucketIndex)createEntry代码分析
void createEntry(int hash,K key,V value, int bucketInde){//bucketIndex:元素存放的位置
Entry<K , V> e = table[bucketIndex];//把原有位置上的元素取出来
table[bucketIndex] = new Entry<>(hash ,key, value, e);
size++;
}
// new Entry<K,V> 分析:
Entry (int h,K k,V v,Entry<K,V> n){
value = v;
nect = n;
key = k;
hash = h;
}
  • HashMap在JDK8中源码分析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public HashMap(){
this.loadFactor = DEFAULT_LOAD_FACTOR;//new HashMap并没有创建长度为16的数组
}
//put方法
public V put(K key, V value){
return putVal(hash(key),key, value,false,true);
}
//return putVal(hash(key),key, value,false,true)中hash用法
static final int hash(Object key){
int h;
return (key == null) ?0:(h = key.hashCode()) ^ (h >>> 16);
}
//return putVal(hash(key),key, value,false,true)中putVal用法
final V putVal(int hash,K key, V value,boolean onlyIfAbsent,boolean evict){
Node<K,V>][] tab; Node<K,V>p; int n, i;
if((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//扩容
if((p = tab[i = (n-1) & hash]) == null);//tab底层数组
tab[i] = newNode(hash,key,value,null);//添加成功,情况1
else{//不是null的情况
Node<K,V> e;K k;
if(p.hash == hash &&((k = p.key) == key || (key !=null && key.equals(k))))
e = p;//当前的p放e
else if(p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this,tab,hash,key,)
else{//哈希值不相等
for(int binCount = 0; ; ++binCount){
if((e = p.next) == null){//p.next表示p的下一个元素
p.next = newNode(hash,key,value,null);//七上八下原则
if(binCount >= TREEIFY_THRESHOLD -1)//TREEIFY_THRESHOLD:8
treeifyBin(tab,hash);
break;
}
if(e.hash == hash && ((K = e.key) == key || (key != nullb&&key.equals(k))))//比较的是p.next
break;
p=e;
}
}
//替换
if(e !=null){
V oldValue = e.value;
if(!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//putVal方法中的  n = (tab = resize()).length中的resize()用法
final Node<K,V>[] resize(){//创建数组
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null)? 0 :oldTab.length;// oldCap:0
int oldThr = threshold;//threshold临界值为0
int newCap,newThr = 0;
if(oldCap >0){
if(oldCap >= MAXIMUM_CAPACITY){
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr <<1 ;
}
else if(oldThr >0)
newCap = oldThr;
else {
newCap = DEFAULT_INITIAL_CAPACITY;//16出现
newThr = (int)(DEFAULT_LOAD_FACTOR*DEFAULT_INITIAL_CAPACITY);//12出现
}
if(newThr == 0){
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY)
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//数组造好
table = newTab;
if(oldTab != null){
for(int j = 0;j<oldCap;++j){
Node<K,V> e;
.....
}
}
return newTab;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//putVal方法中的  treeifyBin(tab,hash)的分析,红黑树
final void treeifyBin(Node<K,V>[] tab,int hash){
int n,index;Node<K,V> e;
if(tab == null || (n = tab.lengt<MIN_TREEIFY_CAPACITY)//MIN_TREEIFY_CAPACITY:64
resize();
else if((e = tab[index = (n -1)& hash]) != null){
TreeNode<K,V> hd = null,t1 = null;
do{
TreeNode<K,V> p = replacementTreeNode(e,null);
if (t1 == null)
hd = p
else{
p.prev = t1;
t1.next = p;
}
t1 = p;
}
}
}
  • DEFAULT_INITIAL_CAPACITY: HashMap的默认容量 ,16

  • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子: 0.75

  • threshold:扩容的临界值,=容量*填充因子:16 * 0.75 => 12

  • TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8

  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

  • LinkedHashMap的底层实现原理

1
2
3
4
5
6
7
8
9
10
11
12
//与HashMap区别,重写了putVal()中的newNode
Node<K,V> newNode(int hash,K key,V value,Node<K,V> e){
LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash,key,value,e)
linkedNodeLast(p);
return p;
}
static class Entry<K,V> extends HashMap.Node<K,V>{
Entry<K,V> before,after;//能够记录元素添加的顺序
Entry(int hash,K key,V value,Node<K,V> next){
super(hash,key,value,next);
}
}
  • Map中常用的方法:

添加,删除,修改操作:

1
2
3
4
Object put (Object key,Object value)://将指定key-value添加到(或修改)当前map对象中
void putAll(Map m)://将m中的所有key-value对存放到当前map中
Object remove(Object key)://移除指定key的key-value对,并返回value
void clear()//清空当前map中的所有数据

元素查询的操作:

1
2
3
4
5
6
Object get(Object key)//获取指定key对应的value
boolean containsKey(Object key)//是否包含指定的key
boolean containsValue(Object value)//是否包含指定的value
int size()//返回map中key-value对的个数
boolean isEmpty()//判断当前map是否为空
boolean equals(Object obj)//判断当前map和参数对象obj是否相等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//Set keySet():返回所有key构成的set
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的value集合values()
Collection values = map.values();
for(Object obj : values){
System.out.println(obj);
}
//遍历所有的key - value:entrySet()
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
Map.Entry entry = (Map.Entry) obj;//entrySet集合中的元素都是entry,强转
System.out.println(entry.getKey()+"---->"+entry.getValue());
}
//方法二:遍历所有的key - value
Set keySet = map.keySet();
Iterator iterator = keySet.iterator();
while(iterator.hasNext()){
Object key = iterator.next();
Object value = map.get(key);
}
System.out.println(key +"---->"+value);
  • TreeMap

1.向TreeMap中添加key - value,要求key必须是由同一个类创建的对象

2.因为要按照key进行排序:自然排序 , 定制排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//自然排序
public int compareTo(Object o){
if(o instanceof User){
User user = (User)o;
int compare = -this.name.compareTo(user.name);
if(compare != 0){
return compare;
}else{
return Integer.compare(this.age,user.age);
}
}else{
throw new RuntimeException("输入的类型不匹配");
}
}
//定制排序
TreeMap map = new TreeMap(new ComparaTor(){
public int compare(Object o1,Object o2){
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("输入的类型不匹配");
}
})
  • Properties:处理配置文件。key和value都是String类型
1
2
3
4
5
6
7
8
public static void main (String[] args) throws Exception{
Properties pros = new Properties();
FileInputStream fis = new FileInputStream(name:"");
pros.load(fis);//加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name="+name+",password="+password);
}
  • Collections工具类

Collections:操作Collection,Map的工具类

1
2
3
4
5
6
7
8
//reverse(List):反转list
//sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
//sort(list):排序(对指定的list集合元素按升序排序)
//swap(List,int,int):将指定list 集合中的i处元素和j处元素进行交换
//int frequency(Collection,Object):返回指定集合中指定元素出现的次数
//void copy(List dest,List src):将src中的内容复制到dest中
List dest = Arrays.asList(new Object[list.size()])
//List list1 = Collections.synchronizedList(list):返回的list1即为线程安全的List

例题:姓氏统计:每行隔一个名字,姓与名以空格分隔:现在想统计所有姓氏在文件中出现的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
HashMap<String ,Integer> map = new HashMap<>();
BufferedReader br = null;
try{
br = new BufferedReader(new FileReader(new File("e:/name.txt")));
String value = null;//临时接收文件中的字符串变量
StringBuffer buffer = new StringBuffer();
flag:while((value=br.readLine())!=null){
//开始读取文件中的字符
char[] c =value.toCharArray();
for(int i=0,i<c.length;i++){
if(c[i]!=''){
buffer.append(String.valueOf(c[i]));
}else{
if(map.containsKey(buffer.toString())){
int count = map.get(buffer.toString());
map.put(buffer.toString(),count +1);
}else{
map.put(buffer.toString(),1);
}
buffer.delete(0,buffer.length());
continue flag;
}
}
}
}