Java集合总结

集合按照其储存结构可分为两大类:即单列集合Collection和双列集合Map.

Collection:单列集合类的根接口,用于储存一系列复合某种规则的元素,它有两个重要的子接口,分别为List和Set。其中List的特点是元素有序,元素可重复。Set的特点是元素无序并且不可重复。List接口的主要实现类有ArrayList和LinkedList,Set接口的主要实现类有HashSet和TreeSet。

Map:双列集合类的根接口,用于储存具有键(Key),值(Value)映射关系的元素,每个元素都包含一对键值,在使用Map集合时可以通过指定的Key找到对应的Value。Map接口的主要实现类有HashMap和TreeMap。
这里写图片描述

一、Collection接口:
List接口继承了Collection接口,包含Collection接口的所有方法,List是列表类型,会提供一些适合于自己的方法。

1、List接口:
List接口中适合于自身的方法都与索引有关。由于List集合以线性方式储存对象,因此可以通过对象的索引来操作对象。
这里写图片描述

(1)List接口下实现ArrayList类
底层数据结构: 数组
初始化大小为 10
增长大小为当前大小的1.5倍(创建的 数组的长度的1.5倍)

该类实现了可变的数组,允许所有元素,包括null。可以根据索引位置对集合进行快速随机访问。缺点是向指定的索引位置插入对象或删除对象的速度较慢。

/**
 * ArrayList类的交集,并集,去重代码实现
 */
public class ArrayListDemo {
public static void main(String[] args) {
        /**
         * ArrayList
         */
        ArrayList<Integer> arraylist = new ArrayList<Integer>();
        arraylist.add(13);
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list2.add(1);
        list2.add(2);
        arraylist.addAll(list2);
        for(int i : arraylist){
            System.out.println(i);
        }
        //查询这个数据是否存在在集合中
        System.out.println(arraylist.contains(3));
        //得到指定位置元素
        Integer integer = arraylist.get(2);
        System.out.println(integer);
        //清除集合内元素
        arraylist.clear();
        for(int i : arraylist){
            System.out.println(i);
        }
        ArrayList<Integer> list1 = new ArrayList<Integer>();
        list1.add(1);
        list1.add(2);
        ArrayList<Integer> list2 = new ArrayList<Integer>();
        list2.add(2);
        list2.add(3);
        //求集合并集(去重和不去重)
        ArrayList<Integer> list3 = new ArrayList<Integer>();
        ArrayList<Integer> list4 = new ArrayList<Integer>();
        //不去重
        for(int i = 0;i < list1.size();i++){
            list3.add(list1.get(i));
        }
        for(int i = 0;i < list2.size();i++){
            list3.add(list2.get(i));
        }
        for(int i : list3){
            System.out.printf(i+" ");
        }
        System.out.println("------------");
        //去重

        for(int i = 0;i < list1.size();i++){
            list4.add(list1.get(i));
        }
        for(int i = 0;i < list2.size();i++){
            if(!list4.contains(list2.get(i)))
            {
                list4.add(list2.get(i));
            }
        }
        for(int i : list4){
            System.out.printf(i+" ");
        }
        System.out.println("------------");
        //求集合交集
        //list1.containAll(list2);
        for(int i = 0;i < list1.size();i++){
            if(list2.contains(list1.get(i)) ){
                System.out.printf(list1.get(i) + " ");
            }
        }
        System.out.println("------------");
        //求集合差集
        //list1.removeAll(list2);
        for(int i = 0;i < list1.size();i++){
            if(!(list2.contains(list1.get(i))) ){
                System.out.printf(list1.get(i) + " ");
            }
        }
    }

(2)List接口下实现LinkedList类
LinkedList类的底层数据结构为双向链表,这种结构的优点是便于向集合中插入和删除对象,经常需要向集合中插入,删除对象时使用LinkedList类实现的List集合的效率较好,但对于随机访问集合中的对象使用LinkedList类实现的List集合的效率较慢。

2、Set接口:
Set接口继承了Collection接口,也具有Collection接口的所有方法。
这里写图片描述

由于Set集合中的对象是无序的,遍历Set集合的结果与插入Set集合的顺序并不相同。

Set接口下实现类常用的有HashSe类t和TreeSet类。

(1)HashSet类
-底层:哈希表
-键值段变单值
-内部元素不能重复
HashSet底层实现源码:
这里写图片描述
HashSet实现数据查重(仅限第一个出现重复的数据)
代码如下:

public static char findFirstRepeat(String A, int n) {

        char[] a=A.toCharArray();
        HashSet hs=new HashSet<>();
        for(int i=0; i<n;i++) {
            if (!hs.add(a[i]))  {
                return a[i];
            }
        }
        return 0;
        }

        public static void main(String[] args) {
    System.out.println(findFirstRepeat("dytkrzcvnjuekcn",12));
        }

(2)TreeSet类

*TreeSet底层为键值段,继承于AbstractSet,并且实现了NavigableSet接口。

*TreeSet的本质是一个”有序的(自动排序),并且没有重复元素”的集合,它是通过TreeMap实现的

Map接口:

Map接口提供了将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
这里写图片描述
由于Map集合中的元素是通过key,value进行存储的,要获取指定的key值或value值,需要先通过相应的方法获取key集合或value集合,再遍历key集合或value集合获取指定值。

Map接口下实现类:

1、HashMap类

*HashMap类底层是一个hash表,即数组+链表。默认初始化大小是16(必须满足2的n次幂),默认加载因子是0.75f,最大容量是2的30次幂。在无参构造函数中使用的是默认大小和默认加载因子。

*特点:允许key为null。

*get方法:如果存在指定的键对象,则返回对象对应的值,否则返回null。

*HashMap通过哈希码对其内部的映射关系进行快速查找。不保证映射的顺序,尤其是不保证该顺序恒久不变。

*数组的扩容方式:2的n次幂。

*源码中的threshold:集合的阈值 容量乘以加载因子=阈值(16*0.75=12)

*HashMap扩容:原容量的2倍(当元素达到当前阈值时进行扩容)

*线程不安全导致并发问题:在扩容时

*Java7和Java8HashMap的区别:7中使用数组加链表,8中当数据大于8时会转换成红黑树。

class Entry{
    /**
     * 键
     */
    int key;
    /**
     * 值
     */
    String value;
    /**
     * 下一个节点
     */
    Entry next;
    /**
     * 构造函数
     * @param key 键
     * @param value 值
     */
    Entry(int key,String value){
        this.key = key;
        this.value = value;
        this.next = null;
    }
    /**
     * 构造函数
     */
    Entry(){
        this.key = 0;
        this.value = null;
        this.next = null;
    }
}
class Hash{
    /**
     * 哈希数组表
     */
    Entry[] table;
    /**
     * 构造函数
     * 
     */
    Hash(){
        /**
         * 初始化这个table表
         */
        table = new Entry[10];
        for(int i = 0;i < table.length;i++){
            table[i] = new Entry();
        }
    }
    /**
     * put方法
     * @param key 键
     * @param value 值
     */
    public void put(int key,String value){
        Entry entry = new Entry(key,value);
        //临时key 
        int keys = key%10;
        if(table[keys].next == null){
            table[keys].next = entry;
            entry.next = null;
        }else{
            /**
             * 哈希冲突-->用链地址解决
             */
            Entry tar = table[keys];
            while(tar.next != null){
                tar = tar.next;
            }
            tar.next = entry;
            entry.next = null;
        }
    }

    public String get(int key){
        int keys = key%10;
        Entry tar = table[keys];
        while(tar.key != key && tar != null){
            tar = tar.next;
        }
        if(tar == null){
            return null;
        }
        return tar.value;
    }
}

public class HashMap {
    /**
     * hashMap 
     *   put()
     *   get()
     * @param arg
     */
    public static void main(String[] args) {
        Hash h = new Hash();
        h.put(3, "wangjie");
        h.put(4, "haha");
        h.put(13,"xixi");
        System.out.println(h.get(13));
    }
}

2、TreeMap类

*该类实现了Map接口,还实现了java.util.SortedMap接口,因此集合中的映射关系具有一定的顺序。

*在添加,删除和定位映射关系上,TreeMap类比HashMap类的性能差一些。

*由于TreeMap类实现的Map集合中的映射关系是根据键对象按照一定的顺序排序的,因此不允许键对象是null。

*可以通过HashMap类创建Map集合,当需要顺序输出时,再创建一个完成相同映射关系的TreeMap类实例。

3、LinkedHashMap类
*LinedHashMap继承自HashMap,具有HashMap的属性和方法。它也有自己特有的属性(双向列表头结点和迭代顺序)。
更为准确地说,LinedHashMap是一个将所有Entry节点炼入一个双向链表的HashMap。
*HashMap是无序的,也就是说,迭代HashMap所得到的元素顺序并不是它们最初放置到HashMap的顺序。HashMap的这一缺点往往会造成诸多不便,因为在有些场景中,我们确需要用到一个可以保持插入顺序的Map,此时有序的LinedHashMap就会解决这一问题。
这里写图片描述
LinkedHashMap的快速存取

LinkedHashMap 的存储实现 : put(key, vlaue)

LinkedHashMap没有对 put(key,vlaue) 方法进行任何直接的修改,完全继承了HashMap的 put(Key,Value) 方法,源码如下:

public V put(K key, V value) { 
//当key为null时,调用putForNullKey方法,并将该键值对保存到table的第一个位置
if (key == null)
 return putForNullKey(value); 
//计算该键值对在数组中的存储位置(哪个桶) 
int i = indexFor(hash, table.length); 
//在table的第i个桶上进行迭代,寻找 key 保存的位置 
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))) {
   e.value = value;
  e.recordAccess(this); // LinkedHashMap重写了Entry中的recordAccess方法--- (1) 
} 
}
 modCount++; //修改次数增加1,快速失败机制
 addEntry(hash, key, value, i); // LinkedHashMap重写了HashMap中的createEntry方法 ---- (2) 
 return null;
  }

4、.LinkedHashSet(有序)

*LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同。

  • LinkedHashSet 的实现上通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可

    LinkedHashSet 的源代码如下:

public class LinkedHashSet extends HashSet implements Set, Cloneable, java.io.Serializable {
 private static final long serialVersionUID = -2851667679971038690L; 
 /** 
 * 构造一个带有指定初始容量和加载因子的新空链接哈希set。 
 * 底层会调用父类的构造方法,构造一个有指定初始容量和加载因子的LinkedHashMap实例。 
 * @param initialCapacity 初始容量。 
*/ 
public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } 
/** 
* 构造一个带指定初始容量和默认加载因子0.75的新空链接哈希set。 
* 底层会调用父类的构造方法,构造一个带指定初始容量和默认加载因子0.75的LinkedHashMap实例。 
* @param initialCapacity 初始容量。 
*/ 
public LinkedHashSet(int initialCapacity) { 
super(initialCapacity, .75f, true); 
} 
/** 
* 构造一个带默认初始容量16和加载因子0.75的新空链接哈希set。 
* 底层会调用父类的构造方法,构造一个带默认初始容量16和加载因子0.75的LinkedHashMap实例。 
*/ 
public LinkedHashSet() { 
super(16, .75f, true); 
} 
/** 
* 构造一个与指定collection中的元素相同的新链接哈希set。 
* 底层会调用父类的构造方法,构造一个足以包含指定collection中所有元素的初始容量和加载因子为0.75的LinkedHashMap实例。 
* @param c 其中的元素将存放在此set中的collection。
*/ 
public LinkedHashSet(Collection c) { 
super(Math.max(2*c.size(), 11), .75f, true); 
addAll(c); 
} 
}

三、哈希表
.哈希表:通过关键值找对应值的一个数据结构

常用哈希构造函数
|– 直接寻址法:f(key) = key
|– 除留余数法:f(p) = p % n,n < m(m是数组的大小)

解决哈希冲突的重要方法:

|– 链地址法:数组和单向链表的组合(尾插法)

|– 探测法:pos(n) = f(n) + p(n)

a:线性探测法:依次做加1探测,找到空位,即pos(n) = f(n) + 1,+2,+3……;

b:随机探测法:以随机数的方式探测空位,pos(n) = f(n) + random();(伪随机)

四、迭代器的创建和使用

利用Iterator接口创建迭代器,Iterator接口位于java.util包下。Iterator接口有三个方法:

*hashNext():如果仍有元素可以迭代,则返回true。

*next():返回迭代的下一个元素。

*remove():从迭代器指向的collection中移除迭代器返回的最后一个元素(可选操作)。

/**
 * Iterator遍历ArrayList代码实现
 */
 public class IteratorDemo {
 public static void main(String[] args) {
 List<Integer> list = new ArrayList<Integer>();
 for(int i = 0; i < 10; 
i++) {
  List.add(i);
}
System.out.println("列表中的元素:");
for(Iterator<Integer> it = list.iterator();it.hashNext();) {
System.out.print(it.next()+" ");
}
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值