Java之Queue与Map集合

一、Queue

1.特点

和数据结构中的队列类似,只允许在队头删除元素,队尾插入元素。队列中允许有重复的元素。

2.Queue中的方法

2.1 插入元素

 boolean add(E element)
 boolean offer(E element)

说明:二者都是向队列中添加元素,如果成功,返回true。区别在于当队列已满时,add()抛出IllegalStateException,而返回false。

2.2 删除元素

 E remove()
 E poll()

说明:二者都是删除队列中的元素,删除成功,返回删除的元素。区别在于当队列为空时,remove()抛出NoSuchElementException异常,而offer()返回false。

2.3 获取元素

E element()
E peek()

说明:二者都会返回队头元素,但并不删除它。区别在于如果队头为空,element()会抛出NoSuchElementException异常,而peek()会返回null。

3.Queue的子类

3.1 Deque接口(双向队列)
 3.1.1 特点:可以在队头和队尾插入或删除元素。
 3.1.2 Deque中的方法(抽象,需要被覆写):
 (1)插入元素

        void addFirst(E element);
	void addLast(E element);
	boolean offerFirst(E element);
	boolean offerLast(E element);

说明:这四个方法都会在队头或队尾插入元素。区别是如果队列已满,前两个方法会抛出IllegalStateException异常;而后两个方法会返回false。
  (2)删除元素

        E removeFirst();
	E removeLast();
	E pollFirst();
	E pollLast();

区别:如果队列为空,前两个方法会抛出NoSuchElementException异常,而后两个方法返回null。

(3)获取队头或队尾元素(不会删除)

        E getFirst();
	E getLast();
	E peekFirst();
	E peekLast();

区别:如果队列为空,前两个方法会抛出NoSuchElementException异常,而后两个方法返回null。

3.1.3 Deque的实现子类

(1)LinkedList和ArrayDeque都实现了Deque接口。LinkedList之前已经介绍过,这里不再做介绍。下面是ArrayDeque的用法

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

public class TestCollection {

    public static void main(String[] args) {
        Queue<Integer> queue=new ArrayDeque<Integer>();
        queue.add(1);
        queue.offer(2);
        queue.add(3);
       queue.add(4);//1->2->3->4
        //打印队列中的元素
       for(Integer i:queue){
           System.out.print(i+" ");
       }//1 2 3 4
        System.out.println();
       ((ArrayDeque<Integer>) queue).removeLast();//删除最后一个元素,队列变为:1->2->3
       System.out.println(((ArrayDeque<Integer>) queue).getLast());//3
        ((ArrayDeque<Integer>) queue).pollLast();//1->2
        System.out.println(((ArrayDeque<Integer>) queue).peekLast());//2
  }
}

(2)PriorityQueue(优先级队列)
  PriorityQueue(优先级队列)会按照排序的方式对队列中的元素进行排序和检索,因此PriorityQueue的对象必须实现Comparable接口,提供对元素排序时两个元素之间的比较规则。
但要注意:当通过foreach语句遍历优先队列时,获得的元素并没有进行排序,而在通过remove()方法删除元素时,该方法总是会删除当前队列中的最小元素。

import java.util.PriorityQueue;
import java.util.Queue;

public class TestCollection {

    public static void main(String[] args) {
        Queue<Integer> queue=new PriorityQueue<Integer>();
        queue.add(13);
        queue.add(56);
        queue.add(26);
        System.out.println("队列中的元素依次为:");
        for(Integer i:queue){
            System.out.print(i+" ");
        }
        System.out.println();
        System.out.println("依次删除优先级队列中的元素:");
        while(!queue.isEmpty()){
            int c=queue.remove();
            System.out.print(c+" ");
        }
  }
}

结果为:

队列中的元素依次为:
13 56 26 
依次删除优先级队列中的元素:
13 26 56 

二、Map

1.Collection集合的特点是每次进行单个对象的保存,而Map可以进行一对对象的保存,Map集合一次性会保存两个对象,且这两个对象的关系:key=value结构。这种结构最大的特点是可以通过key找到对应的value内容
2.特点:集合中的key不能重复,而value可以重复。
3.Map接口中的方法:

	public V put(K key,V value);  向Map中追加数据。
	public V get(Object key);    通过key找到对应的value信息。
	public Set<K> keySet();     取得所有key信息,key不能重复。
	public Collection<V> values();    取得所有value信息,可以重复。
	public Set<Map.Entry<K,V>> entrySet();  将Map集合变为Set集合。 

4.子类
4.1 HashMap--是使用Map集合中最常用的子类。
    4.1.1 特点:线程不安全。不能在循环遍历Map的时候修改值,否则会产生ConcurrentModificationsException并发修改异常。
    4.1.2 HashMap的实现原理:在数据量小(JDK8设置阈值为8)的时候,HashMap按照链表形式存储;当数据量变大之后,为了进行快速查找,会将链表变为红黑树(均衡二叉树)来进行数据保存,用hash码作为数据定位。
   4.1.3 HashMap的存储结构:在JDK1.8之前,以链表形式存储。在JDK1.8之后,可以看作是桶数组和链表结合组成的复合结构。数组被分为一个个桶,通过哈希值决定了键值对在这个数组上的寻址。哈希值相同的键值对,会以链表形式存储。当链表的长度超过8时,则链表自动转换为红黑树。
   4.1.4 HashMap的一些属性

public class HashMap<k,v> extends AbstractMap<k,v> implements Map<k,v>, Cloneable, Serializable {
private static final long serialVersionUID = 362498820763181265L;
// 默认的初始容量是16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
// 最大容量:2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认的填充因子(加载因子):0.75f
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// 阈值1:8。含义:当桶(bucket)上的链表数大于这个值时会转成红黑树,put方法的代码里有用到。
static final int TREEIFY_THRESHOLD = 8;
// 阈值2:6。含义:同上一个相反,当桶(bucket)上的结点数小于这个值时树转链表
static final int UNTREEIFY_THRESHOLD = 6;
// 源码注释里说是:树的最小的容量,至少是 4 x TREEIFY_THRESHOLD = 32 然后为了避免(resizing 和 treeification thresholds) 设置成64
static final int MIN_TREEIFY_CAPACITY = 64;
// 存储元素的数组,总是2的倍数
transient Node<k,v>[] table;
transient Set<map.entry<k,v>> entrySet;
// 存放元素的个数,注意这个不等于数组的长度。
transient int size;
// 每次扩容和更改map结构的计数器
transient int modCount;
// 临界值 当实际大小(容量*填充因子)超过临界值时,会进行扩容
int threshold;
// 填充因子
final float loadFactor;

说明:

1.容量:哈希表中桶的数量
2.初始容量:创建对象时桶的数量。
3.大小:元素的数量
4.负载因子:等于大小/容量,即元素的数量/桶的数量。HashMap中默认是0.75.
由此可见容量和负载系数决定了可用的桶的数量。
对于负载因子,建议:如果没有特别需求,不要轻易修改。如果确实需要修改,建议不要设置超过0.75的数值,因为这样会显著增加冲突,降低HashMap的性能。

5.树化
下面是精简过的treeifyBin方法

final void treeifyBin(Node<K,V>[] tab,int hash){
	int n,index;
	Node<K,V> e;
	if(tab==null || (n=tab.length)<MIN_TREEIFY_CAPACITY)
	resize();
	else if((e=tab[index=(n-1)&hash])!=null){
	//树化改造逻辑
}

因此树化改造的逻辑可以理解为,当bin的数量大于TREEIFY_THRESHOLD时:
    如果容量(哈系桶的数量)<MIN_TREEIFY_CAPACITY,只会进行简单的扩容;
    如果容量(哈系桶的数量)>MIN_TREEIFY_CAPACITY,则会进行树化改造。

树化的目的:确保安全。因为在元素放置的过程中,如果元素发生哈希碰撞,就会放到同一个哈希桶里,形成一个链表。而链表是线性的,会严重影响存取的性能。而在现实世界中,构造哈希冲突的数据非常容易,恶意代码就可以利用这些数据大量与服务器端交互,导致服务器端CPU被大量占用,这就构成了哈希碰撞拒绝服务攻击。

4.1.5 测试HashMap

import java.util.*;

public class TestMap{

    public static void main(String[] args) {
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"Hello");
        map.put(3,"Java");
        map.put(2,"Bit");

        map.put(null,"null");//可以设置k,v为null.

        //获取所有的key信息
        Set<Integer> set=map.keySet();
        Iterator<Integer> iterator=set.iterator();
        while(iterator.hasNext()){
            System.out.print(iterator.next()+" ");
        }
        //获取所有的value
        Collection<String> values=map.values();
        for(String value:values){
            System.out.print(value+",");
        }
        System.out.println();
        //打印map
        //方式一
        System.out.println(map);
        //通过key遍历value
        //方式二
        System.out.println("通过key遍历value") ;
        for(Integer key:map.keySet()){
            System.out.print(key+"="+map.get(key)+" ");
        }
        System.out.println();
        //方式三
        //将Map集合变为Set集合
        Set<Map.Entry<Integer,String>> entries=map.entrySet();
        for(Map.Entry<Integer,String> entry:entries){
            System.out.print(entry.getKey()+"="+entry.getValue()+" ");
        }
}
}

结果:

null 1 2 3 null,Hello,Bit,Java,
{null=null, 1=Hello, 2=Bit, 3=Java}
通过key遍历value
null=null 1=Hello 2=Bit 3=Java 
null=null 1=Hello 2=Bit 3=Java 

4.2 Hashtable(注意t小写)

4.2.1 特点:线程安全。k和v不能设置为null。
4.2.2 Hashtable与HashMap的区别:
(1)出现版本不同:Hashtable在JDK1.0出现,HashMap在JDK1.2出现。
(2)性能:Hashtable同步处理,性能低;HashMap异步处理,性能高。
(3)安全性:Hashtable线程安全;而HashMap线程不安全。
(4)null的操作:Hashtable中的key和value都不能为null,而HashMap中都可以为null。

因此,在单线程的时候,考虑使用Hashtable。

4.2.3 Hashtable的操作:

public class TestCollection {

    public static void main(String[] args) {
        Map<String,String> map=new Hashtable<>();
        map.put("First","Monday");
        map.put("Second","Tuesday");
        map.put("Third","Wednesday");
       // map.put("A",null);//抛出java.lang.NullPointerException异常
        System.out.println(map);
}
}

结果:

{Second=Tuesday, First=Monday, Third=Wednesday}

4.3ConcurrentHashMap
4.3.1 特点:线程安全,性能高。ConcurrentHashMap的特点=HashMap的性能+Hashtable的线程安全。因此,在多线程需要进行异步处理时,考虑使用ConcurrentHashMap。

4.4TreeMap
4.4.1 特点:可排序的Map子类,按照key的内容进行排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值