//迭代器遍历
        Iterator iterator = hashSet.iterator();//获取迭代器对象 有序:有下标
        while (iterator.hasNext()){//判断是否指定能够移动
            Object next = iterator.next();//指定移动并获取当前的元素
            System.out.println(next);
        }

1.1 目录

1. 为什么使用集合?
2. 集合架构有哪些?
3. List集合
4. ArrayList集合
5. LinkedList集合。
6. Set集合
7. HashSet集合
8. TreeSet集合。
9. Map
10.HashMap集合。
11.TreeMap集合。

1.2 为什么使用集合?

1. 我们原来学习过数组.

思考: 数组有缺陷?--定容【一定数组定义好,他们得长度就无法改变.】如果需要改变数组得长度,变得很复杂。

2.我们是否可以定义一个长度改变的容器。---当然可以。

3.手撕可变长度的容器。

public class MyArray {

     private Object [] arr; //声明一个Object类型的数组
     private int size;//表示数组的下标 因为他们是类成员变量 在创建对象时都有默认值。

     public MyArray(){ //无参构造函数
            this(3);  //本类中其他的构造函数 this本类的对象。 如果在构造方法中this()表示调用本类的其他构造函数
     }

     public MyArray(int initSize){ //有参构造函数--表示数组的长度
          if(initSize<0){ //长度不合法
              throw new RuntimeException("sorry 数组的长度有误。");
          }
          arr=new Object[initSize];
     }

     //把元素o放入数组arr
     public void addData(Object o){
           //判断你的数组是否已满
           if(size>=arr.length){
                //扩容--(1)容器的长度变长 (2)把原来容器中的元素复制到新的容器中
               Object[] newArr = Arrays.copyOf(arr, size * 2);
               arr=newArr;
           }
           arr[size]=o;
           size++;
     }

     //根据下标获取数组中的元素。
     public Object getData(int index){
            if(index>=size){
                 throw new ArrayIndexOutOfBoundsException("下标越界");
            }
           Object o = arr[index];
            return o;
     }

}

基于这种可以手撕框架的思路,

java官网 基于数组 根据不同的数据结构 创建了多个类 而这些类统称 为集合框架。

以后 我们在说集合框架时 就表示多个类。

1.3 集合的架构

1.4 List集合-ArrayList

1.4.0 创建集合对象

List list = new ArrayList(); //创建一个集合对象 如果没有指定集合容器的长度默认为10

   List list1 = new ArrayList(15); //创建一个集合对象,指定集合容器长度是15
  //添加 (1)可以添加任意类型
        list.add("java01");//添加字符串
        list.add("java02");
        list.add(15.5);//添加小数
        list.add(18);//添加整数
        list.add(true);//添加布尔值
        list.add(new Date());//添加日期
        System.out.println(list);//显示list中所有的对象
        list.add(2,"hello"); //下标为2的位置添加元素 并把后面的元素进行位移
        System.out.println(list); //打印一个对象时默认调用的为toString()

        List list2=new ArrayList();
        list2.add("a");
        list2.add("b");

        list.addAll(list2);//添加多个元素 把list2中的每个元素一一添加到list中
        System.out.println(list);

1.4.2 删除的操作

 //删除操作
        list.remove(2);//移除下标为2的元素
        System.out.println(list);//显示集合中的元素
        list.clear();//清空集合中的元素.
        System.out.println(list);//显示集合中的元素

1.4.3 修改的操作

  //修改操作
        list.set(1,"刘德华");//将下标为1的元素修改为“刘德华”字符串
        System.out.println(list);

1.4.4 查询操作

       List list = new ArrayList();
        list.add("java01");
        list.add("java02");
        list.add("java03");
        list.add("java02");

        //查询的方法
        Object o = list.get(1);//根据下标获取元素
        System.out.println(o);

        int size = list.size();//获取集合中元素的个数。
        System.out.println(size);

        boolean f = list.contains("java05");//判断元素是否在集合中
        System.out.println(f);

        int index = list.indexOf("java05");//查询元素在集合中第一次出现的位置
        System.out.println(index);

        //遍历集合中的元素 for循环
        for(int i=0;i<list.size();i++){
            Object o1 = list.get(i);
            System.out.println(o1);
        }

1.4.5 ArrayList底层源码

从构造方法来入手。new ArrayList(22) 底层声明了一个Object类型的数组 名字elementData
  Object[] elementData

  public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { //大于0
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { //等于初始化为一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else { //抛出一个异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

==========add("java01")======E理解为Object类型================  
   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 扩容 
        elementData[size++] = e;  //把元素赋值给数组的相应位置
        return true;
    }
==========indexOf("java02") 判断元素在集合中第一次的位置=============
     public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i])) //和数组中的每个元素内容进行比对
                    return i;  //返回元素在集合中位置
        }
        return -1;
    }   

===========size() 请求数组的长度======================
 public int size() {
        return size;
    }   

============contain("java05")判断元素是否在集合中==============
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
===============get(1) 获取指定位置的元素========
   public E get(int index) {
        rangeCheck(index); //判断指定的位置是否合法 

        return elementData(index);
    }  

    E elementData(int index) {
        return (E) elementData[index];
    } 

============toString() 为什么不打印对象的引用地址 
    [java01, java02, java03, java02]因为重写了Object里面的toString方法。
    
 public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }   

通过对ArrayList方法的底层代码分析:底层就是对数组的操作。
    ArrayList的底层就是基于数组实现的。

1.5 LinkedList

它是一个链表结构。

1.5.1 删除操作

 //删除操作
        linkedList.removeFirst();//移除头部元素
        System.out.println(linkedList);

        linkedList.remove(2);//移除指定位置的元素
        System.out.println(linkedList);

        linkedList.removeLast();//移除尾部的元素
        System.out.println(linkedList);

1.5.2 添加操作//添加
      

//添加
        linkedList.add("java01"); //追加尾部
        linkedList.addFirst("java02"); //添加到头部
        linkedList.addLast("java03");//追加到尾部
        linkedList.addFirst("java04"); //追加到头部
        linkedList.addLast("java05");//追加到尾部
        System.out.println(linkedList);

1.5.3 修改操作

 //修改操作
        linkedList.set(1,"java11");
        System.out.println(linkedList);

1.5.4 查询操作

 //查询操作
        int size = linkedList.size();//求长度
        boolean empty = linkedList.isEmpty();//是否为空

        boolean b = linkedList.contains("java01");//判断元素是否在集合中

        Object o = linkedList.get(1);//根据下标获取指定位置的元素

        Object first = linkedList.getFirst();//获取第一个元素
        System.out.println(first);

        Object last = linkedList.getLast();
        System.out.println(last);

2.0LinkedList的底层源码

1、==================== get(1)-----获取元素========================
   public E get(int index) {
        checkElementIndex(index); //检查index下标是否正确。
        return node(index).item;  //李四Node对象
    }
 ========================node(index)=============================
 Node<E> node(int index) {
        //>> 位运算二进制运算 ----- size >> 1 一半的意思size/2
        if (index < (size >> 1)) { //前半部分
            Node<E> x = first; 
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {  //后半部分
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

分析: LinkedList查询效率低。因为它要一个节点一个节点的往后找。

3. Set集合

 

3.1 HashSet集合

3.1.1 创建HashSet对象。

public class Test02 {
    public static void main(String[] args) {
        HashSet  hashSet= new HashSet();

        HashSet  hashSet1 = new HashSet(16);//初始容器的大小

        //loadFactor:--->0.7f 表示负载因子 当空间使用70%时 要求扩容
        HashSet hashSet2 = new HashSet(16,0.7f);
    }
}

3.1.2 添加元素

 //添加操作
        hashSet.add("java01");
        hashSet.add("java02");
        hashSet.add("java04");
        hashSet.add("java03");
        hashSet.add("java02");

        HashSet set2=new HashSet();
        set2.add("刘德华");
        set2.add("张学友");
        set2.add("黎明");
        
        hashSet.addAll(set2); //把set2中的每个元素添加到hashset中
        System.out.println(hashSet); //元素不能重复 而且无序

3.1.3 删除

 //删除
        hashSet.remove("黎明");//因为是无序的,不能通过下标来删除
        hashSet.clear();//清空容器集合
        System.out.println(hashSet);

3.1.4 修改

  //修改操作
        boolean empty = hashSet.isEmpty(); //判断是否为空
        System.out.println(empty);

        boolean b = hashSet.contains("刘德华");//判断元素是否在容器中
        System.out.println(b);

3.1.5 hashSet的遍历

(1)通过迭代器遍历(2)通过for each来遍历

 //迭代器遍历
        Iterator iterator = hashSet.iterator();//获取迭代器对象 有序:有下标
        while (iterator.hasNext()){//判断是否指定能够移动
            Object next = iterator.next();//指定移动并获取当前的元素
            System.out.println(next);
        }
 //遍历--- foreach
        for(Object o: hashSet){
            System.out.println(o);
        }

3.1.6 hashSet的源码

从构造函数说起:
    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
 在创建一个HashSet的对象时,底层创建的是HashMap。

3.2 TreeSet集合。

TreeSet中的方法和HashSet中的方法一模一样 只是他们的实现不一样。
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。

例子: 存储String类型。

TreeSet treeSet=new TreeSet();
        treeSet.add("java05");
        treeSet.add("java03");
        treeSet.add("java04");
        treeSet.add("java01");
        treeSet.add("java02");
        treeSet.add("java04");

        System.out.println(treeSet);

存储一个对象类型:  

public class Test04 {
    public static void main(String[] args) {
        TreeSet treeSet=new TreeSet();
        treeSet.add(new Student("王俊凯",17));
        treeSet.add(new Student("赵晓普",16));
        treeSet.add(new Student("赵俊涛",16));
        treeSet.add(new Student("闫克起",15));

        System.out.println(treeSet);
    }
}

发现: TreeSet中的元素必须实现Comparable接口 方可放入TreeSet

解决办法有两个:

第一个: 让你的类实现Comparable接口

public class Test04 {
    public static void main(String[] args) {
        TreeSet treeSet=new TreeSet(); //TreeSet不允许重复元素
        treeSet.add(new Student("王俊凯",17));
        treeSet.add(new Student("赵晓普",16));
        treeSet.add(new Student("赵俊涛",16));
        treeSet.add(new Student("闫克起",15));

        System.out.println(treeSet);
    }
}
class Student implements Comparable{
     private String name;
     private Integer age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
   //排序:---返回如果大于0 表示当前元素比o大  如果返回-1 当前添加的元素比o小  返回0表示相同元素。
    @Override
    public int compareTo(Object o) {
        Student student= (Student) o;
        System.out.println(this+"===================>"+o);

        if(this.age>student.age){
            return 1;
        }
        if(this.age<student.age){
            return -1;
        }

        return 0;
    }
}

第二种: 在创建TreeSet时指定排序的对象。

1.自定义MyComparator类

package com.qy151.Test2;

import java.util.Comparator;

public class MyComparator5 implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Student5 s1 = (Student5)o1;
        Student5 s2 = (Student5)o2;
        if(s1.getAge()>s2.getAge()){
            return 1;
        }else if(s1.getAge()<s2.getAge()){
            return -1;
        }else{
            return 0;
        }
    }
}

2.添加学生类

package com.qy151.Test2;

public class Student5 {
    private String name;
    private int age;

    public Student5(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student5() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

3.把自定义的MyComparator放在TreeSet容器里

package com.qy151.Test2;

import java.util.TreeSet;

public class TreeSet5 {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new MyComparator5());
        ts.add(new Student5("串串",16));
        ts.add(new Student5("麻辣烫",17));
        ts.add(new Student5("凉皮",18));
        ts.add(new Student5("热干面",16));
        System.out.println(ts);
    }
}

4. Map 属于键值对模式

map中得每个元素属于键值对模式。 如果往map中添加元素时 需要添加key 和 value. 它也属于一个接口,该接口常见得实现类有: HashMap.

4.1 如何创建Map对象

//默认初始化大小为16 负载因子为0.75
         Map map=new HashMap();
         //初始化大小
         Map map2=new HashMap(16);
         //初始化大小  负载因子
         Map map3=new HashMap(16,0.78f);

4.2 添加和删除操作

 HashMap hm = new HashMap();
        hm.put("name1","张三");//单个添加 key value;
        hm.put("name2","李四");//key不能有重复的,如果重复,后面会把前面的覆盖掉
        hm.put("name3","王五");//value可以有重复的
        System.out.println(hm);
        HashMap hm1 = new HashMap();
        hm1.put("k1","v1");
        hm1.put("k2","v2");
        System.out.println(hm1);
        hm.putAll(hm1);//批量添加
        System.out.println(hm);
        hm.remove("name1");//根据key来进行删除
        System.out.println(hm);
        System.out.println(hm.putIfAbsent("name3","唐伯虎"));//通过key值来判断,如果有相同的key值,则此项不添加进去,如果没有这个key值,则添加进去。
        System.out.println(hm.putIfAbsent("k3","v4"));

4.3 修改操作

 //修改操作
        map.replace("name","刘德华");//替换元素
        System.out.println(map);//这个功能和添加功能类似

4.4 查询

        System.out.println(hm.size()); //查看长度
        System.out.println(hm.isEmpty());//查看是否为空
        System.out.println(hm.containsValue("花甲粉丝"));//查看是否有value为“花甲粉丝”的
        System.out.println(hm.containsKey("k1"));//查看是否有key为“k1”的
        System.out.println(hm.get("k2"));//通过key来查找value
        System.out.println(hm.keySet());//查找出所有的key
        //遍历 循环
        //keySet()为所有的key值
        for(Object i : hm.keySet()){
            Object value =  hm.get(i);
            System.out.println(i+"   "+value);
        }

4.5 HashMap得底层原理

小结:

JDK1.8 HashMap原理
Hashmap的原理,存储元素使用的put(key,value),根据key的hash计算出相应哈希值,根据相应算法求出该元素在数组中的位置, 如果求出得哈希值相同,则称为哈希冲突,会根据equals来判断元素是否一致,如果equals不同,则存入单向链表上, 如果哈希碰撞得个数超过8个,则把链表转换为红黑二叉树

JDK1.7和JDK1.8 HashMap得区别。(七上八下)
   JDK1.7使用得数据结构: 数组+链表  而且链表插入模式为头部插入(造成死循环)。
   jdk1.8使用得数据结构: 数组+链表+红黑树 而且链表得插入模式为尾部插入。

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[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                 //如果key得hash值相同,判断key得equals是否相同,替换原来得元素
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 判断链表得长度是否超过8个
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 把链表转换为红黑树结构
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }