• 15.collection


    数据结构

    常见数据结构:栈

    数据进入栈的过程称为: 压/进栈

    数据离开栈的过程称为: 弹/出栈

    栈底和栈顶元素, 栈是一种先进后出的数据结构image-20201015115855692

    常见数据结构:队列

    数据从后端进入队列过程称为: 入队列

    数据从前端离开队列过程称为: 出队列

    队列是一种先进先出的模型

    image-20201015115930362

    常见数据结构: 数组

    image-20201015120054990

    查询数据通过索引定位, 查询任意数据耗时相同, 插叙效率高

    删除数据时, 要将原始数据删除, 同时后面每个数据迁移, 删除效率低

    添加元素时, 添加位置后的每个数据后移, 在添加元素, 添加效率极低.

    数组是一种查询快, 增删慢的模型

    常见数据结构: 链表

    image-20201015120344868

    链表存储过程

    image-20201015120447523

    添加元素过程

    image-20201015120543688

    删除元素过程

    image-20201015120617399

    查询过程

    无论是查询哪一个数据,都需要从head开始遍历

    链表是一种增删快,查询慢的数据结构

    常见数据结构: 哈希表

    哈希表

    • JDK8之后, 底层采用数组+链表实现, 可以说是一个元素为链表的数组
    • JDK8之后, 长度比较长的时候, 底层实现了优化

    image-20201015171737064

    collection

    集合类体系结构

    特点: 提供一种存储空间可变的存储类型, 存储的数据内容可以随时发生改变

    集合:

    • Collection: 单列集合*
      • List: 可重复*
        • ArrayList
        • LinkedList
      • Set: 不可重复*
        • HashSet
        • TreeSet
    • Map: 双列集合*
      • HashMap

    注:其中带*的是接口, 不带的是实现类

    概述

    collection集合

    • 单列集合的顶层接口, 表示一组对象, 这些对象也称为Collection的元素
    • JDK不提供此接口的任何直接实现类, 他提供更具体的子接口(如Set和List)实现

    创建Collection集合的对象

    • 多态的方式
    • 具体的实现类ArrayList

    常用方法

    方法名 说明
    Boolean add(E e) 添加元素
    boolean remove(Object) 从集合中移除指定的元素
    void clear() 清空集合中的元素
    Boolean contains(Object o) 判断集合中是否存在指定的元素
    boolean isEmpty() 判断集合是否为空
    int size() 集合的长度, 也就是集合中元素的个数

    demo

    package commonApi.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    /*
        Alt+7打开窗口查看类的方法
     */
    
    public class CollectionDemo2 {
        public static void main(String[] args) {
            // 创建对象
            Collection<String> c = new ArrayList<String>();
    
            // boolean add(E e): 添加元素
            System.out.println(c.add("hello"));
            System.out.println(c.add("world"));
            System.out.println(c.add("world"));
    
            // boolean remove(E e): 移除元素
            // System.out.println(c.remove("java"));
            // System.out.println(c.remove("hello"));
            
            // void clear(): 清空集合元素
            // c.clear();
            
            // boolean contains(Object o): 判断集合中是否存在指定元素
            System.out.println(c.contains("java"));
            System.out.println(c.contains("javaee"));
            
            // boolean isEmpty(): 判断集合是否为空
            System.out.println(c.isEmpty());
            
            // int size(): 集合的长度
            System.out.println(c.size());
    
            System.out.println(c);
        }
    }
    
    

    集合的遍历

    Iterator: 迭代器, 集合的专用遍历方式

    • Iterator<E> iterator(): 返回此集合中元素的迭代器, 通过集合的iterator()方法得到
    • 迭代器是通过集合的iterator()方法得到的, 所以我们说它是依赖于集合而存在的

    Iterator中的常用方法:

    • E next(): 返回迭代器中的下一个元素
    • boolean hashNext(): 如果迭代器具有更多元素, 则返会true

    demo

    package commonApi.collection;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class IteratorDemo {
        public static void main(String[] args) {
            // 创建集合对象
            Collection<String> c = new ArrayList<String>();
    
            // 添加元素
            c.add("hello");
            c.add("world");
            c.add("java");
    
            // Iterator<E> iterator(): 返回集合元素中的迭代器
            Iterator<String> it = c.iterator();
            /*
                public Iterator<E> iterator() {
                    return new Itr();
                }
    
                private class Itr implements Iterator<E> {
                    ...
                }
             */
            // E next(): 返回迭代中的下一元素
            /*
            System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());
            System.out.println(it.next());  // NoSuchElementException
            System.out.println(it.next());
    
             */
    
            // boolean hasNext(); 如果迭代中有元素, 返回true
            /*
            if (it.hasNext()) {
                System.out.println(it.next());
            }
            if (it.hasNext()) {
                System.out.println(it.next());
            }
            if (it.hasNext()) {
                System.out.println(it.next());
            }
            if (it.hasNext()) {
                System.out.println(it.next());
            }
    
             */
    
            // 用while循环改进判断
            while (it.hasNext()) {
                String s = it.next();
                System.out.println(s);
            }
        }
    }
    
    

    List

    list概述

    有序集合(也称为序列), 用户可以精确控制列表汇总每个元素的插入位置. 用户可以通过整数索引访问元素, 并搜索列表中的元素.

    与set集合不同, 列表通常允许重复的元素

    ListDemo

    package commonApi.list;
    
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class ListDemo {
        public static void main(String[] args) {
            // 创建集合对象
            List<String> list = new ArrayList<String>();
    
            // 添加元素
            list.add("hello");
            list.add("world");
            list.add("java");
            list.add("hello");
    
            // 输出集合对象
            System.out.println(list);
    
            // 迭代器方式遍历
            Iterator<String> it = list.iterator();
            while (it.hasNext()) {
                String s = it.next();
                System.out.println(it.next());
            }
    
        }
    }
    
    

    常用方法

    方法名 说明
    void add(int index, E element) 在此集合指定位置插入指定元素
    E remove(int index) 删除指定索引出的元素, 返回被删除的元素
    E set(int index, E element) 修改指定索引出的元素, 返回被修改的元素
    E get(int index) 返回指定所引处的元素

    ListDemo

    package commonApi.list;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ListDemo2 {
        public static void main(String[] args) {
            // 创建集合对象
            List<String> list = new ArrayList<String>();
    
            // 添加元素
            list.add("hello");
            list.add("world");
            list.add("java");
            // void add(int index, E element)
            list.add(1,"javaee");
            // list.add(11,"javaee");  // IndexOutOfBoundsException
    
            // E remove(int index)
            list.remove(1);
            // list.remove(11);  // IndexOutOfBoundsException
    
            // E set(int index, E element)
            System.out.println(list.set(1, "python"));
            // System.out.println(list.set(11, "python"));  // IndexOutOfBoundsException
    
            // E get(int index)
            System.out.println(list.get(1));
            // System.out.println(list.get(11));  // IndexOutOfBoundsException
    
            // 输出集合对象
            System.out.println(list);
    
            // 遍历集合
            for (int i=0; i<list.size(); i++) {
                System.out.println(list.get(i));
            }
        }
    }
    
    

    List并发修改

    并发修改异常: ConcurrentModificationException

    原因: 迭代器遍历过程中, 通过集合对象修改了集合的长度, 造成了迭代器获取元素中判断预期修改值和实际修改值不一致.

    解决: 用for循环, 然后用集合对象做对应的操作即可

    源码分析

    public interface List<E> {
     Iterator<E> iterator();
     boolean add(E e);
    }
    public abstract class AbstractList<E> {
     protected int modCount = 0;
    }
    
    public class ArrayList<E> extends AbstractList<E> implements List<E>{
     public Iterator<E> iterator() {
         return new Itr();
     }
     private class Itr implements Iterator<E> {
         int expectedModCount = modCount;
         /*
             modCount: 实际修改集合的次数
             expectModCount: 预期修改集合的次数
         */
    
         public boolean add(E e) {
             modCount++;
             add(e, elementData, size);
             return true;
         }
    
         public E next() {
             checkForComodification();
             int i = cursor;
             if (i >= size)
                 throw new NoSuchElementException();
             Object[] elementData = ArrayList.this.elementData;
             if (i >= elementData.length)
                 throw new ConcurrentModificationException();
             cursor = i + 1;
             return (E) elementData[lastRet = i];
         }
    
         final void checkForComodification() {
             if (modCount != expectedModCount)
                 throw new ConcurrentModificationException();
         }
     }
    }
    

    示例

    package commonApi.list;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    /*
        ConcurrentModificationException: 当不允许这样的修改时, 可以通过检车到对象的并发修改的方法来抛出此异常
     */
    
    public class ConcurrenceModify {
        public static void main(String[] args) {
            // 创建list集合
            List<String> list = new ArrayList<String>();
    
            // 添加元素
            list.add("hello");
            list.add("world");
            list.add("java");
    
            // 遍历集合, 得到每个元素, 如果有"world"元素, 添加一个"javaee"
            Iterator<String> it = list.iterator();
            /*
            while (it.hasNext()) {
                String s = it.next();  // ConcurrentModificationException
                if (s.equals("world")) {
                    list.add("javaee");
                    // 迭代器遍历时, 迭代器元素个数发生了改变, 类似python中遍历时修改列表大小,也会报错
                }
            }
             */
    
            for (int i=0; i<list.size(); i++) {
                String s = list.get(i);
                if (s.equals("world")) {
                    list.add("javaee");
                }
            }
            System.out.println(list);
        }
    }
    
    

    ListIterator

    列表迭代器

    • 通过List集合的iterator()方法得到, 所以说它是List集合特有的迭代器

    • 用于允许沿任意方向遍历列表的列表迭代器, 在迭代期间修改列表, 并获取列表中迭代器的当前位置

    ListIterator中的常用方法

    方法名 说明
    E next(): 返回迭代器中下一个元素
    boolean hasNext() 如果迭代器中具有更多元素, 则返回true
    E previous() 返回列表中的上一个元素
    boolean hasPrevious() 如果此列表迭代器在相反方向遍历列表时具有更多元素, 则返回true
    void add(E e) 将指定元素插入列表

    demo

    package commonApi.list;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.ListIterator;
    
    public class ListIteratorDemo {
        public static void main(String[] args) {
            // 创建集合对象
            List<String> list = new ArrayList<String>();
    
            // 添加元素
            list.add("hello");
            list.add("world");
            list.add("java");
    
            // 通过list集合的listIterator()方法得到
            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);
            }
    
            // 获取列表迭代器
            while(lit.hasNext()) {
                String s = lit.next();
                if (s.equals("world")) {
                    lit.add("javaee");
                }
            }
    
            System.out.println(list);
        }
    }
    
    

    ListIterator不会并发异常源码分析

    public interface List<E> {
     Iterator<E> iterator();
     ListIterator<E> listIterator();
    }
    public abstract class AbstractList<E> {
     protected int modCount = 0;
    }
    
    public class ArrayList<E> extends AbstractList<E> implements List<E> {
    
     public Iterator<E> iterator() {
         return new Itr();
     }
     private class Itr implements Iterator<E> {
         ...
     }
     }
     public ListIterator<E> listIterator() {
         return new ListItr(0);
     }
    
     private class ListItr extends Itr implements ListIterator<E> {
         public void add(E e) {
             checkForComodification();
    
             try {
                 int i = cursor;
                 ArrayList.this.add(i, e);
                 cursor = i + 1;
                 lastRet = -1;
                 expectedModCount = modCount;  // 修改后会把实际修改值赋值给预期修改值, 所以不会触发异常
             } catch (IndexOutOfBoundsException ex) {
                 throw new ConcurrentModificationException();
             }
         }
     }
    }
    

    增强for循环

    增强for: 简化数组和Collection集合的遍历

    • 实现Iterable接口的类允许其对象成为增强型for语句的目标
    • JDK5后出现, 内部原理是一个Iterator迭代器

    格式:

    for(元素数据类型 变量名: 数组或者Collection集合) {
    	// 在此使用变量即可, 该变量就是元素
    }
    

    范例

    int [] arr = {1, 2, 3};
    for(int i: arr) {
    	System.out.println(i);
    }
    

    Demo

    package commonApi.list;
    
    import modifier.pkgs.Parent;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class EnforceForDemo {
        public static void main(String[] args) {
            int [] arr = {1, 2, 3, 4, 5};
            for (int i: arr) {
                System.out.println(i);
            }
    
            System.out.println("--------");
    
            String [] strArray = {"hello", "world", "java"};
            for (String s: strArray) {
                System.out.println(s);
            }
            System.out.println("--------");
    
            List<String> list = new ArrayList<String>();
            list.add("hello");
            list.add("world");
            list.add("java");
    
            for (String s: list) {
                System.out.println(s);
            }
            
            // 内部是一个迭代器
            for(String s: list) {
                if (s.equals("world")) {
                    list.add("javaee");  // ConcurrentModificationException, 说明内部原理还是一个迭代器
                }
            }
        }
    }
    

    List集合子类特点

    常用List子类: ArrayList, LinkedList

    ArrayList: 底层数据结构是数组, 查询快, 增删慢

    LinkedList: 底层数据结构是链表, 增删快, 查询慢

    Demo

    package commonApi.list;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    
    public class LinkedAndArrayList {
        public static void main(String[] args) {
            // 创建
            ArrayList<String> array = new ArrayList<String>();
    
            array.add("hello");
            array.add("world");
            array.add("java");
    
            // 遍历
            for (String s: array) {
                System.out.println(s);
            }
    
            System.out.println("----------");
    
            LinkedList<String> linkedList = new LinkedList<String>();
            linkedList.add("hello");
            linkedList.add("world");
            linkedList.add("java");
            
            for(String s: linkedList) {
                System.out.println(s);
            }
        }
    }
    
    

    LinkedList集合

    特有功能

    方法名 说明
    public void addFirst(E e) 在改列表开头插入指定元素
    public void addLast(E e) 将指定的元素追加到此列表末尾
    public E getFirst() 获取列表中第一个元素
    public E getLast() 获取列表中最后一个元素
    public E removeFirst() 移除并返回列表第一个元素
    public E removeLast() 移除并返回列表最后一个元素

    Demo

    package commonApi.list;
    
    import java.util.LinkedList;
    
    public class LinkedListDemo {
        public static void main(String[] args) {
            // 创建LinkedList对象
            LinkedList<String> linkedList = new LinkedList<String>();
            linkedList.add("hello");
            linkedList.add("world");
            linkedList.add("java");
    
            // public void addFirst(E e)
            linkedList.addFirst("javaee");
            // public void addLast(E e)
            linkedList.addLast("javaee");
            //public E getFirst() 获取列表中第一个元素
            System.out.println(linkedList.getFirst());
            // public E getLast() 获取列表中最后一个元素
            System.out.println(linkedList.getLast());
            // public E removeFirst() 移除并返回列表第一个元素
            System.out.println(linkedList.removeFirst());
            // public E removeLast() 移除并返回列表最后一个元素
            System.out.println(linkedList.removeLast());
            System.out.println(linkedList);
        }
    }
    
    

    Set集合

    set集合特点:

    • 不包含重复元素的集合
    • 没有带索引的方法, 所以不能使用普通for循环遍历

    Demo

    package commonApi.list;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class SetDemo {
        public static void main(String[] args) {
            // 创建集合对象
            Set<String> st = new HashSet<String>();
    
            // 添加元素
            st.add("hello");
            st.add("world");
            st.add("java");
            // st.add("java");  // Set不能包含重复元素
    
            // 遍历
            for(String s: st) {
                System.out.println(s);
            }
        }
    }
    
    

    哈希值

    哈希值: 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值.

    Object类中有一个方法可以获取对象的哈希值

    • public int hashCode(): 返回对象的哈希码值

    哈希值的特点:

    • 同一个对象调用多次hashCode方法返回的哈希值是相同的
    • 默认情况下, 不同对象的哈希值是不同的. 重写hashCode()方法, 可以实现让不同对象的哈希值相同.

    Demo

    package commonApi.hash;
    
    public class HashCodeDemo {
        public static void main(String[] args) {
            // 创建学生对象
            Student s1 = new Student("林青霞", 30);
    
            // 对一个对象多次调用hashCode()方法返回哈希值一样
            System.out.println(s1.hashCode());  // 41903949
            System.out.println(s1.hashCode());  // 41903949
    
            // 默认情况下, 两个对象使用hashCode方法, 哈希值不一样
            Student s2 = new Student("林青霞", 30);
            System.out.println(s1.hashCode());  // 41903949
            System.out.println(s2.hashCode());  // 488970385
    
            // 重写hashCode()方法,可以修改hashCode()返回的哈希值
    
            System.out.println("hello".hashCode());  // 99162322
            System.out.println("world".hashCode());  // 113318802
    
            // 不同值哈希值一样的情况
            System.out.println("重地".hashCode());  // 1179395
            System.out.println("通话".hashCode());  // 1179395
        }
    }
    
    

    HashSet集合

    Set的接口实现类, 特点:

    • 底层数据结构是哈希表
    • 对集合的迭代顺序不做保证, 存储和取出顺序可能不一致
    • 没有带索引的方法, 所以不能用for循环
    • 不包含重复元素

    Demo

    package set;
    
    import java.util.HashSet;
    
    public class HashSetDemo {
        public static void main(String[] args) {
            // 创建集合对象
            HashSet<String> hs = new HashSet<String>();
            // 添加元素
            hs.add("hello");
            hs.add("world");
            hs.add("java");
            hs.add("java");
    
            // 遍历
            for(String s: hs) {
                System.out.println(s);
            }
        }
    }
    
    

    add源码分析

    image-20201015170424192

    public boolean add(E e) {
     return map.put(e, PRESENT)==null;
    }
    
    static final int hash(Object key) {
     int h;
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
    public V put(K key, V value) {
     return putVal(hash(key), key, value, false, true);
    }
    
    // hash值和元素的hashCode()方法相关
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
     Node<K,V>[] tab; Node<K,V> p; int n, i;
     // 如果哈希表未初始化, 则对其进行初始化
     if ((tab = table) == null || (n = tab.length) == 0)
         n = (tab = resize()).length;
     // 根据对象的哈希值计算对象的存储位置, 如果该位置没有元素, 就存储元素
     if ((p = tab[i = (n - 1) & hash]) == null)
         tab[i] = newNode(hash, key, value, null);
     else {
         Node<K,V> e; K k;
         /*
             存入的元素和以前的元素比较哈希值
                 如果哈希值不同, 会继续向下执行, 把元素添加到集合
                 如果哈希值相同, 调用对象的equals()方法
                     如果返回false, 会继续向下执行, 把元素添加到集合
                     如果返回true, 说明元素重复, 不存储
         */
         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);
     return null;
    }
    

    LinkedHashSet集合

    特点:

    • 哈希表和链表实现的Set接口, 具有可预测的迭代顺序
    • 链表保证元素有序
    • 哈希表保证元素唯一

    Demo

    package set;
    
    import java.util.LinkedHashSet;
    
    public class LinkedHashSetDemo {
        public static void main(String[] args) {
            // 创建集合对象
            LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();
    
            // 添加元素
            linkedHashSet.add("hello");
            linkedHashSet.add("world");
            linkedHashSet.add("java");
            linkedHashSet.add("world");  // 不会存储
    
            // 遍历
            for(String s: linkedHashSet) {
                System.out.println(s);
            }
        }
    }
    

    TreeSet集合

    特点:

    • 元素有序, 这个顺序不是值存储和取出的顺序, 而是按照一定的规则进行排序, 具体排序方式取决于构造方法
      • TreeSet(): 根据其元素的自然排序进行排序
      • TreeSet(Comparator comparator): 根据指定的比较器进行排序
    • 没有带索引的方法, 所以不能使用普通for循环
    • 不包含重复元素

    Demo

    package set;
    
    import java.util.TreeSet;
    
    public class TreeSetDemo {
        public static void main(String[] args) {
            // 创建集合对象
            TreeSet<Integer> ts = new TreeSet<Integer>();
    
            // 添加元素
            ts.add(10);
            ts.add(50);
            ts.add(30);
            ts.add(40);
    
            ts.add(50);
    
            // 遍历几个
            for(Integer i: ts) {
                System.out.println(i);
            }
        }
    }
    
    

    自然排序Comparable的使用

    创建学生对象并遍历, 创建TreeSet集合使用无参构造方法

    要求: 年龄从小到大排序, 年龄相同, 按照姓名的字母顺序排序

    方式一:

    Student

    package set;
    
    public class Student implements Comparable<Student> {  // 指定泛型
        private String name;
        private int age;
    
        public Student() {};
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getAge() {
            return this.age;
        }
    
        @Override
        public int compareTo(Student s) {
            // return 0;  // 会把所有元素当成同一元素, 只会出一个
            // return 1;  // 会认为前者比后者小, 按照存储顺序输出
            // return -1;  // 会认为前者比后者大, 按照存储的倒序输出
            int num =  this.age - s.age;  // 升序
            int num2 = num==0?this.name.compareTo(s.name): num;  // num==0,也就是年龄相同, 则比较姓名字母排序
            return num2;
        }
    }
    
    

    ComparableDemo

    package set;
    
    import java.util.TreeSet;
    
    public class ComparableDemo {
        public static void main(String[] args) {
            // 创建集合对象
            TreeSet<Student> ts = new TreeSet<Student>();
    
            // 创建学生
            Student s1 = new Student("xishi",29);
            Student s2 = new Student("wangzhaojun",28);
            Student s3 = new Student("diaochan",30);
            Student s4 = new Student("yangyuhuan",33);
    
            Student s5 = new Student("lingqingxia",33);
            Student s6 = new Student("lingqingxia",33);
    
            // 添加学生到集合
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
            ts.add(s5);
            ts.add(s6);
    
            // 遍历集合
            for(Student s: ts) {
                System.out.println(s.getName() + "," + s.getAge());
            }
    
        }
    }
    
    

    方式二:

    Student

    package set.comparableDemo2;
    
    public class Student {
        private String name;
        private int age;
    
        public Student() {};
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getAge() {
            return this.age;
        }
    
    }
    
    

    ComparableDemo

    package set.comparableDemo2;
    
    import set.Student;
    /*
        在构造方法中使用带参数构造方法实现
     */
    import java.util.Comparator;
    import java.util.TreeSet;
    
    public class ComparableDemo {
        public static void main(String[] args) {
            // 创建集合对象
            TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
                @Override
                public int compare(Student s1, Student s2) {
                    // return 0;
                    // this.age - s.age, s1, s2
                    int num = s1.getAge() - s2.getAge();
                    int num2 = num==0?s1.getName().compareTo(s2.getName()): num;
                    return num2;
                }
            });  // 匿名内部类实现
    
            // 创建学生
            Student s1 = new Student("xishi",29);
            Student s2 = new Student("wangzhaojun",28);
            Student s3 = new Student("diaochan",30);
            Student s4 = new Student("yangyuhuan",33);
    
            Student s5 = new Student("lingqingxia",33);
            Student s6 = new Student("lingqingxia",33);
    
            // 添加学生到集合
            ts.add(s1);
            ts.add(s2);
            ts.add(s3);
            ts.add(s4);
            ts.add(s5);
            ts.add(s6);
    
            // 遍历集合
            for(Student s: ts) {
                System.out.println(s.getName() + "," + s.getAge());
            }
    
        }
    }
    
    

    总结:

    • 使用TreeSet集合存储自定义对象, 带参构造方法使用的是比较器排序对元素进行排序的
    • 比较器排序, 就是让集合构造方法接受Comparator的实现类对象, 重写compareTo(T o1, T o2)方法
    • 重写方法时, 一定要注意排序规则要求的主要条件和次要条件来写

    泛型

    概述

    JDK5后引入的特性, 提供了编译时类型安全监测机制, 该机制允许在编译时检测到非法的类型. 他的本质是参数化类型, 也就是说所有操作的数据类型被指定为一个参数.

    参数化类型:

    • 就是讲类型有原来具体的类型参数化, 然后在使用/调用时传入具体的类型
    • 可以用在类, 方法和接口中, 分别被称为泛型类, 泛型方法, 泛型接口

    泛型定义格式:

    • <类型>: 指定一种类型格式, 这里的类型可以看成是形参

    • <类型1, 类型2...>: 指定多种类型的格式, 多种类型之间用逗号隔开. 这里的类型可以看成是形参.

    • 将来具体调用的时候给定的类型可以看成是实参, 并且实参的类型只能是引用数据类型.

    泛型的好处:

    • 把运行时期的问题提前到了编译期间
    • 避免了强制类型转换

    Demo

    package set;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class GenericDemo {
        public static void main(String[] args) {
            // Collection存储字符, 遍历
            // Collection c = new ArrayList();  // 未使用泛型, 会报错
            Collection<String> c = new ArrayList<String>();
    
            // 添加元素
            c.add("hello");
            c.add("world");
            c.add("java");
            // c.add(100);  // 1.泛型会在编译中就发现错误
    
            //遍历集合
            // Iterator it = c.iterator();  // 未使用泛型
            Iterator<String> it = c.iterator();
            while (it.hasNext()) {
                // Object o = it.next();
                // System.out.println(o);
                // String s = (String)it.next();  // ClassCastException: 类型转换错误
                String s = it.next();  // 2.避免使用强制转换
                System.out.println(s);
            }
        }
    }
    
    

    泛型类

    定义格式:

    • 格式: 修饰符 class 类型名<类型> {}
    • 范例: pulic class Ceneric<T> {}, 此处T可以随便写为任意标识, 常见的如T,E,K,V等形式的参数常用语表示泛型.

    Demo

    package set.generic;
    /*
        实现一个参数接受多种数据类型, 使用泛型类
     */
    public class GenericDemo2 {
        public static void main(String[] args) {
            Student s = new Student();
            s.setName("林青霞");
            System.out.println(s.getName());
    
            Teacher t = new Teacher();
            t.setAge(30);
            System.out.println(t.getAge());
    
            System.out.println("--------");
            Generic<String> g1 = new Generic<String>();
            g1.setT("林青霞");
            System.out.println(g1.getT());
    
            Generic<Integer> g2 = new Generic<Integer>();
            g2.setT(30);
            System.out.println(g2.getT());
    
            Generic<Boolean> g3 = new Generic<Boolean>();
            g3.setT(true);
            System.out.println(g3.getT());
        }
    }
    
    

    泛型方法

    泛型方法格式

    • 格式: 修饰符 <类型> 返回值类型 方法名(类型 变量名) {}
    • 范例: public <T> void show(T t) {}

    Generic

    package set.generic.genericFunction;
    /*
    // 这种方式实现很麻烦
    public class Generic {
        public void show(String s) {
            System.out.println(s);
        }
    
        public void show(Integer i) {
            System.out.println(i);
        }
    
        public void show(Boolean b) {
            System.out.println(b);
        }
    }
    
     */
    
    /*
    // 泛型类改进
    public class Generic<T> {
        public void show(T t) {
            System.out.println(t);
        }
    }
     */
    
    // 泛型方法改性
    public class Generic {
        public <T> void show(T t) {
            System.out.println(t);
        }
    }
    

    GenericFunction

    package set.generic.genericFunction;
    
    public class GenericFunction {
        public static void main(String[] args) {
            /*
            Generic g = new Generic();
            g.show("林青霞");
            g.show(30);
            g.show(true);
            // g.show(12.34);  // 报错
             */
    
            // 泛型类的实现
            /*
            Generic<String> g1 = new Generic<String>();
            g1.show("林青霞");
    
            Generic<Integer> g2 = new Generic<Integer>();
            g2.show(30);
    
            Generic<Boolean> g3 = new Generic<Boolean>();
            g3.show(true);
             */
    
            // 泛型方法实现
            Generic g = new Generic();
            g.show("林青霞");
            g.show(30);
            g.show(true);
            g.show(12.34);
        }
    }
    
    

    泛型接口

    泛型接口的定义格式

    • 格式: 修饰符 interface 接口名 <类型> {}
    • 范例: public interface Generic <T t> {}

    Generic

    package set.generic.genericInterface;
    
    public interface Generic<T> {
        void show(T t);
    }
    
    

    GenericImpl

    package set.generic.genericInterface;
    
    public class GenericImpl<T> implements Generic<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }
    
    

    GenericDemo

    package set.generic.genericInterface;
    
    public class GenericDemo {
        public static void main(String[] args) {
            Generic<String> g1 = new GenericImpl<String>();
            g1.show("林青霞");
    
            Generic<Integer> g2 = new GenericImpl<Integer>();
            g2.show(30);
        }
    }
    
    

    类型通配符

    为表示各种泛型List的父类, 可以使用类型通配符

    • 类型通配符: <?>
    • List<?>: 表示元素类型位置的List, 他的元素可以匹配任何的类型, 但是只能是单一类型
    • 仅表示他是各种泛型List的父类, 并不能把元素添加到其中

    如果我们不希望List<?>是任何泛型List的父类, 只希望他代表某一类泛型List的父类, 可以使用类型通配符的上限.

    • 类型通配符上限: <?extends类型>
    • List<? extends Number>: 表示的类型是Number或者其子类型

    除了可以指定类型通配符上限, 也可以表示类型通配符的下线

    • 类型通配符下限: <?super类型>
    • List<?super Number>: 他表示类型是Number或者其父类

    GenericDemo

    package set.generic.typeGeneric;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class GenericDemo {
        public static void main(String[] args) {
            // 类型通配符 <?>
            List<?> list1 = new ArrayList<Object>();
            List<?> list2 = new ArrayList<Number>();
            List<?> list3 = new ArrayList<Integer>();
            System.out.println("--------");
    
            // 类型通配符上限: <? extends 类型>
            // List<? extends Number> list4 = new ArrayList<Object>();  // 超过了Number的上限
            List<? extends Number> list5 = new ArrayList<Number>();
            List<? extends Number> list6 = new ArrayList<Integer>();
    
            // 类型通配符下限: <? super 类型>
            List<? super Number> list7 = new ArrayList<Object>();
            List<? super Number> list8 = new ArrayList<Number>();
            // List<? super Number> list9 = new ArrayList<Integer>();  // 超过Number下限
        }
    }
    
    

    可变参数

    可变参数又称为参数个数可变, 用作方法的形参出现, 那么方法参数个数就是可变的

    • 格式: 修饰符 返回值类型 方法名(数据类型...变量名) {}
    • 范例: public static int sum(int ... a) {}

    VariableArgsDemo

    package set.generic.variableParams;
    
    public class VariableArgsDemo {
        public static void main(String[] args) {
            // System.out.println(sum(10, 20));
            // System.out.println(sum(10,20, 30));
            // System.out.println(sum(10,20, 30, 40));
    
            System.out.println(sum(10, 20, 30, 40));
            System.out.println(sum(10, 20, 30, 40, 50));
            System.out.println(sum(10, 20, 30, 40, 50, 60));
    
        }
        /*
        public static int sum(int a, int b) {
            return a + b;
        }
    
        public static int sum(int a, int b, int c) {
            return a + b + c;
        }
    
        public static int sum(int a, int b, int c, int d) {
            return a + b + c + d;
        }
         */
    
        public static int sum(int ... a) {
            // System.out.println(a);  // a 是个数组
            int sum = 0;
            for(int i : a) {
                sum += i;
            }
            return sum;
        }
    
        public static int sum(int a, int ... b) {
            // 多个参数包含可变参数, 可变参数放在最后
            int sum = 0;
            for(int i : b) {
                sum += i;
            }
            return sum;
        }
    }
    
    

    注意:

    • 可变参数的变量是一个数组
    • 如果方法有多个参数,包含可变参数, 可变参数要放在最后

    可变参数的使用

    Arrays工具类中有一个静态方法

    • public static <T> List <T> asList(T ... a): 返回由指定数组支持的固定大小的列表
    • 不能做增删操作, 可以修改

    List接口中有一个静态方法:

    • public static <E> List<E> of(E ... elements): 返回包含任意数量元素的不可变列表
    • 不能做增删改操作

    Set接口中有一个静态方法:

    • public static Set of(E ... elements): 返回一个包含任意数量元素的不可变集合
    • 不能给重复的元素
    • 返回的集合不能做增删操作, 没有修改方法

    ArgsFunctionDemo

    package set.generic.variableParams;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Set;
    
    public class ArgsFunctionDemo {
        public static void main(String[] args) {
            // public static <T> List <T> asList(T ... a): 返回由指定数组支持的固定大小的列表
            List<String> list = Arrays.asList("hello", "world", "java");
            // list.add("javaee");  // UnsupportedOperationException
            // list.remove("world");  // UnsupportedOperationException
            list.set(1, "javaee");
            System.out.println(list);
    
            // public static <E> List <E> of(E ... elements): 返回包含任意数量元素的不可变列表
            List<String> list1 = List.of("hello", "world", "java", "world");
            // list1.add("javaee");  // UnsupportedOperationException
            // list1.remove("java");  // UnsupportedOperationException
            // list1.set(1, "javaee");  // UnsupportedOperationException
            System.out.println(list1);
    
            // public static <E> Set <E> of(E ... elements): 返回一个包含任意数量元素的不可变集合
            // Set<String> list2 = Set.of("hello", "world", "java", "world");  // IllegalArgumentException
            Set<String> list2 = Set.of("hello", "world", "java");
            // list2.add("javaee");  // UnsupportedOperationException
            // list2.remove("java");  // UnsupportedOperationException
            System.out.println(list2);
        }
    }
    
    

    Map集合

    Map集合和概述

    k-v类型数据结构

    • Interface Map<K, V>: K-键的类型, V-值的类型
    • 将键映射到值的对象; 不能包含重复的键; 每一个键可以映射到最多一个值
    • 举例: 学生的学号和姓名

    Demo

    package map.hashMap;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class MapDemo {
        public static void main(String[] args) {
            // 创建集合对面
            Map<String, String> map = new HashMap<String, String>();
    
            // V put(K key, V value) 将指定的值与该映射中的指定键相关联
            map.put("oldboy01", "林青霞");
            map.put("oldboy02", "张曼玉");
            map.put("oldboy03", "王祖贤");
            map.put("oldboy03", "刘岩");
    
            // 输出
            System.out.println(map);
            // {oldboy02=张曼玉, oldboy01=林青霞, oldboy03=刘岩}
        }
    }
    
    

    注意:

    • 键唯一

    Map集合的基本方法

    方法名 说明
    V put(K key, V value) 添加元素
    V remove(Object Key) 根据键删除键值对元素
    void clear() 移除所有的键值对元素
    boolean containsKey(Object Key) 判断集合是否包含指定的键
    boolean containsValue(Object value) 判断集合是否包含指定的值
    boolean isEmpty() 判断集合是否为空
    int size() 集合的长度, 也就是集合中键值对的个数

    Demo

    package map.hashMap;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class MapFunction {
        public static void main(String[] args) {
            // 创建集合对象
            Map<String, String> map = new HashMap<>();
    
            // V put(K key, V value): 添加元素
            map.put("张无忌", "赵敏");
            map.put("郭靖", "黄蓉");
            map.put("杨过", "小龙女");
    
            // V remove(Object Key): 根据键删除键值对元素
            System.out.println(map.remove("郭靖"));
            System.out.println(map.remove("郭镶"));
    
            // void clear(): 移除所有的键值对元素
            // map.clear();
    
            // boolean containsKey(Object Key): 判断集合是否包含指定的键
            System.out.println(map.containsKey("郭靖"));
            System.out.println(map.containsKey("郭镶"));
    
            // boolean isEmpty(): 判断集合是否为空
            System.out.println(map.isEmpty());
    
            // int size(): 集合的长度, 也就是集合中键值对的个数
            System.out.println(map.size());
    
            // 输出集合
            System.out.println(map);
        }
    }
    
    

    Map集合的过去方法

    方法名 说明
    V get(Object key) 根据键获取值
    Set keySet() 获取所有键的集合
    Collection values() 获取所有值的集合
    Set<Map.Entry<K, V>> entrySet() 获取所有键值对对象的集合

    Demo

    package map.hashMap;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class MapGetFunction {
        public static void main(String[] args) {
            // 创建集合对象
            Map<String, String> map = new HashMap<>();
    
            // V put(K key, V value): 添加元素
            map.put("张无忌", "赵敏");
            map.put("郭靖", "黄蓉");
            map.put("杨过", "小龙女");
    
            // V get(Object key): 根据键获取值
            System.out.println(map.get("张无忌"));
            System.out.println(map.get("张三丰"));
    
            // Set<K> KeySet(): 获取所有键的集合
            System.out.println(map.keySet());
            for(String s: map.keySet()) {
                System.out.println(s);
            }
    
            // Collection <V> values(): 获取所有值的集合
            System.out.println(map.values());
    
        }
    }
    
    

    Map集合的遍历

    方法一:

    • 获取所有键的集合, keySet
    • 遍历所有的键, 获取每一个键, 增强for
    • 根据键去找值, 用get(Object key)方法

    Demo

    package map.hashMap;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class MapTravel {
        public static void main(String[] args) {
            // 创建集合对象
            Map<String, String> map = new HashMap<>();
    
            // V put(K key, V value): 添加元素
            map.put("张无忌", "赵敏");
            map.put("郭靖", "黄蓉");
            map.put("杨过", "小龙女");
    
            Set<String> keySet = map.keySet();
            for(String key: keySet) {
                String value = map.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
    

    方式二:

    • 获取每一个键值对对象的集合, Set<Map.Entry<K, V>> entrySet()
    • 遍历对象集合, 获取每一个键值对, 增强for遍历 map.entry
    • 根据键值对获取键和值
      • getKey()得到键
      • getValue()得到值

    Demo

    package map.hashMap;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class MapTravel {
        public static void main(String[] args) {
            // 创建集合对象
            Map<String, String> map = new HashMap<>();
    
            // V put(K key, V value): 添加元素
            map.put("张无忌", "赵敏");
            map.put("郭靖", "黄蓉");
            map.put("杨过", "小龙女");
            // 方式一:
            /*
            Set<String> keySet = map.keySet();
            for(String key: keySet) {
                String value = map.get(key);
                System.out.println(key + "," + value);
            }
             */
    
            // 方式二:
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for(Map.Entry<String, String> entry: entries) {
                System.out.println(entry.getKey() + "," + entry.getValue());
            }
        }
    }
    
    

    Collections

    针对集合操作的工具类

    Collections常用方法:

    • Public static <T extends Comparable<? Super T>> void sort(List < T > list): 将指定的列表按照升序排序
    • public static void reverse(List < ? > list): 反转指定列表中元素的顺序
    • public static void shuffle(List < ? > list): 使用默认的随机源随机排序指定的列表

    Demo

    package collections;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.List;
    
    public class CollectionsDemo {
        public static void main(String[] args) {
            // 创建集合对象
            List<Integer> list = new ArrayList<Integer>();
    
            // 添加元素
            list.add(30);
            list.add(20);
            list.add(50);
            list.add(10);
            list.add(40);
    
            // Public static <T extends Comparable<? Super T>> void sort(List < T > list): 将指定的列表按照升序排序
            Collections.sort(list);
    
            // public static void reverse(List < ? > list): 反转指定列表中元素的顺序
            Collections.reverse(list);
    
            // public static void shuffle(List < ? > list): 使用默认的随机源随机排序指定的列表
            Collections.shuffle(list);
    
            System.out.println(list);
        }
    }
    
    

    斗地主扑克牌案例

    package collections.poker;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class PokerDemo {
        public static void main(String[] args) {
            // 创建一个牌盒, 存储牌
            ArrayList<String> array = new ArrayList<String>();
    
            // 往牌盒里面装牌
            /*
                ♦2, ♦3,...,♦A
                ♣2, ♣3,...,♣A
                ♥2, ♥3,...,♥A
                ♠2, ♠3,...,♠A
                大王, 小王
             */
            // 定义花色组
            String[] colors = {"♦", "♣", "♥", "♠"};
            // 定义点数
            String[] numbers = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
            for(String color: colors) {
                for(String number: numbers) {
                    array.add(color + number);
                }
            }
            array.add("大王");
            array.add("小王");
    
            // 洗牌
            Collections.shuffle(array);
    
            // 发牌
            ArrayList<String> array1 = new ArrayList<String>();
            ArrayList<String> array2 = new ArrayList<String>();
            ArrayList<String> array3 = new ArrayList<String>();
            ArrayList<String> array4 = new ArrayList<String>();  // 底牌
    
            for(int i=0; i<array.size(); i++){
                String poker = array.get(i);
                if (i>=array.size()-3) {
                    array4.add(poker);
                } else if(i%3==0){
                    array1.add(poker);
                } else if(i%3==1){
                    array2.add(poker);
                } else if(i%3==2){
                    array3.add(poker);
                }
            }
    
            // 看牌
            lookPoker("array1", array1);
            lookPoker("array2", array2);
            lookPoker("array3", array3);
            lookPoker("array4", array4);
        }
    
        // 看牌方法
        public static void lookPoker(String name, ArrayList<String> array) {
            System.out.print(name + "手牌是:");
            for(String poker: array) {
                System.out.print(poker + " ");
            }
            System.out.println();
        }
    }
    
    
  • 相关阅读:
    我记录网站综合系统 技术原理解析[4:我记录框架 路由系统]
    我记录网站综合系统 技术原理解析[番外篇:自己做Stopwatch]
    我记录网站综合系统 技术原理解析[0:简介(代序) 1.7Beta源代码下载开始]
    我记录网站综合系统 技术原理解析[5:JSON 序列化 反序列化]
    一个网站的诞生 MagicDict未来予想図3 [表格的动态增加行和删除行,高手绕路]
    一个网站的诞生 MagicDict未来予想図2 [单页面多个submit的实现,高手绕路]
    我记录网站综合系统 技术原理解析[2:C# 水印和验证码的制作]
    我记录网站综合系统 技术原理解析[3:我记录框架处理流程]
    一个网站的诞生 MagicDict未来予想図4 [表格的动态增加行和删除行,完整版]
    我记录网站综合系统 技术原理解析[1:我记录的整体框架的简介]
  • 原文地址:https://www.cnblogs.com/ryxiong-blog/p/13890435.html
Copyright © 2020-2023  润新知