Java的第十二篇文章——集合

news2024/11/17 7:31:35

目录

第十二章 集合

学习目标

1. 集合框架的由来

2. 集合框架的继承体系

3. Collection接口

3.1 Collection接口的常用方法

4. Iterator接口

4.1 Iterator接口的抽象方法

4.2 获取迭代器接口实现类

4.3 迭代器的实现原理

4.4 并发修改异常

4.5 集合存储自定义对象并迭代

5. List接口

5.1 List接口的特点

5.2 List接口自己的方法(带有索引)

5.3 List集合的特有迭代器 

5.4 List接口的实现类的数据结构

6. ArrayList实现类

6.1 ArrayList集合的特点

6.2 ArrayList源码解析

6.2.1 ArrayList类的成员变量

6.2.2 ArrayList集合类的构造方法

6.2.3 ArrayList集合类的方法add()

7. LinkedList实现类

7.1 LinkedList集合的特点

7.2 LinkedList集合特有方法

7.3 LinkedList源码解析

7.3.1 LinkedList集合的成员变量

7.3.2 LinkedList集合的成员内部类Node(结点)

7.3.4 LinkedList集合的方法add()添加元素

7.3.5 LinkedList集合的方法get()获取元素

8. Set接口

8.1 Set集合存储和遍历

8.2 Set接口实现类HashSet类

8.3 对象的哈希值

8.4 String类的哈希值

8.5 哈希值的相关问题

8.6 哈希表的数据结构

8.7 哈希表存储对象的过程

8.8 哈希表存储自定义的对象

8.9 哈希表源码

8.10 哈希表面试的问题

9. 红黑树

9.1 TreeSet集合使用

9.2 TreeSet存储自定义对象

10. LinkedHashSet

11. Collections工具类

12. 泛型 Generic

12.1 泛型的安全机制

12.2 泛型中的 E 问题

12.3 自定义泛型类

12.4 泛型方法

12.5 泛型接口

12.6 泛型通配符

12.7 泛型限定

13. 增强型的for循环

13.1 for的格式

14. Map接口

14.1 Map接口方法

14.2 Map集合的遍历——键找值

14.3 Map集合的遍历——键值对映射关系

15. HashMap 实现类

16. Hashtable 实现类

17. LinkedHashMap 实现类

18. Vector 实现类

19. TreeMap 实现类

20. Properties 实现类


第十二章 集合

学习目标

  • 认识集合框架

  • List集合的结构

  • ArrayList集合使用

  • ArrayList源码解析

  • LinkedList集合使用

  • LinkedList源码解析

  • 对象的哈希值

  • 哈希表数据结构

  • Collections工具类

  • 了解泛型及其定义

  • 哈希表确定对象唯一性

  • HashSet源码解析

  • 红黑树结构Red/Black Tree Visualization

  • 对象的自然顺序与比较器

1. 集合框架的由来

JDK1.2版本后,出现这个集合框架,到JDK1.5后,大幅度优化。

  • 集合本质上是存储对象的容器

  • 数组也能存储对象,数组弊端就是定长

  • 解决数组的问题,开发出来集合框架,集合框架无需考虑长度

  • 集合和数组的区别与共同点

    • 集合,数组都是容器,都可以存储数据

    • 集合只存储引用数据类型,不存储基本数据类型(如int,就会换成包装类Integer)

    • 数组可以存储基本类型,也可以存储引用类型

    • 数组定长,集合容器变长

牢记:数据多了存数组,对象多了存集合

  • 集合学习的关键点

    • 怎么存储数据

    • 怎么取出数据

    • 选择哪种容器

2. 集合框架的继承体系

​​​​​​​

 

3. Collection接口

是所有单列集合的顶级接口任何单列集合都是他的子接口,或者是实现类该接口中定义的方法,是所有单列集合的共性方法。

Collection<E> 尖括号就是泛型,E我们要写,集合存储的数据类型

3.1 Collection接口的常用方法

方法的定义方法作用
boolean add(E)元素添加到集合
void clear()清空集合容器中的元素
boolean contains(E)判断元素是否在集合中
boolean isEmpty()判断集合的长度是不是0,是0返回true
int size()返回集合的长度,集合中元素的个数
boolean remove(E)移除集合中指定的元素,移除成功返回true
T[] toArray(T[] a)集合转成数组

(1) boolean add(E)

/**
*  boolean add(E) 元素添加到集合中
*  返回值,目前都是true
*/
public static void collectionAdd(){
    //接口多态创建集合容器对象,存储的数据类型是字符串
    Collection<String> coll = new ArrayList<>();
    //集合对象的方法add添加元素
    coll.add("hello");
    coll.add("world");
    coll.add("java");
    coll.add("money");
    coll.add("wife");
    /**
    *  输出语句中,输出集合对象,调用的是方法toString()
    *  看到的内容是一个完整的字符串, 不叫遍历
    *  遍历时一个个过,一个个输出
    */
    System.out.println(coll);
}

(2)void clear(),int size(),boolean isEmpty()

    /**
     *  void clear() 清空集合中的所有元素
     *  int size() 集合的长度
     */
    public static void collectionClear(){
        Collection<Integer> coll = new ArrayList<>();
        coll.add(1);
        coll.add(2);
        coll.add(3);
        System.out.println(coll);
        System.out.println("集合的长度::"+ coll.size());//长度
        coll.clear();
        System.out.println(coll);
        System.out.println("集合的长度::"+ coll.size());
        System.out.println("集合是空吗?" + coll.isEmpty());//长度=0,isEmpty()返回true
    }

(3)boolean contains(),boolean remove()

/**
     *  boolean contains(E) 判断是否包含
     *  boolean remove(E) 移除元素
     */
public static void collectionContains(){
    //接口多态创建集合容器对象,存储的数据类型是字符串
    Collection<String> coll = new ArrayList<>();
    //集合对象的方法add添加元素
    coll.add("hello");
    coll.add("wife");
    coll.add("world");
    coll.add("java");
    coll.add("money");
    coll.add("wife");
    //判断集合中是否包含某个元素
    boolean b = coll.contains("world");
    System.out.println("b = " + b);

    //移除集合中的元素
    //删除成功返回true,如果有多个相同的对象,删除最先遇到的那个
    boolean b1 = coll.remove("wife");
    System.out.println("b1 = " + b1);
    System.out.println(coll);
}

4. Iterator接口

迭代器接口 Iterator,为集合进行遍历的。迭代器技术是所有Collection集合的通用遍历形式。

4.1 Iterator接口的抽象方法

  • boolean hasNext() 判断集合中是否有下一个可以遍历的元素,如果有返回true

  • E next() 获取集合中下一个元素

  • void remove() 移除遍历到的元素

4.2 获取迭代器接口实现类

迭代器就是为了遍历集合而产生。集合的顶层接口Collection中定义了方法方法的名字就是 iterator()返回值是Iterator接口类型, 返回的是Iterator接口实现类的对象

Collection接口中的方法摘要 :
  public Iterator iterator() ; 返回迭代器接口实现类的对象
  
使用的对象ArrayList,实现接口Collection,重写方法iterator();
public static void main(String[] args) {
    //迭代器遍历集合
    //接口多态创建集合容器对象,存储的数据类型是字符串
    Collection<String> coll = new ArrayList<>();
    //集合对象的方法add添加元素
    coll.add("hello");
    coll.add("world");
    coll.add("java");
    coll.add("money");
    coll.add("wife");
    //1 遍历 集合对象,调用方法iterator() 获取迭代器接口的实现类对象
    Iterator<String> it = coll.iterator();
    //2 迭代器对象的方法,判断集合是否有下元素
    //boolean b = it.hasNext();
    //System.out.println(b);
    //3 迭代器对象的方法,取出元素
    //String str = it.next();
    //System.out.println(str);
    //条件,集合中有下一个元素就可以
    while ( it.hasNext() ){
        String str =  it.next();
        System.out.println(str);
    }
}

4.3 迭代器的实现原理

每个集合容器,内部结构不同,但是迭代器都可以进行统一的遍历实现

结论:迭代器是隐藏在集合的内部的(即为成员内部类),提供公共的访问方式。

//Iterator接口的源码和ArrayList中的iterator的源码
interface Iterator{
    boolean hasNext();
    E next();
    void remove();
}

public class ArrayList {
    public Iterator iterator(){
        return  new Itr();
    }
    
    private class Itr implements Iterator{
         boolean hasNext(); //重写
    	 E next(); //重写
         void remove(); //重写
    }
    
}

 图1:ArrayList的内部类实现接口Iterator的源码图片

4.4 并发修改异常

如何不发生这个异常

异常的产生原因:在迭代器遍历集合的过程中,使用了集合的功能,改变了集合的长度造成

public static void main(String[] args) {
    //迭代器遍历集合
    //接口多态创建集合容器对象,存储的数据类型是字符串
    Collection<String> coll = new ArrayList<>();
    //集合对象的方法add添加元素
    coll.add("hello");
    coll.add("world");
    coll.add("java");
    coll.add("money");
    coll.add("wife");
    //迭代器遍历集合
    Iterator<String> it = coll.iterator();
    while ( it.hasNext() ){
        String str = it.next();
        //判断,遍历到的集合元素是不是java
        if (str.equals("java")){
            //添加元素 出现并发修改异常
            coll.add("add");
        }
        System.out.println(str);
    }
}

4.5 集合存储自定义对象并迭代

public static void main(String[] args) {
    //创建集合,存储自定义的对象
    Collection<Person> coll = new ArrayList<>();
    //集合的方法add存储Person对象
    coll.add( new Person("张三",21) );
    coll.add( new Person("李四",22) );
    coll.add( new Person("王五",23) );
    //迭代器遍历集合

    Iterator<Person> iterator = coll.iterator();
    while (iterator.hasNext()){
        Person person = iterator.next();
        System.out.println(person);
        System.out.println(person.getName());
    }
}

/**
 *  定义私有成员
 *  get set方法
 *  无参数构造方法
 *
 *  满足以上的三个条件 ,这个类,换一个名字,叫JavaBean
 */
public class Person   {
    private String name;
    private int age;
    public Person(){}

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

    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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

5. List接口

List接口,继承Collection接口,是单列集合。

5.1 List接口的特点

  • 这个接口的集合都具有索引

  • 这个接口中的元素允许重复

  • 这个接口中的元素是有序

    • 元素不会排序 ,有序指的是 ,元素存储和取出的顺序是一致的

List接口的所有实现类,都具有以上三个特征

5.2 List接口自己的方法(带有索引)

(1)void add(int index ,E e);

/**
* List接口的方法 add(int index, E e)
* 指定的索引位置,添加元素
*
*   IndexOutOfBoundsException 集合越界异常  长度是size()
*     StringIndexOutOfBoundsException 字符串越界异常  长度是 length()
*     ArrayIndexOutOfBoundsException 数组越界异常  长度是 length
*/
public static void listAdd(){
    List<String> list = new ArrayList<>();
    list.add("a") ;//集合的尾部添加
    list.add("b");
    list.add("c");
    list.add("d");
    list.add("e");
    System.out.println(list);
    //指定的索引上,添加元素 ,3索引添加元素
    list.add(3,"QQ");
    System.out.println(list);
}

(2)E get(int index);

    /**
     *  List接口的方法 E get(int index)
     *  返回指定索引上的元素
     *  List集合可以使用for循环像数组一样的方式遍历
     */
    public static void listGet(){
        List<String> list = new ArrayList<>();
        list.add("a") ;//集合的尾部添加
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        //List接口方法get取出元素
        //String s = list.get(3);
        //System.out.println(s);
        for(int i = 0 ; i < list.size() ; i++){
            System.out.println(list.get(i));
        }
    }

 (3)E set(int index,E e); E remove(int index);

/**
     * List接口方法
     *  E set (int index , E e) 修改指定索引上的元素,返回被修改之前的元素
     *  E remove(int index) 移除指定索引上的元素, 返回被移除之前的元素
     */
    public static void listSetRemove(){
        List<String> list = new ArrayList<>();
        list.add("a") ;//集合的尾部添加
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        System.out.println(list);
        //修改指定索引上的元素,3索引
        String str = list.set(3,"https://www.baidu.com");
        System.out.println(list);
        System.out.println(str);
        //删除指定索引上的元素,删除3索引
        str = list.remove(3);
        System.out.println(list);
        System.out.println(str);
    }

5.3 List集合的特有迭代器 

List接口中的方法 listIterator() 返回迭代器,迭代器的接口是ListIterator,List集合的专用迭代器。

  • ListIterator迭代器接口的方法

    • boolean hasNext()

    • E next()

    • boolean hasPrevious() 判断集合中是否有上一个元素,反向遍历

    • E previous() 取出集合的上一个元素

    /**
     * List接口的方法:
     *   listIterator() List集合的特有迭代器
     *   反向遍历
     */
    public static void iterator(){
        List<String> list = new ArrayList<>();
        list.add("a") ;//集合的尾部添加
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        //获取特有迭代器接口实现类对象
        ListIterator<String> lit = list.listIterator();
        //先要正向遍历
        while (lit.hasNext()){
            String s = lit.next();
            System.out.println(s);
        }
        System.out.println("=============");
        //判断上一个元素
        while (lit.hasPrevious()){
            //取出元素
            String s = lit.previous();
            System.out.println(s);
        }
    }

5.4 List接口的实现类的数据结构

LinkedList(链表)

  • 数组 :

    • 有索引,数组中元素的地址是连续,查询速度快

    • 数组的长度为固定,新数组创建,数组元素的复制,增删的效率慢

  • 链表

    • 链表没有索引,采用对象之间内存地址记录的方式存储

    • 查询元素,必须通过第一个节点依次查询,查询性能慢

    • 增删元素,不会改变原有链表的结构,速度比较快

6. ArrayList实现类

6.1 ArrayList集合的特点

ArrayList类实现接口ListArrayList具备了List接口的特性 (有序,重复,索引)

  • ArrayList集合底层的实现原理是数组,大小可变 (存储对象的时候长度无需考虑)

  • 数组的特点:查询速度快,增删慢。

  • 数组的默认长度是10个,每次的扩容是原来长度的1.5倍。

  • ArrayList是线程不安全的集合,运行速度快。

6.2 ArrayList源码解析

6.2.1 ArrayList类的成员变量

private static final int DEFAULT_CAPACITY = 10; //默认容量
private static final Object[] EMPTY_ELEMENTDATA = {};//空数组
transient Object[] elementData; //ArrayList集合中的核心数组
private int size; //记录数组中存储个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //数组扩容的最大值

6.2.2 ArrayList集合类的构造方法

//无参数构造方法
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //数组没有长度
//有参数的构造方法
public ArrayList(int 10) {
    if (initialCapacity > 0) {
        //创建了10个长度的数组
    	this.elementData = new Object[10];
    } else if (initialCapacity == 0) {
    	this.elementData = EMPTY_ELEMENTDATA;
    } else {
    	throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    }
}

6.2.3 ArrayList集合类的方法add()

new ArrayList<>().add("abc"); //集合中添加元素
public boolean add("abc") {
    //检查容量  (1)
    ensureCapacityInternal(size + 1); 
    //abc存储到数组中,存储数组0索引,size计数器++
    elementData[size++] = "abc";//数组扩容为10
    return true;
}
//检查集合中数组的容量, 参数是1
private void ensureCapacityInternal(int minCapacity = 1) {
    //calculateCapacity 计算容量,方法的参是数组 , 1
    // ensureExplicitCapacity (10) 扩容的
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算容量方法, 返回10
private static int calculateCapacity(Object[] elementData, int minCapacity = 1) {
    //存储元素的数组 == 默认的空的数组  构造方法中有赋值
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //返回最大值   max(10,1)
    	return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
//扩容 
private void ensureExplicitCapacity(int minCapacity = 10) {
    modCount++;
   // 10 - 数组的长度0 > 0
    if (minCapacity - elementData.length > 0)
        //grow方法(10) 数组增长的
    	grow(minCapacity);
}
//增长的方法,参数是(10)
 private void grow(int minCapacity = 10) {
     //变量oldCapacity保存,原有数组的长度  = 0
     int oldCapacity = elementData.length; // 0
     //新的容量 = 老 + (老的 / 2)
     int newCapacity = oldCapacity + (oldCapacity >> 1);// 0
     // 0 - 10 < 0 新容量-计算出的容量
     if (newCapacity - minCapacity < 0)
     	newCapacity = minCapacity; //新容量 = 10
     //判断是否超过最大容量
     if (newCapacity - MAX_ARRAY_SIZE > 0)
     newCapacity = hugeCapacity(minCapacity);
     // minCapacity is usually close to size, so this is a win:
//数组的赋值,原始数组,和新的容量
     elementData = Arrays.copyOf(elementData, newCapacity);
 }

7. LinkedList实现类

7.1 LinkedList集合的特点

LinkedList类实现接口List,LinkedList具备了List接口的特性 (有序,重复,索引)

  • LinkedList底层实现原理是链表,双向链表

  • LinkedList增删速度快

  • LinkedList查询慢

  • LinkedList是线程不安全的集合,运行速度快

7.2 LinkedList集合特有方法

集合是链表实现可以单独操作链表的开头元素和结尾元素

  • void addFirst(E e) 元素插入到链表开头

  • void addLast(E e) 元素插入到链表结尾

  • E getFirst() 获取链表开头的元素

  • E getLast() 获取链表结尾的元素

  • E removeFirst() 移除链表开头的元素

  • E removeLast() 移除链表结尾的元素

  • void push(E e)元素推入堆栈中

  • E pop()元素从堆栈中弹出

public static void main(String[] args) {
	linkedPushPop();
}
//- void push(E e)元素推入堆栈中
//- E pop()元素从堆栈中弹出

public static void linkedPushPop(){
    LinkedList<String> linkedList = new LinkedList<String>();
    //元素推入堆栈中
    linkedList.push("a"); //本质就是addFirst() 开头添加
    linkedList.push("b");
    linkedList.push("c");
    System.out.println("linkedList = " + linkedList);

    String pop = linkedList.pop(); // removeFirst()移除开头
    System.out.println(pop);
    System.out.println("linkedList = " + linkedList);
}

//- E removeFirst() 移除链表开头的元素
//- E removeLast() 移除链表结尾的元素
public static void linkedRemove(){
    LinkedList<String> linkedList = new LinkedList<String>();
    linkedList.add("a"); //结尾添加
    linkedList.add("b"); //结尾添加
    linkedList.add("c"); //结尾添加
    linkedList.add("d"); //结尾添加
    System.out.println("linkedList = " + linkedList);
    //移除开头元素,返回被移除之前
    String first = linkedList.removeFirst();
    //移除结尾元素,返回被移除之前的
    String last = linkedList.removeLast();
    System.out.println("first = " + first);
    System.out.println("last = " + last);
    System.out.println("linkedList = " + linkedList);
}

//- E getFirst() 获取链表开头的元素
//- E getLast() 获取链表结尾的元素
public static void linkedGet(){
    LinkedList<String> linkedList = new LinkedList<String>();
    linkedList.add("a"); //结尾添加
    linkedList.add("b"); //结尾添加
    linkedList.add("c"); //结尾添加
    linkedList.add("d"); //结尾添加
    System.out.println("linkedList = " + linkedList);
    //获取开头元素
    String first = linkedList.getFirst();
    //获取结尾元素
    String last = linkedList.getLast();
    System.out.println("first = " + first);
    System.out.println("last = " + last);
    System.out.println("linkedList = " + linkedList);
}

// void addFirst(E e) 元素插入到链表开头
// void addLast(E e) 元素插入到链表结尾
public static void linkedAdd(){
    LinkedList<String> linkedList = new LinkedList<String>();
    linkedList.add("a"); //结尾添加
    linkedList.add("b"); //结尾添加
    linkedList.add("c"); //结尾添加
    linkedList.add("d"); //结尾添加
    System.out.println("linkedList = " + linkedList);
    //结尾添加
    linkedList.addLast("f");
    linkedList.add("g");

    //开头添加
    linkedList.addFirst("e");
    System.out.println("linkedList = " + linkedList);
}

7.3 LinkedList源码解析

7.3.1 LinkedList集合的成员变量

transient int size = 0; //集合中存储元素个数计数器
transient Node<E> first; //第一个元素是谁
transient Node<E> last; //最后一个元素是谁

7.3.2 LinkedList集合的成员内部类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;
        }
}

7.3.4 LinkedList集合的方法add()添加元素

//添加元素 e 存储元素 abc
//再次添加元素 e
void linkLast(E "abc") {
    //声明新的节点对象 = last
    final Node<E> l = last; // l = null  l "abc"节点
    //创建新的节点对象,三个参数, 最后一个对象,"abc", 上一个对象null
    final Node<E> newNode = new Node<>(l, e, null);
    //新节点赋值给最后一个节点
    last = newNode;
    if (l == null)
        //新存储的几点赋值给第一个节点
    	first = newNode;
    else
    	l.next = newNode;
    size++;
    modCount++;
}

7.3.5 LinkedList集合的方法get()获取元素

//集合的获取的方法
//index是索引, size 长度计数器
Node<E> node(int index) {
    //索引是否小于长度的一半,折半思想
    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;
    }
}

8. Set接口

Set集合,是接口Set,继承Collection接口。Set集合不存储重复元素。

Set集合存储的数据是无序、不重复、无索引。

无序指的是存储和取出的顺序不一样,但是由于使用迭代器遍历,Set集合的输出会根据自然顺序输出,例如“a”自然排序在“b”之前,所以先输出“a”。

Set接口下的所有实现类,都会具有这个特性。

Set接口的方法,和父接口Collection中的方法完全一样。

8.1 Set集合存储和遍历

    public static void main(String[] args) {
        //Set集合存储并迭代
        Set<String> set = new HashSet<String>();
        //存储元素方法 add
        set.add("b");
        set.add("a");
        set.add("c");
        set.add("d");
        set.add("d");
        System.out.println("set = " + set);

        Iterator<String> it = set.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }

8.2 Set接口实现类HashSet类

  • HashSet集合类的特点 :

    • 实现Set接口,底层调用的是HashMap集合

    • HashSet的底层实现原理是哈希表

    • HashSet不保证迭代顺序,元素存储和取出的顺序不一定

    • 线程不安全,运行速度快

 图二:创建HashSet其实就是创建HashMap

8.3 对象的哈希值

每个类继承Object类,Object类定义方法:

public native int hashCode(); // C++语言编写,不开源

方法使用没有区别:方法返回int类型的值,就称为哈希值

哈希值的结果不知道是怎么计算的,调用toString()方法的时候,返回的十六进制数和哈希值是一样的,@1b6d3586叫哈希值 (根本和内存地址是无关的)

public static void main(String[] args) {
    Person p = new Person();
    int code = p.hashCode();
    // int 变量 460141958 (是什么,无所谓, 数字就是对象的哈希值)
    System.out.println(code);
    // com.atguigu.hash.Person@1b6d3586
    System.out.println(p.toString());
 }

 可以重写父类的hashCode()方法,再输出该哈希值或toString()方法

   /**
     * 重写父类的方法
     * 返回int值
     */
    public int hashCode(){
        return 9527;
    }

8.4 String类的哈希值

字符串类重写方法hashCode(),自定义了哈希值,哈希值的计算方法是:

h = 31 * 上一次的计算结果 + 字符数组中元素的ASCII码值

*31 的目的,减少相同哈希值的计算

 String类的哈希值:

    //字符串String对象的哈希值
    private static void stringHash(){
        String s1 ="abc";
        String s2 ="abc";
        System.out.println(s1 == s2); //T
        //String类继承Object,可以使用方法hashCode
        System.out.println(s1.hashCode() == s2.hashCode()); //T
        /**
         * String类继承Object类
         * String类重写父类的方法 hashCode() 自己定义了哈希值
         */
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
        System.out.println("=============");

        /**
         *  字符串内容不一样,有没有可能计算出相同的哈希值
         *    String s1 ="abc";
         *    String s2 ="abc";
         */
        String s3 = "通话";
        String s4 = "重地";
        //1179395
        //1179395
        System.out.println(s3.hashCode());
        System.out.println(s4.hashCode());

        System.out.println(s3.equals(s4));
    }

8.5 哈希值的相关问题

问题 : ① 两个对象A、B 两个对象哈希值相同,equals方法一定返回true吗?

          ② 两个对象A、B 两个对象equals方法返回true,两个对象的哈希值一定相同吗?

结论 : 两个对象的哈希值相同,不要求equals一定返回true. 两个对象的equals返回true,两个对象的哈希值必须一致

Sun 公司官方规定 : 上面的结论

8.6 哈希表的数据结构

数组 + 链表的组合体

class Node{
    E element; //存储的元素
    Node next; //下一个元素
}
main(){
    Node[] node = new Node[5];
}
  • 哈希表的底层数组长度默认是16个,扩容为原来长度的2倍

  • 加载因子默认是0.75F,数组中存储元素的个数达到长度的75%就扩容

8.7 哈希表存储对象的过程

public static void main(String[] args) {
    Set<String> set = new HashSet<String>();
    //存储对象
    set.add("abc");
    set.add("bbc");
    set.add(new String("abc"));
    set.add("通话");
    set.add("重地");
    System.out.println("set = " + set);
}

8.8 哈希表存储自定义的对象

需要重写自定义类的hashCode()和equal()两个方法

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

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result = age;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
public static void main(String[] args) {
    Set<Student> set = new HashSet<Student>();
    //存储Student的对象
    set.add(new Student("a1",201));
    set.add(new Student("a2",202));
    set.add(new Student("a2",202));
    set.add(new Student("a3",203));
    set.add(new Student("a4",204));
    System.out.println("set = " + set);
}

8.9 哈希表源码

HashSet集合本身不具备任何功能内部调用了另一个集合对象HashMap

  • 构造方法无参数
public HashSet() {
	map = new HashMap<>();
}
  • HashMap类的成员变量
//哈希表数组的初始化容量,16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;//价值因子
static final int TREEIFY_THRESHOLD = 8;//阈值,转红黑树
static final int UNTREEIFY_THRESHOLD = 6;//阈值,解除红黑树
static final int MIN_TREEIFY_CAPACITY = 64;//阈值,转红黑树
  • HashMap内部类Node
//节点
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash; //对象哈希值
        final K key; //存储的对象
        V value; //使用Set的集合,value没有值
        Node<K,V> next; //链表的下一个节点
}
  • Set集合存储方法add(),调用的是HashMap集合的方法put()【大致了解就行】

//HashMap存储对象的方法put,Key存储的元素,V是空的对象
public V put(K key, V value) {
    //存储值,传递新计算哈希值,要存储的元素
	return putVal(hash(key), key, value, false, true);
}
 //传递存储的对象,再次计算哈希值
 //尽量降低哈希值的碰撞
static final int hash(Object key) { 
   int h;
   return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//存储值,重写计算的哈希值,要存储值
final V putVal(int hash, K key, V value, boolean false,
               boolean true) {
    //Node类型数组,     Node类型数组     n, i
     Node<K,V>[] tab; Node<K,V> p; int n, i;
     //tab =Node[]=null
    if ((tab = table) == null || (n = tab.length) == 0){
        //n=赋值为 tab数组=resize()方法返回数组,默认长度的数组16
          n = (tab = resize()).length;// 16
        //数组的长度-1 & 存储对象的哈希值,确定存储的位置
        //判断数组的索引上是不是空的
         if ((p = tab[i = (n - 1) & hash]) == null)
             //数组索引 赋值新的节点对象,传递计算的哈希值,存储的对象
             tab[i] = newNode(hash, key, value, null);
        else{
            //数组的索引不是空,要存储的对象,已经有了
            //判断已经存在的对象,和要存储对象的哈希值和equals方法
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                //遍历该索引下的链表,和每个元素比较hashCode和equals
        }
    }
}

8.10 哈希表面试的问题

JDK7版本和JDK8版本的哈希表的区别

  • JDK7没有转红黑树

  • JDK8转成红黑树

    • 转成树的两个参数

      • 当一个数组中存储的链表长度>=8 转树

      • 数组的整体长度超过64

    • 树转回链表

      • 链表的长度 <=6

  • JDK7元素采用头插法,JDK8元素采用尾插法

9. 红黑树

红黑树(Red-Black-Tree)

  • 二叉树,本质就是链表

    • 查询速度快

    • 每个一个节点,只有两个子节点,左和右

    • 树长偏了

  • 自然平衡二叉树

    • 二叉树的基础上,改进,保证树是平衡的

  • 红黑树

    • 每个节点有颜色,要么红,要么是黑

    • 根节点必须是黑色

    • 叶子节点必须是黑色

    • 变量表示颜色,true黑色,false红色

9.1 TreeSet集合使用

TreeSet集合,底层是红黑树结构,依赖于TreeMap的实现

红黑树特点查找速度快,线程不安全

可以对存储到红黑树的元素进行排序,元素的自然顺序 abcd.. 字典顺序

   public static void treeSetString(){
       Set<String> set = new TreeSet<>();
       //存储元素
       set.add("abcd");
       set.add("ccdd");
       set.add("z");
       set.add("wasd");
       set.add("bbaa");
       System.out.println("set = " + set);
   }

9.2 TreeSet存储自定义对象

/**
* TreeSet集合存储Student对象
*/
public static void treeSetStudent(){
    Set<Student> set = new TreeSet<Student>();
    set.add(new Student("a",10));
    set.add(new Student("b",20));
    System.out.println("set = " + set);
}

程序出现了异常,类型的转换异常 ClassCastException

异常原因,Student类不能进行类型的转换,有接口没有实现java.lang.Comparable.

类实现接口Comparable,这个类就具有了自然顺序。

  • Student类具有自然顺序

    • 实现接口Comparable,重写方法compareTo

   /**
     * 重写方法compareTo
     * 返回int类型
     * 参数 : 要参与比较的对象
     * this对象和student对象
     *
     * 红黑树,后来的对象是this,原有的对象是参数
     */
    public int compareTo(Student student){
        return this.age - student.age;
    }
  • 自定义比较器

    • java.util.Comparator接口

/**
 * 自定义的比较器
 * 实现接口,重写方法
 */
public class MyCom implements Comparator<Student> {
    @Override
    /**
     * TreeSet集合自己调用方法
     * 传递参数
     * Student o1, Student o2
     * o1是后来的对象
     * o2是已经有的对象
     */
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}
Set<Student> set = new TreeSet<Student>( new MyCom());

 图三:TreeSet集合的构造器

10. LinkedHashSet

底层的数据结构是哈希表,继承HashSet

LinkedHashSet数据是双向链表有序的集合,存储和取出的顺序一样

public static void main(String[] args) {
    Set<String> set = new LinkedHashSet<>();
    set.add("b");
    set.add("e");
    set.add("c");
    set.add("a");
    set.add("d");
    System.out.println("set = " + set);
}

11. Collections工具类

  • java.util.Collection 集合的顶级接口

  • java.util.Collections 操作集合的工具类

    • 工具类的方法全部静态方法,类名直接调用

    • 主要是操作Collection系列的单列集合,少部分功能可以操作Map集合

/**
 *  集合操作的工具类
 *  Collections
 *  工具类有组方法: synchronized开头的
 *
 *  传递集合,返回集合
 *  传递的集合,返回后,变成了线程安全的集合
 */
public class CollectionsTest {
    public static void main(String[] args) {
        sort2();
    }
    //集合元素的排序,逆序
    public static void sort2(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(15);
        list.add(5);
        list.add(20);
        list.add(9);
        list.add(25);
        System.out.println("list = " + list);
        //Collections.reverseOrder() 逆转自然顺序
        Collections.sort(list,Collections.reverseOrder());
        System.out.println("list = " + list);
    }
    //集合元素的排序
    public static void sort(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(15);
        list.add(5);
        list.add(20);
        list.add(9);
        list.add(25);
        System.out.println("list = " + list);
        Collections.sort(list);
        System.out.println("list = " + list);
    }

    //集合元素的随机交换位置
    public static void shuffle(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(15);
        list.add(5);
        list.add(20);
        list.add(9);
        list.add(25);
        System.out.println("list = " + list);
        Collections.shuffle(list);
        System.out.println("list = " + list);

    }

    //集合的二分查找
    public static void binarySearch(){
        List<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(5);
        list.add(9);
        list.add(15);
        list.add(20);
        list.add(25);
        int index = Collections.binarySearch(list, 15);
        System.out.println(index);
    }
}

12. 泛型 Generic

泛型技术是JDK版本一大升级,源自于JDK1.5

泛型就是集合类<泛型>

//无泛型写法
public static void main(String[] args) {
    /**
       *  JDK没有泛型技术,就是这样写
       *  集合可以存储任何数据类型
       *  添加元素的数据类型是Object
       */
    List list = new ArrayList();
    list.add("a");
    list.add(1);
    Iterator it = list.iterator();
    while (it.hasNext()){
        Object obj = it.next();//不能类型转换
        System.out.println(obj);
    }
}

12.1 泛型的安全机制

软件升级:安全性提高,修复Bug错误,改善用户体验,增加功能,提升性能

JDK1.5里程碑版本

泛型作用:强制了集合存储固定的数据类型

泛型的书写格式:

集合类<存储的数据类型>  变量名 = new 集合类<存储的数据类型>();
                                       类型可以不写:钻石操作符

加入泛型后,程序的安全性提升了

    public static void main(String[] args) {
        /**
         *  JDK没有泛型技术,就是这样写
         *  集合可以存储任何数据类型
         *  添加元素的数据类型是Object
         */
        List<String> list = new ArrayList<String>();
        list.add("a");
        list.add(1); //编译错误,数据类型不匹配

        Iterator<String> it = list.iterator();
        while (it.hasNext()){
            String obj =it.next(); //类型转换不需要
            System.out.println(obj);
        }
    }
  • 使用泛型的好处:

    • 安全性提高了

    • 程序的代码量减少

    • 避免了类型的强制转换

    • 程序的问题,由运行时期,提前到编译时期

12.2 泛型中的 E 问题

E没有什么实际价值,只是一个变量而已

特殊:等待接收指定的数据类型

//ArrayList<E>
//创建对象
ArrayList<String> al = new ArrayList<String>();
//E 不在是E了,变成String

public boolean add(String e) {

}

12.3 自定义泛型类

/**
 *  定义类,类名叫工厂
 *  自定义泛型类
 *  Factory<什么都可以写> 只是变量名而已
 */
public class Factory<QQ> {
    private QQ q;

    public void setQ(QQ q){
        this.q = q;
    }

    public QQ getQ(){
        return q;
    }
}
public static void main(String[] args) {
    //创建对象Factory类对象
    // Factory factory = new Factory();//没有泛型,QQ就是Object

    Factory<String> factory = new Factory<String>();
    factory.setQ("abc");
    String s = factory.getQ();
    System.out.println(s);

    Factory<Double> factory2 = new Factory<Double>();
    factory2.setQ(1.5);
    Double q = factory2.getQ();
    System.out.println(q);
}

12.4 泛型方法

/**
 * 泛型的方法,方法参数上
 */
public class Factory<Q> {

    /*
     * 静态方法
     * Q是非静态的, Q的数据类型,是new的时候指定的
     *
     * 静态方法参数中的泛型,不能和类一样
     * 静态方法的泛型,需要在方法上单独定义
     * 写在返回值类型的前面
     */
    public static <T> void staticMethod(T q){
        System.out.println(q);
    }

    public void print(Q q){
        System.out.println(q);
    }
}

12.5 泛型接口

  • 实现类实现接口,不实现泛型

  • 实现类实现接口,同时指定泛型

//泛型接口
public interface Inter <T> {
    public abstract void inter(T t);
}
/**
 * 实现接口,不理会泛型
 * 对象创建的时候,指定类型
 */
public class InterImpl<T> implements Inter<T>{
    public void inter(T t){
        System.out.println(t);
    }
}
/**
 * 实现接口,同时指定泛型
 */
public class InterImpl2 implements Inter<String> {

    public void inter(String s) {
        System.out.println("s=="+s);
    }
}
public class GenericTest {
    public static void main(String[] args) {
        Inter<String> in = new InterImpl<String>();
        in.inter("ok");

        Inter in2 = new InterImpl2();
        in2.inter("kkk");
    }
}

实现接口,但是不实现泛型,会在调用方法传入参数时才确认数据类型,即参数类型会变成Object。

12.6 泛型通配符

?指可以是任意引用类型

//泛型的通配符
public class GenericTest {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<String>();
        stringList.add("abc");
        stringList.add("bbc");

        List<Integer> integerList =  new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);

        each(stringList);
        each(integerList);
    }
    /**
     * 定义方法,可以同时迭代器 遍历这两个集合
     * 方法的参数,是要遍历的集合,不确定是哪个集合
     * 定义参数,写接口类型,不要写实现类
     */
    public static void each(List<?> list){
        Iterator<?> it = list.iterator();
        while (it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }
    }
}

12.7 泛型限定

泛型限定:限制的是数据类型

  • <? extends Company> 传递类型可以是Company或者是他的子类

  • <? extends E>传递E类型或者是E的子类,泛型上限限定

  • <? super E >传递E类型或者是E的父类,泛型下限限定

public static void main(String[] args) {
    //创建集合,存储员工对象

    //开发部的
    List<Development> devList = new ArrayList<Development>();
    //存储开发部员工对象
    Development d1 = new Development();
    d1.setName("张三");
    d1.setId("开发部001");

    Development d2 = new Development();
    d2.setName("张三2");
    d2.setId("开发部002");
    devList.add(d1);
    devList.add(d2);

    //财务部集合
    List<Financial> finList = new ArrayList<Financial>();
    Financial f1 = new Financial();
    f1.setName("李四");
    f1.setId("财务部001");

    Financial f2 = new Financial();
    f2.setName("李四2");
    f2.setId("财务部002");
    finList.add(f1);
    finList.add(f2);
    System.out.println(devList);
    System.out.println(finList);

    each(devList);
    each(finList);

    //        List<Integer> integerList = new ArrayList<>();
    //        integerList.add(1);
    //        each(integerList);
}
/**
     * 要求 : 定义方法
     * 同时遍历2个集合
     * 遍历的同时取出集合元素,调用方法work()
     * ? 接收任何一个类型
     * 只能接收 Company和子类对象
     * 明确父类,不能明确子类
     */
public static void each(List<? extends Company> list){
    Iterator<? extends Company> it = list.iterator();
    while (it.hasNext()){
        //取出元素
        Company obj =it.next();
        obj.work();
    }
}

13. 增强型的for循环

JDK1.5出现的特性:循环的特性 (少些代码)

Collection是单列集合的顶级接口,但是到JDK1.5后,为Collection找了个爹

java.lang.Iterable接口:实现接口,就可以成为 "foreach"语句的目标

Collection、List、Set都继承了接口,包括数组

 Iterable接口定义了foreach()和iterator()两个方法

13.1 for的格式

for(数据类型 变量名 : 集合或者数组){}
  • 遍历数组

   /**
     * for循环遍历数组
     * for(数据类型 变量名 : 集合或者数组){}
     */
    public static void forArray(){
        int[] arr = {1,3,5,7,9};
        for(int i : arr){
            System.out.println(i+1);
        }
        System.out.println("arr=="+arr[0]);
    }
  • 遍历集合

     /* for循环遍历集合
     */
    public static void forList(){
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        for(String s : list){
            System.out.println(s);
        }
    }

14. Map接口

java.util.Map接口,是双列集合的顶级接口。

Map集合容器每次存储2个对象,一个对象称为键(Key),一个对象称为值(Value)。

在一个Map的集合容器中,键保证唯一性,不包含重复键,每个键只能对应一个值

14.1 Map接口方法

  • V put(K,V)存储键值对,存储重复键,返回被覆盖之前的值

/**
* put方法,存储键值对
* Map接口的实现类HashMap
*/
public static void mapPut(){
    //创建对象,指定键的数据类型,值的数据
    Map<String,Integer> map = new HashMap<String,Integer>();
    map.put("a",1);
    map.put("b",2);
    map.put("c",3);
    map.put("d",4);
//相同键,返回被覆盖的值
    Integer value = map.put("c",5);
    System.out.println("map = " + map);
    System.out.println("value = " + value);
}

  • V get(K)通过键获取值,参数传递键,找这个键对应的值,没有这个键返回null

/**
* V get(K)通过键获取值,参数传递键,找这个键对应的值,没有这个键返回null
*/
public static void mapGet(){
    //创建对象,指定键的数据类型,值的数据
    Map<String,Integer> map = new HashMap<String,Integer>();
    map.put("a",1);
    map.put("b",2);
    map.put("c",3);
    map.put("d",4);
    //键找值
    Integer value = map.get("f");
    System.out.println(value);
}
  • boolean containsKey(K)判断集合是否包含这个键,包含返回true

  • boolean containsValue(V)判断集合是否包含这个值,包含返回true

  • int size() 返回集合长度,Map集合中键值对的个数

  • V remove(K)移除指定的键值对,返回被移除之前的值

  • Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合

/*boolean containsKey(K)判断集合是否包含这个键,包含返回true
- boolean containsValue(V)判断集合是否包含这个值,包含返回true
- int size() 返回集合长度,Map集合中键值对的个数
- V remove(K)移除指定的键值对,返回被移除之前的值
- Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合
*/
public static void mapMethod(){
    //创建集合,键是整数,值是String
    Map<Integer,String> map = new HashMap<Integer, String>();
    map.put(1,"a");
    map.put(2,"b");
    map.put(3,"c");
    map.put(4,"d");
    map.put(5,"e");
    //boolean containsKey(K)判断集合是否包含这个键,包含返回true
    boolean b = map.containsKey(1);
    System.out.println("集合中包含键:"+b);

    //boolean containsValue(V)判断集合是否包含这个值,包含返回true
    b = map.containsValue("c");
    System.out.println("集合中包含值:"+b);

    //size()返回集合的长度
    int size = map.size();
    System.out.println("集合长度:"+size);

    //V remove(K)移除指定的键值对,返回被移除之前的值
    String value =  map.remove(1);
    System.out.println("被删除之前的:"+value);
    System.out.println(map);

    //Collection<V> values() Map集合中的所有的值拿出,存储到Collection集合
    Collection<String> coll =  map.values();
    for(String s : coll){
    	System.out.println(s);
    }
}

14.2 Map集合的遍历——键找值

  • 实现思想 :

    • Map接口定义了方法 keySet() 所有的键,存储到Set集合

    • 遍历Set集合

    • 取出Set集合元素 Set集合的元素是Map集合的键

    • Map集合方法get()传递键获取值

    /**
     * - Map接口定义了方法  keySet() 所有的键,存储到Set集合
     * - 遍历Set集合
     * - 取出Set集合元素 **Set集合的元素是Map集合的键**
     * - Map集合方法get()传递键获取值
     */
public static void mapKeySet(){
    Map<String,String> map = new HashMap<String, String>();
    map.put("a","java");
    map.put("b","c++");
    map.put("c","php");
    map.put("d","python");
    map.put("e","erlang");
    //Map接口定义了方法  keySet() 所有的键,存储到Set集合
    Set<String> set = map.keySet();
    //遍历Set集合
    Iterator<String> it = set.iterator();
    //取出Set集合元素 **Set集合的元素是Map集合的键**
    while (it.hasNext()){
        String key = it.next();
        //Map集合方法get()传递键获取值
        String value =  map.get(key);
        System.out.println(key+"==="+value);
    }
}

14.3 Map集合的遍历——键值对映射关系

  • 实现思想 :

    • Map接口的方法 Set< Map.Entry<Key,Value> >    entrySet()【泛型可以多个表示】

      • 方法返回Set集合,集合中存储的元素,比较特别

      • 存储的是Map集合中,键值对映射关系的对象,内部接口 Map.Entry

    • 遍历Set集合

    • 取出Set集合的元素

      • 是Map.Entry接口对象

      • 接口的对象方法:getKey(),getValue()

 public static void mapEntrySet(){
     Map<String,String> map = new HashMap<String, String>();
     map.put("a","java");
     map.put("b","c++");
     map.put("c","php");
     map.put("d","python");
     map.put("e","erlang");
     //Map接口的方法 Set< Map.Entry<Key,Value> > entrySet()
     Set<Map.Entry<String,String>>  set = map.entrySet();
     //- 遍历Set集合
     Iterator<Map.Entry<String,String>> it = set.iterator();
     while (it.hasNext()){
         //取出Set集合的元素
         Map.Entry<String,String> entry =  it.next();
         //- 接口的对象方法: getKey() ,getValue()
         String key = entry.getKey();
         String value = entry.getValue();
         System.out.println(key +"==="+ value);
     }
 }

15. HashMap 实现类

  • HashMap集合特点

    • 是哈希表结构【Set和Map的底层数据结构都是是哈希表】

    • 保证键唯一性,用于键的对象,必须重写hashCode,equals方法【伞公司写的hashCode方法不对外开放的,所以不知道写了什么。但是能确定的一点是,只要是桐哥类建立的对象,他们的哈希值都是一样的。所以保证键唯一性需要重写hashCode,equals方法】

    • 线程不安全集合,运行速度快

    • 集合运行使用null,作为键或者值

  /**
     * HashMap集合
     * 键是Person,值是String
     */
    public static void hashMap2(){
        Map<Person,String> map = new HashMap<Person, String>();
        map.put(new Person("a",20),"广东");
        map.put(new Person("b",22),"香港");
        map.put(new Person("b",22),"贵港");
        map.put(new Person("c",24),"澳门");
        map.put(new Person("d",26),"深圳");
        System.out.println("map = " + map);
    }

    /**
     * HashMap集合
     * 键是字符串,值是Person
     */
    public static void hashMap1(){
        Map<String, Person> map =  new HashMap<String, Person>();
        map.put("a",new Person("张三",20));
        map.put("b",new Person("张三",20));
        map.put("c",new Person("张三",20));
        map.put(null,null);

        //Set<String> set = map.keySet();
        for(String key : map.keySet()){
            //Person person =  map.get(key);
            System.out.println(key+"==="+map.get(key));
        }
        System.out.println("==============");

        //Set<Map.Entry<String,Person>> set = map.entrySet();
        for(Map.Entry<String,Person> entry : map.entrySet()){
            System.out.println(entry.getKey()+"==="+entry.getValue());
        }

16. Hashtable 实现类

Map接口的实现类Hashtable,Hashtable类诞生于JDK1.0版本,Map接口诞生于JDK1.2版本。 Hashtable类从JDK1.2开始,改进为实现Map接口。

  • Hashtable类的特点

    • 底层数据结构是哈希表

    • 线程安全的,运行速度慢,被更加先进的HashMap取代

    • 不允许null值,null键,存储null直接抛出空指针异常。

17. LinkedHashMap 实现类

LinkedHashMap继承HashMap实现Map接口LinkedHashMap底层实现原理是哈希表,双向链,存取有序。其它的特性和父类HashMap一样。

public static void main(String[] args) {
    Map<String,String> map = new LinkedHashMap<String, String>();
    map.put("aa","qq");
    map.put("123","qq");
    map.put("bbb","qq");
    System.out.println(map);
}

18. Vector 实现类

List接口的实现Vector,命运和Hashtable一样。

  • Vector类的特点

    • 底层实现结构是数组

    • 数组的默认容量是10,每次扩容是原来的长度*2

    • 线程安全,运行速度慢,被ArrayList取代

19. TreeMap 实现类

  • TreeMap集合的特点

    • 底层实现是红黑树结构 (添加查询速度比较快)

    • 存储到TreeMap中元素,对键进行排序

    • 排序依据 :

      • 对象的自然顺序,作为键的对象,实现了接口Comparable

      • 自己提供比较器,实现接口Comparator,优先级高

    • 线程不安全的,运行速度快

 /**
     * TreeMap集合存储对象
     * Student作为键,字符串是值
     * 自定义的比较器排序
     */
    public static void treeMap2(){
        Map<Student,String> map = new TreeMap<Student, String>( new MyCom() );
        map.put(new Student("a",20),"广东");
        map.put(new Student("b",19),"广西");
        System.out.println("map = " + map);
    }

    /**
     * TreeMap集合存储对象
     * Person作为键,字符串是值
     */
    public static void treeMap1(){
        Map<Person,String> map = new TreeMap<Person, String>();
        map.put(new Person("a",20),"广东");
        map.put(new Person("b",19),"广西");
        System.out.println("map = " + map);
    }
/**
 * 自定义的比较器,实现接口 Comparator
 */
class MyCom implements Comparator<Student>{
    /**
     *  方法compare 是TreeMap调用
     *  传递参数,后来的对象传递到s1, 已经有的对象传递到s2
     */
   public int compare(Student s1, Student s2){
       return s1.getAge() - s2.getAge();
   }

}
 /**
     *  进行比较:
     *   compareTo方法由,集合TreeMap调用
     *   传递相关的参数 集合中后来的对象是this,先来的对象是参数 p
     */
    public int compareTo(Person p){
        return this.age - p.age;
    }

20. Properties 实现类

  • Properties集合特点

    • 继承Hashtable,实现Map接口

    • 底层是哈希表结构

    • 线程是安全的,运行速度慢

    • 集合没有泛型的写法,键和值的数据类型锁定为String类型

    • 集合有自己的特有方法

    • 此集合可以和IO流对象结合使用,实现数据的持久存储

    • 方法和IO相关:load(输入流)

 /**
     * 集合遍历
     *   Properties类的方法 stringPropertyNames() [等效于map.keySet()] 返回Set集合
     *   Set集合存储的是 Properties集合的所有键
     */
    public static void prop3(){
        Properties prop = new Properties();
        prop.setProperty("a","1");
        prop.setProperty("b","2");
        prop.setProperty("c","3");
        Set<String> set = prop.stringPropertyNames();
        for(String key : set){
            System.out.println(key +"=="+ prop.getProperty(key));
        }
    }

    /**
     * 集合取出元素
     *  Properties集合取出方法 getProperty(String key)
     */
    public static void prop2(){
        Properties prop = new Properties();
        prop.setProperty("a","1");
        prop.setProperty("b","2");
        prop.setProperty("c","3");
        System.out.println(prop);
        String value = prop.getProperty("a");
        System.out.println(value);
    }

    /**
     *  集合存储键值对
     *  Map接口,存储方法put
     *  Properties集合存储方法 setProperty(String key,String value)
     */
    public static void prop1(){
        Properties prop = new Properties();
        prop.setProperty("a","1");
        prop.setProperty("b","2");
        prop.setProperty("c","3");
        System.out.println(prop);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/676563.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Git常用命令及在IDEA中的使用】

Git常用命令及在IDEA中的使用 Git常用命令及在IDEA中的使用1 Git 概述1.1 Git 简介1.2 Git 下载与安装 2 Git 代码托管服务2.1 常用的Git 代码托管服务2.2 使用码云代码托管服务 3 Git 常用命令3.1 Git 全局设置3.2 获取 Git 仓库3.3 工作区、暂存区、版本库 概念3.4 Git工作区…

MyBatis面试题总结

1.概念/使用方法向的问题 1.1 什么是Mybatis? &#xff08;1&#xff09;Mybatis是一个半ORM框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。 &#xff08;2&a…

​​​​SpringBoot 监控神器——Actuator 保姆级教程

pom.xml info beans conditions heapdump shutdown mappings threaddump loggers 端点 metrics 端点 自定义Endpoint 自定义监控端点常用注解 使用Filter对访问actuator做限制 Spring Boot Monitor做监控页面 SpringBoot自带监控功能Actuator&#xff0c;可以帮助…

Kubernetes学习笔记-kubernetes应用扩展(2)-使用kubernetes服务目录扩展kubernetes20230623

一、服务目录介绍 服务目录就是列出所有的服务的目录。用户可以浏览目录并自行设置目录中列出的服务实例&#xff0c;无须处理服务运行所需的pod、service、configmap和其他资源。这听起来和自定义网站资源很类似。 服务目录并不会为每种服务类型的api服务器添加自定义资源&a…

全栈开发实战那些事

文章目录 一个网站是怎么来的&#xff1f; Git篇隔离项目和原有Git工程联系Git冲突的原因通常有以下几种&#xff1a; IDEA篇IDEA常用操作Git可视化操作&#xff08;提交代码前先pull更新merge最新版本一下再push&#xff0c;保证提交的最终项目是最新&#xff09; IDEA中Git冲…

Jenkins 发送文件到远程服务器:Publish Over SSH 插件

Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件 文章目录 Jenkins 发送文件到远程服务器&#xff1a;Publish Over SSH 插件一、Publish Over SSH 插件1、概述2、主要功能和特点3、插件主页4、安装 Publish Over SSH 插件5、配置远程主机 二、发送文件到远程主…

Python基础篇(七):面向对象的编程思想

面向对象 前言1. 面向对象编程思想1.1 面向对象的相关概念1.2 面向对象的三大特性 2. 类的定义2.1 使用class关键字定义类2.2 创建类对象并调用属性和方法 3.面向对象思想示例3.1 详细示例&#xff1a;图形类的设计3.2 基类 Shape3.3 子类 Circle3.4 子类 Rectangle3.5 使用图形…

【Java-SpringBoot+Vue+MySql】Day5-前端进阶

目录 一、Axios网络请求 中文文档&#xff1a; 安装&#xff1a; 导入&#xff1a; 使用方法&#xff1a; 基本语法&#xff1a; 生命周期函数&#xff1a; 二、前端路由VueRouter 视频&#xff1a;12.前端路由VueRouter_哔哩哔哩_bilibili 参考文档: 三、状态管理VueX …

Linux设备驱动程序(三)——字符驱动

文章目录 前言一、scull 的设计二、主设备号和次设备号1、设备编号的内部表达2、分配和释放设备编号3、主编号的动态分配 三、一些重要的数据结构1、文件操作&#xff08;file_operation&#xff09;2、文件结构&#xff08;struct file&#xff09;3、inode 结构 四、字符设备…

【软件设计师暴击考点】黄金五题套路拿

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…

AutoSAR系列讲解(入门篇)2.1-AppL概述

AppL概述 开局一张Autosar的框架图&#xff1a; 一、AppL的组成 二、先来举个例子 三、SWC的通信 四、SWC的分配 开局一张Autosar的框架图&#xff1a; 一、AppL的组成 AppL中最重要的就是SWC了&#xff0c;而SWC与其他SWC通信需要接口&#xff0c;每个SWC中又由runnable…

【C++篇】OOP上部分:封装类和对象

友情链接&#xff1a;C/C系列系统学习目录 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的…

C++【STL】之反向迭代器

反向迭代器 前面在vector和list的模拟实现中都有讲到正向迭代器&#xff0c;今天我们就来讲解一下反向迭代器的思想和模拟实现&#xff0c;在某些场景下还是很实用的&#xff0c;下面正文直接开始。 文章目录&#xff1a; 反向迭代器1. 反向迭代器结构2. 反向迭代器实现2.1 多…

一起学AI:核心基础概念

一起学AI&#xff1a;核心基础概念 AI基础概念什么是学习率&#xff1f;batchsize和epoch分别是什么&#xff1f;梯度消失与梯度爆炸是什么&#xff1f;什么是过拟合、欠拟合和泛化&#xff1f;归一化、正则化、标准化是什么&#xff1f;线性回归和逻辑回归是什么&#xff1f;神…

端午特别征文

前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 韩顺平教育的java坦克大战 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、Java绘图技术 1.g.drawOval()是什么&#xff1f; 画椭圆函数g.drawOval(); (宽高时候为圆形) …

Kafka系列之:一次性传送和事务消息传递

Kafka系列之&#xff1a;一次性传送和事务消息传递 一、目标二、关于事务和流的一些知识三、公共接口四、示例应用程序五、新配置六、计划变更1.幂等生产者保证2.事务保证 七、关键概念八、数据流九、授权十、RPC 协议总结1.获取请求/响应2.生产请求/响应3.ListOffset请求/响应…

GIT —— 基本操作,分支管理,远程操作

GIT 企业开发 最近找实习&#xff0c;在完善简历&#xff0c;遂发一发存货&#xff0c;有待持续完善&#xff0c;我认为这部分非常的有意义 1. 基本操作 1.1 创建&#xff0c;配置本地仓库 创建 mkdir gitcode cd gitcode git init配置 git config user.name "bts&quo…

HTTP 超文本传输协议

1.HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09; 是一种用于传输超媒体文档&#xff08;例如HTML文件&#xff0c;可以包含文本&#xff0c;视频&#xff0c;图片&#xff0c;音乐&#xff0c;地图定位&#xff09;的应用层协议&#x…

【windows10】查看计算机的WIFI密码

【windows10】查看计算机的WIFI密码 1、背景2、操作 1、背景 无线路由器设置完密码后&#xff0c;经常会忘记。 当有新的设备需要接入网络的时候&#xff0c;如何能快速获得wifi密码呢&#xff1f; 本博客分享一种通过已联网的计算机来查看wifi密码。 2、操作 -step-2.1、打…

阿里云服务器的备案流程和规定如何?如何办理备案手续?

阿里云服务器的备案流程和规定如何&#xff1f;如何办理备案手续&#xff1f;   一、备案流程概述   阿里云服务器的备案流程分为五个主要步骤&#xff1a;1&#xff09;实名认证&#xff1b;2&#xff09;填写备案信息&#xff1b;3&#xff09;上传资料&#xff1b;4&…