Java集合详解,韩顺平老师Java集合学习笔记

1、集合的理解与优点

1.1 集合

​ 可以把集合理解为多种数据放在一起的数据类型或者叫数据结构

1.2 集合和数组的区别

​ 数组:只能指定长度,保存同一类型数据在这里插入图片描述

​ 集合:长度可变,可以保存不同类型的数据

public class Arr {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        //数组增加数据
        Person[] people = new Person[1];
        people[0] = new Person();
        Person[] people1 = new Person[people.length+1];
        for (int i = 0; i < people1.length-1; i++) {
            people1[i] = people[i] ;
        }
        people1[people1.length-1] = new Person();
        //集合增加数据
        Collection collection = new ArrayList();
        collection.add(new Person("jack",20));
        collection.add(new Person("tom",21));
        System.out.println(collection);
    }
}
class Person{
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public  Person(){
    }

    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 +
                '}';
    }
}
1.3 集合的优点

​ 1)可以动态保存任意多个对象,使用比较方便。

​ 2)提供了一系列方便的操作对象的方法:add、remove、set、get等。

​ 3)使用集合添加,删除新元素的代码简洁。

2、 集合的框架体系

​ 集合主要分两组,单列(在集合里面放的单个对象)和双列(对象在集合里面以键值对形式存在)

2.1 单列集合
//单列
ArrayList arrayList = new ArrayList();
arrayList.add("Jack");
arrayList.add("Tom");

在这里插入图片描述

2.2 双列集合
//双列集合  hashmap等
HashMap hashMap = new HashMap();
hashMap.put("name","tom");

在这里插入图片描述

3、connection

3.1 connection的方法

在这里插入图片描述

public class CollectionMethod {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        //以ArrayList集合类来演示
        //由于 ArrayList 实现了 Collection 接口,所以可以将 ArrayList类型的对象赋值给 Collection 类型的引用变量。这体		    //现了 Java 的多态性
        Collection collection = new ArrayList();
        
        //add 添加单个元素
        collection.add("Tom");
        collection.add("Jery");
        collection.add(10);
        collection.add(true);
        System.out.println("collection=" + collection);
        
        //remove 删除单个元素
        collection.remove("Tom");
        System.out.println("collection=" + collection);
        
        //contains 查找元素是否存在
        System.out.println(collection.contains("Tom"));//false
        
        //isEmpty 判断集合是否为空
        System.out.println(collection.isEmpty());//false
        
        //size 获取元素个数
        System.out.println(collection.size());//3
        
        //clear 清空
        collection.clear();
        System.out.println("collection=" + collection);//[]
        
        //addAll 添加多个元素
        Collection collection1 = new ArrayList();
        collection1.add("面包");
        collection1.add("薯条");
        collection1.add("汉堡");
        collection.addAll(collection1);
        System.out.println("collection=" + collection);
        
        //containsAll 检查多个元素是否存在
        System.out.println(collection.containsAll(collection1));//true
        
        //removeAll 删除多个元素
        collection.add("火锅");
        System.out.println("collection=" + collection);
        collection.removeAll(collection1);
        System.out.println("collection=" + collection);//火锅 
    }
}
3.2 collection的遍历
3.2.1 迭代器遍历

1)lterator对象称为迭代器,主要用于遍历 Collection 集合中的元素。

2)所有实现了Collection接口的集合类都有一个iterator()方法,用以返回
一个实现了lterator接口的对象,即可以返回一个迭代器。

3)迭代器的执行原理

Iterator iterator = collection.iterator();//得到一个集合的迭代器
//hasNext() 判断是否还有下一个元素
//快捷键: itit
while(iterator.hasNext()){
    //next() 将指针下移,然后将下移以后集合位置上的元素返回
    Object obj =  iterator.next();
    system.out.println("obj=" + obj);
}

在这里插入图片描述

3.2.2 增强for循环
//快捷键:I
System.out.println("增强for循环");
for (Object o :collection) {										
    System.out.println("o=" + o);
}
3.2.3 遍历例子
public class CollectionIterator {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        Collection collection = new ArrayList();
        collection.add(new Book("活着","余华",28.8));
        collection.add(new Book("第七天","余华",20.8));
        collection.add(new Book("平凡的时间","路遥",80.8));
        //使用iterator实现遍历
        //1、先得到collection的iterator
        Iterator iterator = collection.iterator();
        //2、使用while循环遍历
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println("obj=" + obj);
        }
        //3、当遍历完成后iterator.next()指向最后一个元素,如果再用iterator.next()会报错NoSuchElementException
        //4、要重新遍历可以重置迭代器
        System.out.println("第二次迭代");
        iterator = collection.iterator();
        //快捷键 itit
        //查看所有快捷键 Ctrl+j
        System.out.println("迭代器遍历");
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println("obj=" + obj);
        }
        //快捷键 I
        System.out.println("使用增强for循环遍历");
        for (Object o : collection) {
            System.out.println("o=" + o);
        }
    }
}

class Book{
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }
    public Book(){

    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

4、List

4.1 List的特点

1)List中元素是有序的(即添加顺序和取出顺序一致)并且是可重复的。

2)List中的每个元素都有其对应的顺序索引,支持索引获取元素。

public class List_ {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        List list = new ArrayList();
        //1、List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复[案例]
        list.add("Tom");
        list.add("Why");
        list.add("Jack");
        list.add("Mary");
        list.add("Tom");
        System.out.println("list="+list);
        //2 、List集合中的每个元素都有其对应的顺序索引,即支持索引,索引是从0开始的
        //3、List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
        //4、JDK API中List接口的实现类有很多,常用的类有ArrayList、linkedList、vector
        System.out.println(list.get(1));
    }
}
4.2 List的常用方法和遍历
4.2.1 常用方法

在这里插入图片描述

public class ListMethod {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("Tom");
        list.add("Why");
        list.add("Jack");
        list.add("Mary");
        list.add("Tom");
        System.out.println("list="+list);
        //list接口的常用方法,list集合里添加了一些根据索引来操作集合元素的方法
        // 1) void add(int index, Object ele):在index位置插入ele元素
        list.add(0,"cjj");
        System.out.println("list="+list);
        // 2)boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
        List list1 = new ArrayList();
        list1.add("zcw");
        list1.add(10);
        list.addAll(0,list1);
        System.out.println("list="+list);
        //3 Object get(int index):获取指定index位置的元素
        System.out.println(list.get(1));
        //int indexOf(Object obj):返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("Tom"));
        //5)int lastlndexOf(Object obj):返回obj在当前集合中末次出现的位置
        System.out.println(list.lastIndexOf("Tom"));
        //6)Object remove(int index):移除指定index位置的元素,并返回此元素
        Object o = list.remove(7);
        System.out.println("list="+list);
        //7)Object set(int index, Object ele):设置指定index位置的元素为ele相当于是替换.索引必须存在,不然会报索引越界错误
        list.set(0,"zy");
        System.out.println("list="+list);
        //8)List subList(int fromIndex, int tolndex):返回从fromlndex到tolndex位置的子集合
        //左闭右开
        System.out.println("list="+list.subList(0,1));
    }
}
4.2.2 遍历
public class ListFor {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
//        List list = new ArrayList();
        List list = new LinkedList();
//        List list = new Vector();
        list.add("Tom");
        list.add("Jane");
        list.add("Why");
        list.add("Jery");
        //迭代器循环
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println(obj);
        }

        //增强for循环
        for (Object o : list) {
            System.out.println("o=" + o);
        }

        //普通for循环 fori
        for (int i = 0; i < list.size(); i++) {
            System.out.println("obj=" + list.get(i));
        }

    }
}
4.3 ArrayList的底层结构(数组)

1)ArrayList中维护了一个Object类型的数组elementData(transient Object[] elementData)

2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加数据时扩容为10,如需再次扩容,则扩容为elementData容量的1.5倍

3)如果使用指定大小的构造器,则elementData容量为指定大小,如果需要扩容,则直接扩容为elementData容量的1.5倍

@SuppressWarnings({"all"})
public class ArrayListSource {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList(9);
        for (int i = 1; i <= 10 ; i++) {
            arrayList.add(i);
        }
        for (int i = 11; i <= 15; i++) {
            arrayList.add(i);
        }
        /*
        使用无参构造方法ArrayList(),初始化elementData。
        1. 执行ArrayList()构造方法,transient Object[] elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA(容量为0)。
        public ArrayList() {
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
        2. 执行add(E e),先确定是否需要扩容,扩容后再添加元素。
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
        3. 执行ensureCapacityInternal(int minCapacity),确定扩容后的容量,第一次扩容为10,第二次为15(10+10/2)。
        private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
        3.1 执行calculateCapacity(Object[] elementData, int minCapacity),确定elementData所需要的最小容量
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
            return minCapacity;
        }
        3.2 执行ensureExplicitCapacity(int minCapacity),如果elementData的容量不足minCapacity(最小容量),则扩容至minCapacity。
        private void ensureExplicitCapacity(int minCapacity) {
            modCount++; // 记录修改次数
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        3.3 执行grow(int minCapacity),真正的实现扩容。
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);//第二次及以后扩容为1.5倍(capacity+capacity右移一位)
            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);//扩容
        }

        使用有参构造方法ArrayList(int initialCapacity),初始化elementData。
        1. 执行ArrayList(int initialCapacity)构造方法,设置elementData为new Object[initialCapacity]。
         public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
        如果要扩容就按照参数的1.5倍扩容,(initialCapacity+initialCapacity右移一位)。
         */
        arrayList.add(100);
        arrayList.add(200);
        arrayList.add(null);
        for (Object o : arrayList) {
            System.out.println(o);
        }

    }
}
4.4 Vector的底层结构(数组)

1)Vector中维护了一个Object类型的数组elementData(protected Object[] elementData)

2)当创建Vector对象时,如果使用的是无参构造器,则初始elementData容量为10,如需扩容,则扩容为elementData容量的2倍

3)如果使用指定大小的构造器,则elementData容量为指定大小,如果需要扩容,则直接扩容为elementData容量的2倍

4)Vector是线程同步的,即线程安全,在开发中需要线程同步时,考虑使用Vector

@SuppressWarnings({"all"})
public class VectorSource {
    public static void main(String[] args) {
        Vector vector = new Vector(9);
        for (int i = 1; i <= 10 ; i++) {
            vector.add(i);
        }
        for (int i = 11; i <= 15; i++) {
            vector.add(i);
        }
        /*
        使用无参构造方法Vector(),创建和使用Vector
        1. 执行Vector()
        public Vector() {
            this(10);
        }
        1.1 执行Vector(int initialCapacity)
        public Vector(int initialCapacity) {
            this(initialCapacity, 0);
        }
        1.2 执行Vector(int initialCapacity, int capacityIncrement),将elementData初始化,容量为10
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }
        2. 执行add(E e),确定elementData的容量,并将e添加到elementData中
        public synchronized boolean add(E e) {
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }
        2.1 执行ensureCapacityHelper(int minCapacity),确定elementData的容量
        private void ensureCapacityHelper(int minCapacity) {
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        2.2 如果需要扩容则执行grow(int minCapacity),扩容elementData(原来容量的两倍),并将原有元素复制到新数组中
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                             capacityIncrement : oldCapacity);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        
        使用有参构造器Vector(int initialCapacity),创建和使用Vector
        1. 执行Vector(int initialCapacity)
        public Vector(int initialCapacity) {
            this(initialCapacity, 0);
        }
        1.1 执行Vector(int initialCapacity, int capacityIncrement),将elementData初始化,容量为initialCapacity
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
            this.capacityIncrement = capacityIncrement;
        }
        如果要扩容就按照参数的2倍扩容,(initialCapacity+initialCapacity)。
         */
        vector.add(100);
        vector.add(200);
        vector.add(null);
        for (Object o : vector) {
            System.out.println(o);
        }

    }
}
4.5 LinkedList的底层结构(双向链表)

1)LinkedList底层维护了一个双向链表

2)LinkedList中维护了两个属性first和last分别指向首节点和尾节点

3)每个节点(Node对象)里面又维护了prev、next、item三个属性,通过prev指向前一个节点,next指向后一个节点,这样来实现双向链表

4)LinkedList的删除和添加不是通过数组完成的,而是通过修改prev和next的指向完成,相对来说效率较高
双向链表

@SuppressWarnings("all")
public class LinkedListMethod {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        System.out.println("LinkedList:" +linkedList);
        /*
        1、执行,这是linkedList的属性 first = null, last = null, size = 0
        public LinkedList() {
            }
         2、执行 添加
         public boolean add(E e) {
                linkLast(e);
                return true;
            }
          3、执行 将新的节点添加到链表最后
          void linkLast(E e) {
                final Node<E> l = last;
                final Node<E> newNode = new Node<>(l, e, null);
                last = newNode;
                if (l == null)
                    first = newNode;
                else
                    l.next = newNode;
                size++;
                modCount++;
            }
         */

        //删除
        linkedList.remove();
        System.out.println("LinkedList:" + linkedList);
        /*
        1、执行 remove()默认调用removeFirst()方法
        public E remove() {
            return removeFirst();
        }
        2、执行 removeFirst()方法
        public E removeFirst() {
            final Node<E> f = first;
            if (f == null)
                throw new NoSuchElementException();
            return unlinkFirst(f);
        }
        3、执行 unlinkFirst(f) 方法
        private E unlinkFirst(Node<E> f) {
            // assert f == first && f != null;
            final E element = f.item;
            final Node<E> next = f.next;
            f.item = null;
            f.next = null; // help GC
            first = next;
            if (next == null)
                last = null;
            else
                next.prev = null;
            size--;
            modCount++;
            return element;
        }
         */
        //修改
        linkedList.set(0,4);
        System.out.println("LinkedList:" + linkedList);
        //查看
        System.out.println(linkedList.get(0));
        //遍历
        System.out.println("=======迭代器遍历========");
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
        System.out.println("=======增强for循环遍历========");
        for (Object o : linkedList) {
            System.out.println(o);
        }
        System.out.println("=======普通for循环遍历========");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }
    }
}
4.6 ArrayList、Vector和LinkedList比较

1)如果改查的操作多,选择ArrayList

2)如果增删的操作多,选择LinkedList

3)如果有线程安全要求,选择Vector

4)一般来说在程序中80%-90%都是查询,大部分情况下会选择ArrayList

5)在一个项目中也可以根据业务灵活选择,一个模块使用ArrayList,另一个模块使用LinkedList等

List接口的集合实现类比较

5、Set

5.1 Set的特点

1)无序(添加和取出的顺序不一致),没有索引

2)不允许重复元素,所以最多包含一个null

@SuppressWarnings("all")
public class Set_ {
    public static void main(String[] args) {
        Set set = new HashSet();
        //1)无序(添加和取出的顺序不一致),没有索引
        set.add("apple");
        set.add("banana");
        set.add("orange");
        set.add("tom");
        System.out.println("set: " + set);
        //2)不允许重复元素,所以最多包含一个null
        set.add(null);
        set.add(null);
        set.add("apple");
        System.out.println("set: " + set);
    }
}
5.2 Set的常用方法和遍历
5.2.1 常用方法

在这里插入图片描述

5.2.2 遍历
public class SetFor {
    public static void main(String[] args) {
        Set set = new HashSet();
        set.add("apple");
        set.add("banana");
        set.add(null);
        System.out.println(set);
        System.out.println("====迭代器===");
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println(obj);
        }
        System.out.println("====增强for循环===");
        for (Object o : set) {
            System.out.println(o);
        }

    }
}
5.3 HashSet的特点

1)HashSet实际上是HashMap

2)可以存放null值,但只能有一个null

3)HashSet不保证元素是有序的,取决于hashCode,再确定索引的结果(即不保证存放元素的顺序和取出顺序一致)

4)不能有重复的元素

@SuppressWarnings("all")
public class HashSet_ {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        System.out.println(hashSet.add("jack"));//T
        System.out.println(hashSet.add("Tom"));//T
        System.out.println(hashSet.add("Mary"));//T
        System.out.println(hashSet.add("Jerry"));//T
        //不能有重复的元素
        System.out.println(hashSet.add("jack"));//F
        hashSet.remove("jack");
        System.out.println(hashSet);
        hashSet.add("Joe");
        hashSet.add("Ross");
        hashSet.add("Joe");
        hashSet.add(new Dog("Joe"));
        hashSet.add(new Dog("Joe"));
        System.out.println(hashSet);
    }
}
class Dog{
    private String name;

    public Dog(String name) {
        this.name = name;
    }
    public Dog(){
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}
5.4 HashSet的底层结构(底层是HashMap(数组+链表+红黑树))

1)HashSet底层是HashMap

2)添加一个元素时先得到hash值,将hash值作为索引值

3)找到存储数据表table,看这个索引位置是否已经存放元素

4)如果没有直接加入

5)如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后

6)当table的一条链表对应的节点到达了8个并且table的长度达到64就会转换为红黑树

@SuppressWarnings("all")
public class HashSetResource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("apple");
        hashSet.add("banana");
        hashSet.add("orange");
        hashSet.add("java");
        System.out.println(hashSet);
        /*
        1. 执行HashSet(),实际上是执行HashMap(),创建一个HashMap对象。
        public HashSet() {
            map = new HashMap<>();
        }
        1.1 执行HashMap(),设置加载因子为0.75(加载因子的作用是当HashMap中元素的个数大于等于阈值(加载因子乘以HashMap的容量)时,HashMap会自动扩容)。
        public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
        }
        2. 执行add(E e),将元素e添加到HashMap中。
        public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
        2.1 执行put() 计算key的hash值,然后将key-value对存放到数组的对应位置
         public V put(K key, V value) {
             return putVal(hash(key), key, value, false, true);
         }
         2.1.1 执行 hash(key) 计算key的hash值,采用jdk1.8的hash算法,将key的hash值与该key的hash值无符号右移16位做异或操作,得到真正的hash值
         public static int hash(Object key) {
             int h;
             return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
         }
         2.1.2 执行 putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 存放key-value对到数组或链表中
         final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量,n为数组长度,i为数组下标,tab为数组,node为存放数据的节点
            if ((tab = table) == null || (n = tab.length) == 0)// 数组为空,进行初始化
                n = (tab = resize()).length;// 扩容,第一次扩容后,数组长度为默认值16
            if ((p = tab[i = (n - 1) & hash]) == null)// 根据key的hash值得到要存放数据的位置,如果该位置为空,则直接存放
                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))))
                    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);
                            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);// 节点插入后,调用afterNodeInsertion方法,进行一些后续操作,比如扩容,红黑树转换等
            return null;
        }
         */
    }
}
5.5 HashSet小练习

1)定义一个Employee类,该类包含:private成员属性name,age 要求:创建3个Employee 放入 Hashset中;当 name和age的值相同时,认为是相同员工,不能添加到HashSet集合中

public class HashSetTest {
    public static void main(String[] args) {
        // 定义一个Employee类,该类包含:private成员属性name,age 要求:
        //1.创建3个Employee 放入 Hashset中
        //2.当 name和age的值相同时,认为是相同员工,不能添加到HashSet集合中
        HashSet<Employee> employees = new HashSet<>();
        employees.add(new Employee("Tmo",25));
        employees.add(new Employee("Jerry",22));
        employees.add(new Employee("Joe",24));
        employees.add(new Employee("Joe",24));
        System.out.println(employees);

    }
}
class Employee {
    private String name;
    private  int age;

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

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age && Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, 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 "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

2)定义一个Employee类,该类包含:private成员属性name,sal,birthday(MyDate类型),其中 birthday 为 MyDate类型(属性包括:year, month, day),要求:创建3个Employee 放入 HashSet中;当 name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中

public class HashSetTest1 {
    public static void main(String[] args) {
//        定义一个Employee类,该类包含:private成员属性name,sal,birthday(MyDate类
//        型),其中 birthday 为 MyDate类型(属性包括:year, month, day),要求:
//        1.创建3个Employee 放入 HashSet中
//        2.当 name和birthday的值相同时,认为是相同员工,不能添加到HashSet集合中
        HashSet<Employee1> employee1s = new HashSet<>();
        employee1s.add(new Employee1("Tom",5000,new MyData(2000,1,1)));
        employee1s.add(new Employee1("Tom",5000,new MyData(2000,1,1)));
        employee1s.add(new Employee1("Jerry",5000,new MyData(2000,1,1)));
        System.out.println(employee1s);
    }
}
class Employee1 {
    private String name;
    private double sal;
    private MyData data;

    public Employee1(String name, double sal, MyData data) {
        this.name = name;
        this.sal = sal;
        this.data = data;
    }

    public Employee1() {
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    public MyData getData() {
        return data;
    }

    public void setData(MyData data) {
        this.data = data;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee1 employee1 = (Employee1) o;
        return Objects.equals(name, employee1.name) && Objects.equals(data, employee1.data);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, data);
    }

    @Override
    public String toString() {
        return "Employee1{" +
                "name='" + name + '\'' +
                ", sal=" + sal +
                ", data=" + data +
                '}';
    }
}
class MyData {
    private int year;
    private int month;
    private int day;

    public MyData(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public MyData() {
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyData myData = (MyData) o;
        return year == myData.year && month == myData.month && day == myData.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }

    @Override
    public String toString() {
        return "MyData{" +
                "year=" + year +
                ", month=" + month +
                ", day=" + day +
                '}';
    }
}
5.6 LinkedHashSet(底层是LinkedHashMap,为数组+双向链表)

1)LinkedHashSet中维护了一个hash表和双向链表(有一个head和tail)

2)每一个节点有before和after属性,这样可以形成双向链表(可以保证遍历的时候读取顺序和插入顺序一致)

3)在添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入双向链表(如果已经存在则不添加)

在这里插入图片描述

5.7 TreeSet(底层是TreeMap(红黑树))

1)不能插入null的值

2)元素不能重复

3)元素按照自然顺序或者根据提供的Comparator进行排序

4)自定义类要实现Comparable接口才能进行排序

@SuppressWarnings("all")
public class TreeSet_ {
    public static void main(String[] args) {
        // 自然排序
        // TreeSet字符串按照长度排序,字符串长度相同按照字母顺序排序,字符串长度不同按照字符串顺序排序,null值排在最后
        // int类型默认升序排序
        TreeSet treeSet = new TreeSet();
        //Comparator排序 自定义排序 按照字符串长度排序
        TreeSet treeSet1 = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return o1.toString().length() - o2.toString().length();
            }
        });
        /*
        TreeSet底层是有一个TreeMap,TreeSet最大的特点就是提供了排序,可以按照自然排序或者自定义排序,自定义排序的话
        可以用Comparator或者Comparable接口来实现自定义排序
        它实现排序的原理就是当加入一个新元素时,会先获取传过去的自定义的比较器,再按照比较器里面比较规则进行排序,是通过一个
        循环来完成的,当比较的结果为0时,就认为它们是重复的,TreeSet不会将第二个元素加入到集合中(因为treeSet是传入的key),而		TreeMap则会直接将该位置对应的value改变,就相当于替换。
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        */
        //Comparable排序 自定义排序 自定义类要实现Comparable接口再重写compareTo方法才能存储到TreeSet中
        //如果两个元素通过比较器比较得出相等(返回 0),TreeSet 就认为它们是重复的,不会将第二个元素加入到集合中
        TreeSet treeSet2 = new TreeSet();
        treeSet2.add(new Person("Tom", 25));
        treeSet2.add(new Person("Jerry", 30));
        treeSet2.add(new Person("Mike", 20));
        treeSet2.add(new Person("Lily", 25));

        //自然排序 整型默认升序排序
        treeSet.add(1);
        treeSet.add(10);
        treeSet.add(3);
        treeSet.add(5);
        treeSet.add(6);
        treeSet.add(20);

        treeSet1.add("abc");
        treeSet1.add("d");
        treeSet1.add("hi");
        treeSet1.add("cdef");
        //treeSet.add(null);// NullPointerException
        System.out.println(treeSet);
        System.out.println(treeSet1);
        System.out.println(treeSet2);
    }
}
class Person implements Comparable<Person> {
    private String name;
    private int age;

    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 int compareTo(Person o) {
        return this.age - o.age;
    }

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

6、Map

6.1 Map的特点

1)Map和Collection并列共存用于保存具有映射关系的数据:key-value

2)Map中的key不允许重复

3)Map中的value可以重复

4)Map中的key可以为null,value也可以为null,但key只能有一个为null,value可以有多个为null

5)常用String类作为Map的key

6)key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value

public class Map_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1","why");
        map.put("no1","zcw");//等价替换
        map.put("no2","joe");
        map.put("no3","joe");
        map.put(null,null);
        map.put("no4",null);
        System.out.println(map);
        //通过get获取key再返回对应的value
        System.out.println(map.get("no4"));
    }
}
6.2 Map的常用方法和遍历
6.2.1 常用方法

在这里插入图片描述

@SuppressWarnings("all")
public class MapMethod {
    public static void main(String[] args) {
        Map map = new HashMap();
        //adding key-value pairs to the map
        map.put("no1","Why");
        map.put("no1","Joe");//this will replace the previous value
        map.put("no3","Roos");//this will add a new key-value pair
        map.put("no4","Tom");//this will add a new key-value pair/
        map.put(null,"why");//this will not add a new key-value pair as the key is null
        map.put("no5",null);//this will not add a new key-value pair as the value is null
        System.out.println(map);
        //根据key删除映射关系
        map.remove(null);//removing a key-value pair from the map
        System.out.println(map);

        Object val = map.get("no1");//getting the value of a key
        System.out.println(val);
        //获取集合元素个数
        System.out.println(map.size());//4
        //判断集合是否为空
        System.out.println(map.isEmpty());//F
        //判断集合是否包含指定的key
        System.out.println(map.containsKey("no1"));//T
        //判断集合是否包含指定的value
        System.out.println(map.containsValue("Joe"));//T
        //清空集合
        map.clear();
        System.out.println(map);
    }
}
6.2.2 遍历
@SuppressWarnings("all")
public class MapFor {
    public static void main(String[] args) {
        //创建Map对象
        Map map = new HashMap();
        map.put("no1","Java");
        map.put("no2","Python");
        map.put("no3","C++");
        map.put("no4","Ruby");
        map.put("no5","PHP");
        //添加键值对
        //方式一:keySet()方法,获取所有的key
        Set keySet = map.keySet();
        //(1) 增强for循环
        System.out.println("增强for循环:");
        for(Object key : keySet){
            System.out.println(key + ":" + map.get(key));
        }
        //(2) 迭代器
        System.out.println("迭代器:");
        Iterator iterator = keySet.iterator();
        while (iterator.hasNext()) {
            Object obj =  iterator.next();
            System.out.println(obj + ":" + map.get(obj));
        }
        //方式二:values()方法,取出所有的value
        Collection collection = map.values();
        //(1) 增强for循环
        System.out.println("========values()增强for循环:");
        for(Object value : collection){
            System.out.println(value);
        }
        //(2) 迭代器
        System.out.println("=======values()迭代器:");
        Iterator iterator2 = collection.iterator();
        while (iterator2.hasNext()) {
            Object obj =  iterator2.next();
            System.out.println(obj);
        }
        //方式三:entrySet()方法:获取所有k-v
        Set entrySet = map.entrySet();
        //(1) 增强for循环
        System.out.println("=======entrySet()增强for循环:");
        for(Object entry : entrySet){
            Map.Entry me = (Map.Entry)entry;
            System.out.println(me.getKey() + ":" + me.getValue());
        }
        //(2) 迭代器
        System.out.println("=======entrySet()迭代器:");
        Iterator iterator3 = entrySet.iterator();
        while (iterator3.hasNext()) {
            //iterator3.next() 返回的是HashMap$Node对象, HashMap$Node->实现了Map.Entry接口
            // 所以可以强转,再使用getKey()和getValue()方法
            Map.Entry me = (Map.Entry)iterator3.next();
            System.out.println(me.getKey() + ":" + me.getValue());
        }
    }
}
6.4 Map的小练习

​ 使用HashMap添加3个员工对象,要求:键:员工id,值:员工对象,并遍历显示工资>18000的员工(遍历方式至少两种);员工类:姓名、工资、员工id

public class MapTest {
    public static void main(String[] args) {
        //使用HashMap添加3个员工对象,要求
        //键:员工id
        //值:员工对象
        //并遍历显示工资>18000的员工(遍历方式最少两种)
        //员工类:姓名、工资、员工id
        Map map = new HashMap();
        map.put(1,new Employee("Tom", 18500, 1));
        map.put(2,new Employee("Jerry", 20000, 2));
        map.put(3,new Employee("Mike", 17000, 3));
        Set keySet = map.keySet();
        System.out.println("keySet: 增强for循环");
        for (Object o : keySet) {
            Employee employee = (Employee) map.get(o);
//            System.out.println(o + ":" + map.get(o));
            if (employee.getSalary() > 18000){
                System.out.println(employee.toString());
            }
        }

        Set entrySet = map.entrySet();
        System.out.println("entrySet: 增强for循环");
        for (Object o : entrySet) {
            Map.Entry entry = (Map.Entry) o;
//            System.out.println(entry.getKey() + ":" + entry.getValue());
            Employee employee = (Employee) entry.getValue();
            if (employee.getSalary() > 18000){
                System.out.println(employee.toString());
            }
        }


    }
}
class Employee {
    private String name;
    private double salary;
    private int id;

    public Employee(String name, double salary, int id) {
        this.name = name;
        this.salary = salary;
        this.id = id;
    }

    public Employee() {
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", id=" + id +
                '}';
    }
}
6.5 HashMap的特点

1)HashMap是以k-v的方式来存放元素

2)key不能重复,但是value可以重复,允许使用null键和null值

3)如果添加相同的key,则会覆盖原来的k-v,等同于修改(k不会被替换,v会被替换)

4)不保证k-v存储是有序的(存储位置取决于hashCode)

5)没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized

6.6 HashMap的底层结构(数组+链表+红黑树)

1)HashMap底层维护了Node类型的数组table,默认为null
2)当创建对象时,将加载因子(loadfactor)初始化为0.75
3)当添加key-val时,通过key的哈希值得到在tablel的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key
是否相等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相
应处理。如果添加时发现容量不够,则需要扩容。
4)第1次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
5)以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即24,依次类推
6)在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且
tablel的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

@SuppressWarnings("all")
public class HashMapResource {
    public static void main(String[] args) {
        HashMap hashMap = new HashMap();
        hashMap.put("java", 10);
        hashMap.put("c++", 15);
        hashMap.put("python", 20);
        hashMap.put("javascript", 25);
        // 底层机制:数组+链表+红黑树
        // 数组:存放key-value对,通过hash算法计算key的hash值,然后将key-value对存放到数组的对应位置
        // 链表:当hash冲突时,将key-value对存放到链表中
        // 红黑树:当链表长度大于8时并且数组长度大于64时,链表转换为红黑树,提高查询效率
        // 扩容:当存放进hash表的数据到达临界值时,进行扩容,扩容后重新计算hash值,将key-value对存放到新的数组中
        // 底层源码分析
        /*
        1. 执行HashMap() 将加载因子设置为默认值0.75(用hash表的长度*加载因子得到一个临界值,当hash表的元素个数超过临界值时,进行扩容)
         public HashMap() {
             this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
         }
         2. 执行put() 计算key的hash值,然后将key-value对存放到数组的对应位置
         public V put(K key, V value) {
             return putVal(hash(key), key, value, false, true);
         }
         2.1 执行 hash(key) 计算key的hash值,采用jdk1.8的hash算法,将key的hash值与该key的hash值无符号右移16位做异或操作,得到真正的hash值
         public static int hash(Object key) {
             int h;
             return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
         }
         2.2 执行 putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 存放key-value对到数组或链表中
         final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量,n为数组长度,i为数组下标,tab为数组,node为存放数据的节点
            if ((tab = table) == null || (n = tab.length) == 0)// 数组为空,进行初始化
                n = (tab = resize()).length;// 扩容,第一次扩容后,数组长度为默认值16
            if ((p = tab[i = (n - 1) & hash]) == null)// 根据key的hash值得到要存放数据的位置,如果该位置为空,则直接存放
                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))))
                    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);
                            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);// 节点插入后,调用afterNodeInsertion方法,进行一些后续操作,比如扩容,红黑树转换等
            return null;
        }
         */

        for (int i = 0; i <= 12 ; i++) {
            hashMap.put(new Book(i), "book");
        }
        System.out.println(hashMap);
    }
}
class Book {
    private int id;

    public Book(int id) {
        this.id = id;
    }

    public Book() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return 100;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                '}';
    }
}
6.7 Hashtable的特点(数组+链表)

1)存放的元素是k-v

2)key和value都不能为null,否则会抛出NullPointerException

3)是线程安全的(方法前有synchronized)

@SuppressWarnings("all")
public class HashTable_ {
    public static void main(String[] args) {
        Hashtable hashtable = new Hashtable();
        hashtable.put("apple", 1);
        hashtable.put("banana", 2);
        hashtable.put("orange", 3);
        hashtable.put(null, 4);//NullPointerException
        hashtable.put("apples", null);//NullPointerException
        System.out.println(hashtable);
    }
}
6.8 Properties的特点和常用方法

1)Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据
2)它的使用特点和Hashtable类似
3)Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改

public class Properties_ {
    public static void main(String[] args) {
        //properties的特点;
        //Properties继承自Hashtable,数据以k-v形式存储
        Properties properties = new Properties();
        properties.put("name", "John");
        properties.put("age", "30");
        properties.put("city", "New York");
        // properties.put(null,"null value");key为null会抛出NullPointerException
        // properties.put("data",null); value为空会抛出NullPointerException
        properties.put("city", "Beijing");//如果key相同则value会被替换
        System.out.println(properties);
        //通过key获取value
        System.out.println(properties.get("name"));
        //properties的方法
        //删除
        properties.remove("name");
        System.out.println(properties);
        //修改
        properties.setProperty("name", "Tom");
        System.out.println(properties);
        //查找
        System.out.println(properties.get("name"));

    }
}
6.9 TreeMap(红黑树)

1)TreeMap是用键进行排序的,默认采用升序排序;通过Comparable或Comparator来排序;
2)允许值重复,不允许键重复;
3)键不可以为null,值可以为null;

@SuppressWarnings("all")
public class TreeMap_ {
    public static void main(String[] args) {
        //自然排序,按照key排序,int类型默认升序,字符串类型按照字符串长度升序
        TreeMap treeMap = new TreeMap();
        //key不能重复,value可以重复,key重复时,后面的会覆盖前面的
        treeMap.put(1, "a");
        treeMap.put(3, "c");
        treeMap.put(2, "b");
        treeMap.put(6, "a");
        // treeMap.put(null, "d");//NullPointerException
        //value可以为null
        treeMap.put(4, null);
        System.out.println(treeMap);
        //自然排序,按照key排序,字符串类型按照字符串长度升序
        TreeMap treeMap1 = new TreeMap();
        treeMap1.put("abc", "a");
        treeMap1.put("abcd", "b");
        treeMap1.put("ab", "c");
        System.out.println(treeMap1);

        //Comparator自定义排序
        TreeMap treeMap2 = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o2).length() -((String)o1).length();
            }
        });
        treeMap2.put("abcde", "a");
        treeMap2.put("abcd", "b");
        treeMap2.put("abc", "c");
        System.out.println(treeMap2);

        //Comparable排序 实现Comparable接口,重写compareTo方法,按照age排序
        TreeMap treeMap3 = new TreeMap();
        treeMap3.put(new Student(1,"a",20),"a");
        treeMap3.put(new Student(3,"b",25),"b");
        treeMap3.put(new Student(2,"c",18),"c");
        System.out.println(treeMap3);
    }
}
class Student implements Comparable<Student> {
    private int id;
    private String name;
    private int age;

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

    public Student() {
    }

    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;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.id - o.id;
    }
}

7、泛型

1)类型参数:泛型允许在类、接口和方法中使用类型参数。常见的类型参数有 T(Type)、E(Element)、K(Key)、V(Value)等

2)类型安全:泛型在编译时进行类型检查,避免了运行时的 ClassCastException

3)代码重用:通过泛型,可以编写与类型无关的代码,增强代码的重用性

泛型可分为泛型类、泛型接口、泛型方法

7.1 泛型类
public class Box<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

7.2 泛型接口
public interface Container<T> {
    void add(T item);
    T get(int index);
}

7.3 泛型方法
public class Util {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }
}

7.4 通配符(用于表示不确定的类型)

无界通配符:<?> 表示任意类型

public void printList(List<?> list) {
    for (Object elem : list) {
        System.out.println(elem);
    }
}

上界通配符:<? extends T> 表示类型是 TT 的子类

public void process(List<? extends Number> list) {
    // 可以处理 Number 或其子类
}

下界通配符:<? super T> 表示类型是 TT 的父类

public void addNumbers(List<? super Integer> list) {
    // 可以添加 Integer 或其子类
}

最后示例(其实泛型在集合中就是起一个定义参数类型的作用)

public class Test {
    public static void main(String[] args) {
        ArrayList<Teacher> arrayList = new ArrayList<>();
        arrayList.add(new Teacher("John", 25));
        arrayList.add(new Teacher("Mary", 30));
        arrayList.add(new Teacher("Tom", 35));
        System.out.println(arrayList);
    }
}
class Teacher {
    private String name;
    private int age;

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

    public Teacher() {
    }

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

8、总结-开发中如何选择集合实现类

1)先判断存储的类型(一组对象[单列]或一组键值对[双列])

2)一组对象[单列]:collection接口

2.1)允许重复:List

​ 增删多:LinkedList(底层维护了一个双向链表)

​ 改查多:ArrayList(底层维护了一个数组)

2.2)不允许重复:Set

​ 无序:HashSet

​ 排序:TreeSet

​ 插入和取出顺序一致:LinkedHashSet(数组+双向链表)

3)一组键值对[双列]:Map

​ 键无序:HashMap

​ 键排序:TreeMap

​ 键插入和取出顺序一致:LinkedHashMap

​ 读取xxx.properties文件:Properties

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值