美文网首页基础知识
Java集合看了这篇烂文 就够了(不要脸的说)

Java集合看了这篇烂文 就够了(不要脸的说)

作者: 吾乃韩小呆 | 来源:发表于2018-09-13 18:16 被阅读85次

    由于自己不断受挫,最近在深恶补Java 基础,感觉集合的魅力深深的吸引了我,我来记录一下最近的学习笔记,同时分享给大家:

    提个名词,或许你知道 数组, 在不认识集合之前存储大量对象,一般使用的就是数组,但是呢众所周知数组如果进行动态的改变即增加或者删除元素 时,会导致很不方便。大概由于这个原因吧,产生了集合这种 interesting 的容器,下面我们聊聊集合那些事。


    偷来的集合框架图

    一、关于 Collection 接口 的那些事

    1、常识

    集合中存储的东西,被称之为对象,也可以称之为元素。Collection 是集合的顶层接口,他的具体实现类存取的元素,有唯一的、有序的、也有无序的。

    2、Collection 的具体功能描述

    1)、添加功能
    boolean add(E e) 添加单个元素
    boolean addAll(Collection c) 添加一个集合元素
    2)、删除功能
    void clear() 删除集合内所有元素
    boolean remove(Object o)移除一个元素
    boolean removeAll(Collection c)移除集合内数据
    3)、判读功能
    boolean contains(Object o)判读集合内是否包含指定元素
    boolean containsAll(Collection c)判断集合中包含指定集合的集合元素
    boolean isEmpty()判断集合是否为空
    4)、获取功能
    Iterator<E> iterator() 迭代
    5)、测量长度
    int size()返回集合内元素的数量
    6)、交集
    boolean retainAll(Collection c)两个集合内公共的元素即两个集合包含的相同元素

    3、方法使用演示

    由于 Collection 是个接口并且没有子接口,那么我们就用它的子接口的实现类,ArrayList就很好,下面开始我的表演

    1、 add(E e)方法
        //添加功能
        private static void collectionTest() {
            Collection<String> ct = new ArrayList<>();
            boolean what = ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println("what:" + what + "   集合内容:" + ct);
        }
    
    运行结果

    可以看出,添加成功返回值为 true。

    2、clear()方法
      Collection<String> ct = new ArrayList<>();
            boolean what = ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println("what:" + what + "   集合内容:" + ct);
    
            //移除功能
            ct.clear();
            System.out.println("clear 之后的集合:" + ct);
    
    移除之后的结果

    调用 clear() 方法之后 集合内数据被清空

    3、remove()方法
      Collection<String> ct = new ArrayList<>();
            boolean what = ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println("what:" + what + "   集合内容:" + ct);
            //移除指定元素
            ct.remove("天津");
            System.out.println("移除指定元素之后:"+ct);
    
    移除指定元素

    remove 方法:当移除成功时返回值为true,当移除失败 返回 false(当集合内不包含指定元素则移除失败)

    4、contains(Object o)方法
      Collection<String> ct = new ArrayList<>();
            ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println( "集合内容:" + ct);
            //判断包含
            boolean what = ct.contains("天津");
            System.out.println("what:" + what +"  调用方法之后集合:" + ct);
    
    判断包含的结果

    可以看出来,当包含元素时,返回为true,否则为false

    5、size()和 isEmpty() 方法

    size()方法类似,数组的 length()方法
    isEmpty()方法 只是对集合进行的判空,是空的则返回 true,否则为 false

    6、addAll()方法
      Collection<String> ct = new ArrayList<>();
            ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println("ct 集合内容:" + ct);
            //判断包含
            Collection<String> ctAdd = new ArrayList<>();
            ctAdd.addAll(ct);
            System.out.println("ctAdd 集合内容:" + ctAdd);
    
    方法效果展示

    由此可以推断出
    boolean removeAll(Collection c)方法和boolean containsAll(Collection c)方法均是将整个集合进行相关操作,返回值表示是否操作成功

    7、集合的遍历

    1)、转换集合为数组进行相关操作

      Collection<String> ct = new ArrayList<>();
            ct.add("beijing");
            ct.add("tianjin");
            ct.add("shanghai");
            ct.add("hebei");
            ct.add("shandong");
            ct.add("shanxi");
            System.out.println("ct 集合内容:" + ct);
    
            Object[] o = ct.toArray();
    
            for (int i = 0; i < ct.size(); i++) {
                System.out.println("O 数组内容:" + o[i]);
            }
    
    运行结果

    2)、迭代器

            ct.add("北京");
            ct.add("天津");
            ct.add("上海");
            ct.add("河北");
            ct.add("山东");
            ct.add("山西");
            System.out.println("ct 集合内容:" + ct);
    
            Iterator<String> iterator = ct.iterator();
    
            while (iterator.hasNext()) {
                Object obj = iterator.next();
                System.out.println("遍历结果:" + obj);
            }
    

    使用 Iterator 对象对 集合内数据进行遍历输出,是常用的遍历手段。

    其中 hasNext() 方法为判断遍历集合是否包含下一个元素。

    next() 方法为查找下一个元素。

    通过集合获取迭代器对象的过程本质上是直接拿到集合,然后在集合内进行相关操作。

    总结:遍历集合的步骤大概总结为: a、拥有一个非空集合; b、通过集合对象回去迭代器对象; c、通过迭代器的 hasNext()方法判读是否还有下一个对象;d、通过迭代器的 next()方法获取元素并且移动到下一个位置。

    二、List 的那些事

    1、常识

    List 是 一个有序的 Collection 的接口,这里的有序指的是,添加到集合和从集合内取出的顺序是有序的。

    2、方法使用与演示

    由于List 集成 Collection 接口,所以父接口有得方法,它都有了,面向对象三大特性,你懂得。下面我们说一下特有的方法吧
    1)、添加功能
    void add(int index, Object o) 集合指定位置添加元素
    2)、获取功能
    Object get(int index) 根据索引获取元素
    int indexOf(Object o)获取集合内第一次出现元素的索引
    int lastIndexOf(Object o) 获取集合内最后一次出现指定元素的索引
    3)、迭代功能
    ListIterator listIteretor()List 集合的特有迭代器
    4)、删除功能
    Object remove(int index)删除指定位置元素并返回
    5)、截取功能
    subList(int startIndex, int endIndex)截取集合内两个索引之间的数据

    3、方法演示

    1、void add(int index, Object o) 方法
     List<String> list = new ArrayList<>();
            list.add("北京");
            list.add("天津");
            list.add("上海");
            list.add("河北");
            list.add("山东");
            list.add("山西");
    
    
            System.out.println("list 集合内容:" + list);
    
            list.add(0,"中国");
    
            System.out.println("list 集合插入元素后:" + list);
    
    演示实例 但是该方法使用的索引必须的最大值只能等于集合的长度,否则会: 数组下标越界
    2、Object get(int index) 方法
       List<String> list = new ArrayList<>();
            list.add("北京");
            list.add("天津");
            list.add("上海");
            list.add("河北");
            list.add("山东");
            list.add("山西");
    
    
            System.out.println("list 集合内容:" + list);
    
            String s=list.get(2);
            System.out.println("list 得到的数据:" + s);
    
    运行结果

    该方法,如果获取的元素的索引大于集合的长度,则会 数组下标越界。

    3、列表迭代器 ListIterator listIteretor()
       List<String> list = new ArrayList<>();
            list.add("北京");
            list.add("天津");
            list.add("上海");
            list.add("河北");
            list.add("山东");
            list.add("山西");
    
    
            System.out.println("list 集合内容:" + list);
    
            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);
            }
    
    使用效果

    这个迭代器可以进行倒序迭代,但是呢,前提必须是之前进行过正序迭代之后,才可以。

    三、ArrayList 的那些事儿

    1、常识

    ArrayList: 底层为数组实现,查询速度快,增删慢,线程不安全,效率高。
    

    2、方法演示

    ArrayList 没有什么特别的方法,使用 ArrayList 和使用 List 没什么特别的区别,建议研究透彻 List 接口 即可很熟练的使用 ArrayList 。

    四、Vector 类的那些事儿

    1、常识

    底层实现为数组 ,查询快 、增删慢 、线程安全、效率低

    2、特有方法

    1)、添加功能
    void addElement(Object o)添加元素
    2)、获取功能
    Object elementAt(int index)获取指定位置元素

    Enumeration elements()类似于迭代器

    3、方法演示

       Vector<String> vector = new Vector<>();
            vector.addElement("北京");
            vector.addElement("天津");
            vector.addElement("上海");
            vector.addElement("河北");
            vector.addElement("山东");
            vector.addElement("山西");
    
            System.out.println("vector 集合内容:" + vector);
    
            for (int i = 0; i < vector.size(); i++) {
                System.out.println("for 循环遍历产生的第" + i + "个数据:" + vector.elementAt(i));
            }
    
            System.out.println("-------------------------------");
    
            Enumeration<String> enumeration = vector.elements();
            while (enumeration.hasMoreElements()) {
                System.out.println("Enumeration 遍历产生数据:" + enumeration.nextElement());
            }
    
    效果展示

    ArrayList 的出现就是为了替代 Vector 集合的。所以这两个类的各种功能很相似。

    五、LinkedList 类的那些事

    1、常识

    顶层为链表实现、查询慢、增删快、线程不安全、效率高

    2、特有方法

    1)、增加功能
    void addFirst(Object o)在集合首端添加元素
    void addLast(Object o)在集合的末尾添加元素
    2)、获取功能
    Object getFirst()获取集合第一个元素
    Object getLast()获取集合最后一个元素
    3)、删除功能
    Object removeFirst()移除第一个
    Object removeLast()移除最后一个

    3、方法演示

        LinkedList<String> linkedList = new LinkedList<>();
            linkedList.add("北京");
            linkedList.add("天津");
            linkedList.add("上海");
            linkedList.add("河北");
            linkedList.add("山东");
            linkedList.add("山西");
    
            System.out.println("linkedList 集合内容:" + linkedList);
    
            linkedList.addFirst("中国");
            linkedList.addLast("台湾");
    
            System.out.println("添加首尾的 linkedList集合:"+linkedList);
    
    效果展示

    这里不再重复移除功能和获取功能,三种功能类似。

    总结:

    到这里 List 的分支已经说的差不多了,下面我们来实际运用一下。

    需求:去除集合内的重复元素

    实现需求:

    1)、创建新集合方式的去重

       List<String> list = new ArrayList<>();
            list.add("北京");
            list.add("天津");
            list.add("上海");
            list.add("河北");
            list.add("河北");
            list.add("河北");
            list.add("河北");
            list.add("山东");
            list.add("山西");
    
            System.out.println("原有数据:" + list);
    
            List<String> list1New = new ArrayList<>();
    
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
    
                String s = iterator.next();
                if (!list1New.contains(s)) {
                    list1New.add(s);
                }
            }
            for (int i = 0; i < list1New.size(); i++) {
                System.out.println("去重之后的集合 list1New:" + list1New.get(i));
            }
    
    实现效果

    2)、不创建新集合方式

      List<String> list = new ArrayList<>();
            list.add("北京");
            list.add("天津");
            list.add("上海");
            list.add("河北");
            list.add("河北");
            list.add("河北");
            list.add("河北");
            list.add("山东");
            list.add("山西");
            list.add("山西");
            list.add("山西");
    
            for (int i = 0; i < list.size(); i++) {
                for (int j = i + 1; j < list.size(); j++) {
                    if (list.get(i).equals(list.get(j))) {
                        list.remove(j);
                        //不添加该步骤,会导致连续数据去重,去不干净
                        j--;
                    }
                }
            }
    
            System.out.println("去重后集合: " + list);
    
    运行结果

    在注释处添加指定的操作,否则导致有重复数据不可去除干净。

    六、Set 接口那些事儿

    1、常识

    存入数据 唯一、无序。

    2、特有方法

    Set 查询得知,它的方法和 collection 方法完全相同,所以,不太懂或者还有点懵逼,可以看一下前面。

    3、效果演示

     Set<String> set = new HashSet<>();
            set.add("北京");
            set.add("天津");
            set.add("上海");
            set.add("河北");
            set.add("山东");
            set.add("山东");
    
            System.out.println("set集合数据: " + set);
    

    运行效果:


    运行效果

    充分验证了无序与唯一;

    七、HashSet 的那些事儿

    1、常识

    不能保证迭代顺序、底层数据结构是哈希表(哈希表依赖于哈希值存储)、底层添加数据依赖于haseCode()方法和 equals()方法。

    2、独有方法

    很高兴的告诉你,HashSet 类也没有自己独特的方法,那么,我们来分析一下它是这么保证唯一性的吧。

    3、分析 Set 的实现集合怎么保证数据唯一性

    HashSet<String> set = new HashSet<>();
            set.add("北京");
            set.add("天津");
            set.add("上海");
            set.add("河北");
            set.add("山东");
            set.add("山东");
    
            for (String s:set){
                System.out.println("set集合数据: " + s);
            }
    

    下面拿起我们的小手,熟练地 跟着add()方法进入源码:

     public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    

    仔细看只是个 map 添加数据啊,然后跟着 map对象 查看一下:private transient HashMap<E,Object> map; 呦,是个HashMap啊,跟着 map 的值看一下 private static final Object PRESENT = new Object();是个常量,没大用。那么 HashMap 就是我们研究的重点了,HashMap 实现了 Map 接口。然后那我们只能看一下 HashMap 的添加元素的方式了,跟着 put()方法,点开源码:

    public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    
    //------------------------------------------------------
    
     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    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;
        }
    

    仔细一看 putVal()方法一枝独秀,对我们很有价值,仔细研究一下:首先,数据进入的时候进行非空的的判断,待判断完成之后,进行 hash值的判断,但是hash 值相等,但是不代表数据一致,继续进行地址值的判断,还需要进行 equals()方法的判断,若判断结果为 true 则可以将数据进行存储。

    总结:

    可以得出一个结论比较事物的相同,首先比较 hash 值、其次是比较地址值、最后是进行 equals 判断。

    这样导致一个问题,一个类没有实现 hashCode()方法和 equals()方法, 则需要开发者主动实现这两个方法。这样才可以保证唯一性。

    八、LinkedHashSet 的那些事

    1、常识

    元素有序唯一、底层由链表保证元素有序、哈希表保证元素唯一性。

    2、特有方法

    方法全部来自于实现的接口,所以,请看Set 的相关方法。

    3、方法使用

       LinkedHashSet<String> set = new LinkedHashSet<>();
            set.add("北京");
            set.add("天津");
            set.add("上海");
            set.add("河北");
            set.add("山东");
            set.add("山东");
    
            for (String s:set){
                System.out.println("set集合数据: " + s);
            }
    
    结果展示

    总结:

    该集合特点:有序、唯一

    九、TreeSet 的那些事儿

    1、常识

    底层基于 TreeMap 实现、按照元素的自然顺序排序(按照某种规则排序),也可根据创建 Set 时提供的 comparator(比较器) 进行排序。

    2、特有方法

    方法也是和之前的 Set 类型集合相同,没有特殊方法

    3、代码演示

      TreeSet<String> set = new TreeSet<>();
            set.add("aaa");
            set.add("ddd");
            set.add("ccc");
            set.add("zzz");
            set.add("kkk");
            set.add("rrr");
    
            for (String s:set){
                System.out.println("set集合数据: " + s);
            }
    
    运行结果

    4、TreeSet 存储自定义对象 相关问题

    1)、自定义对象如何保证排序以及唯一性
    原始自定义对象代码:

    public class Provice {
        private String name;
        private int num;
    
        public Provice(String name, int num) {
            this.name = name;
            this.num = num;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    }
    

    调用代码:

      TreeSet<Provice> set = new TreeSet<>();
            set.add(new Provice("北京",1));
            set.add(new Provice("天津",2));
            set.add(new Provice("上海",3));
            set.add(new Provice("重庆",4));
            set.add(new Provice("河北",5));
    
    
            for (Provice s:set){
                System.out.println("set集合数据: " + s);
            }
    

    运行结果:


    运行结果

    报错,报错原因不能按照某种规则排序,所以改进

    两种解决bug方法:

    第一种,外部类实现 Comparable 接口

    改进之后的自定义对象代码:

    public class Province implements Comparable<Province> {
        private String name;
        private int num;
    
        public Province(String name, int num) {
            this.name = name;
            this.num = num;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        @Override
        public String toString() {
            return "Province{" +
                    "name='" + name + '\'' +
                    ", num=" + num +
                    '}';
        }
    
    
        @Override
        public int compareTo(Province o) {
    
            //排序的主要条件,按照数字的大小进行排序
            int number = this.num - o.num;
            //次要条件,按照字典顺序进行排序
            return number == 0 ? this.name.compareTo(o.name) : number;
        }
    }
    

    代码调用

      TreeSet<Province> set = new TreeSet<>();
            set.add(new Province("北京",1));
            set.add(new Province("天津",2));
            set.add(new Province("上海",10));
            set.add(new Province("山西",10));
            set.add(new Province("重庆",9));
            set.add(new Province("河北",5));
    
    
            for (Province s:set){
                System.out.println("set集合数据: " + s);
            }
    
    第二种方法 使用匿名内部类方式

    外部类代码不变

    public class Province {
        private String name;
        private int num;
    
        public Province(String name, int num) {
            this.name = name;
            this.num = num;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        @Override
        public String toString() {
            return "Province{" +
                    "name='" + name + '\'' +
                    ", num=" + num +
                    '}';
        }
    }
    
    

    代码调用:

       TreeSet<Province> set = new TreeSet<>(new Comparator<Province>() {
                @Override
                public int compare(Province o1, Province o2) {
    
                    //排序的主要条件,按照数字的大小进行排序
                    int number = o1.getNum() - o2.getNum();
                    //次要条件,按照字典顺序进行排序
                    return number == 0 ? o1.getName().compareTo(o2.getName()) : number;
                }
            });
            set.add(new Province("北京", 1));
            set.add(new Province("天津", 2));
            set.add(new Province("上海", 10));
            set.add(new Province("山西", 10));
            set.add(new Province("重庆", 9));
            set.add(new Province("河北", 5));
    
    
            for (Province s : set) {
                System.out.println("set集合数据: " + s);
            }
    

    总结:

    当使用无参构造的时,TreeMap 内的元素按照自然顺序进行相关排序。

    十、Map 集合那些事儿

    1、常识

    存储键值对元素、键值对内键是唯一、值可以重复、结构特点只与键有关。

    Map集合特点:存储数据成对出现、键唯一、值可重复;
    Collection集合特点:集合元素单独出现、Set集合元素唯一、List集合元素可重复;

    2、特有方法

    1)、添加功能:
    V put(K key, V value)添加元素;
    2)、删除功能:
    void clear();删除集合所有元素;
    V remove(Object key) ;根据键移除数据,并且返回值;
    3)、判断功能:
    boolean containsKey(Object key);判断集合是否包含指定的键;
    boolean containsValue(Object value);判断集合是否包含指定的值;
    boolean isEmpty();判断集合是否为空;
    4)、获取功能
    Set<Map.Entry<K,V>> entrySet()
    v get(Object key)根据键 获取值;
    Set<K> keySet();获取集合中所有键的集合;
    Collection <v>values获取集合中所有值的集合;
    5)、数量统计
    int size()返回键值对的数量。

    3、方法演示

    1)、 put() 方法

    代码实例:

    
            Map<String, Integer> map = new HashMap<>();
    
            map.put("北京", 1);
            map.put("天津", 1);
            map.put("上海", 3);
            map.put("重庆", 4);
            map.put("河北", 5);
            map.put("河北", 6);
            map.put("山东", 7);
    
            System.out.println("map 内数据:" + map);
    
    效果展示

    总结:

    数据无序、key 不可重复、value可重复、 当key重复时,后面的值会覆盖前面的值。

    2、remove()方法
         Map<String, String> map = new HashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("河北", "6");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
    
            String re = map.remove("北京");
    
            System.out.println("remove之后的  map 内数据:" + map + " remove之后的返回值 re: " + re);
    
    运行结果

    可以看出,map 的基本用法大致和 Collection 相同,只是叫法不太一样。

    3、get()方法
    
            Map<String, String> map = new HashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("河北", "6");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
    
            String g=map.get("河北");
    
            System.out.println("get方法调用后: "+g);
    
    get()方法调用后效果
    4、Set<K> keySet() 与 Collection <v>values 的使用
      Map<String, String> map = new HashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
    
            Set<String> set = map.keySet();
    
            System.out.println("获取集合内所有的键: " + set);
    
            Collection<String> values = map.values();
    
            System.out.println("获取集合内所有的值: " + values);
    
    运行结果
    5、Set<Map.Entry<K,V>> entrySet() 的相关

    关于这个的使用,我们还是从一个小例子引出吧
    需求:遍历集合,取出所有的键值对

    1)、方案一 分批获取
    
            Map<String, String> map = new HashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
    
            Set<String> set = map.keySet();
    
            for (String key : set) {
                String value = map.get(key);
                System.out.println("键值对 ---键:" + key + "  键值对---值:" + value);
            }
    
    运行结果
    方案二: 成对获取
    
            Map<String, String> map = new HashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
    
            
            Set<Map.Entry<String, String>> set = map.entrySet();
    
            //Map.Entry<String, String> 为接口 ,但是 m 肯定为子类类型,可以取出 使用。
            for (Map.Entry<String, String> m : set) {
                System.out.println("键值对---键:" + m.getKey() + "  键值对---值:" + m.getValue());
            }
    
    运行结果

    两个方案比较:第一种方案,是在遍历的过程中先取到集合的键,然后根据键去获取值,属于分、分、总。类似于 1+1=2 的 过程。
    第二种方法,是直接遍历集合 “键值对” 的对象,然后通过该对象 直接取出 键和值。

    十一、HashMap 的那些事儿

    可以说 HashMap 只是 Map 的 的简简单单的实现,并没有什么特别的。

    注意一点:

    当使用自定义类来做键时,需要在自定义类内重写 equals() 方法 ,否则会导致出现重复的键值对。

    十二、LinkedHashMap 的那些儿

    1、常识

    LinkedHashMap 是 HashMap 的子类、键唯一、有序(存储和取出顺序一致)

    2、代码演示

       LinkedHashMap<String, String> map = new LinkedHashMap<>();
            map.put("北京", "1");
            map.put("天津", "1");
            map.put("上海", "3");
            map.put("重庆", "4");
            map.put("河北", "5");
            map.put("山东", "7");
            System.out.println("map 内数据:" + map);
            
            Set<Map.Entry<String, String>> set = map.entrySet();
    
            for (Map.Entry<String, String> m : set) {
                System.out.println("键值对---键:" + m.getKey() + "  键值对---值:" + m.getValue());
            }
    
    运行效果

    可以看出,存入的顺序和输出的顺序是一致的。

    十三、TreeMap 的那些事儿

    1、键是红黑树结构,可以保证键的排序和唯一性。类似于 TreeSet 。

    2、方法使用

    TreeMap 没有什么特别的方法,我们来关注一下它的排序规律吧

    1)、基本类型数据
    
            TreeMap<String, String> map = new TreeMap<>();
            map.put("beijing", "1");
            map.put("tianjin", "1");
            map.put("shanghai", "3");
            map.put("chongqing", "4");
            map.put("hebei", "5");
            map.put("shandong", "7");
            System.out.println("map 内数据:" + map);
    
    实现效果
    2)、自定义对象数据

    自定义对象的

    public class Province {
        private String name;
        private int num;
    
        public Province(String name, int num) {
            this.name = name;
            this.num = num;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getNum() {
            return num;
        }
    
        public void setNum(int num) {
            this.num = num;
        }
    
        @Override
        public String toString() {
            return "Province{" +
                    "name='" + name + '\'' +
                    ", num=" + num +
                    '}';
        }
    }
    

    调用代码

      TreeMap<Province, String> map = new TreeMap<>();
    
            Province province1=new Province("北京",1);
            Province province2=new Province("上海",2);
            Province province3=new Province("天津",3);
            Province province4=new Province("深圳",4);
            Province province5=new Province("重庆",5);
            Province province6=new Province("广州",6);
    
    
            map.put(province1, "1");
            map.put(province2, "1");
            map.put(province3, "3");
            map.put(province4, "4");
            map.put(province5, "5");
            map.put(province6, "7");
            System.out.println("map 内数据:" + map);
    

    笑嘻嘻一运行,心里马上MMP;


    运行效果

    产生问题原因是:我们并没有指定排序顺序,下面开始解决问题

    类似于 TreeSet

    
            TreeMap<Province, String> map = new TreeMap<>(new Comparator<Province>() {
                @Override
                public int compare(Province o1, Province o2) {
                    //主要排序条件
                    int num = o1.getNum() - o2.getNum();
                    //次要排序规则
                    int name = num == 0 ? o1.getName().compareTo(o2.getName()) : num;
                    return name;
                }
            });
    
            Province province1 = new Province("北京", 1);
            Province province4 = new Province("深圳", 4);
            Province province6 = new Province("广州", 6);
            Province province5 = new Province("重庆", 5);
            Province province2 = new Province("上海", 2);
            Province province3 = new Province("天津", 3);
    
            map.put(province1, "1");
            map.put(province2, "1");
            map.put(province3, "3");
            map.put(province4, "4");
            map.put(province5, "5");
            map.put(province6, "7");
            System.out.println("map 内数据:" + map);
    
    运行结果

    十四、集合相关的其它问题

    1、Hashtable 与 HashMap 的区别

    Hashtable:线程安全、效率低、不允许 null 键、不允许 null 值
    HashMap :线程不安全、效率高、允许 null 键、允许 null 值

    2、Collections 的那些事儿

    1)、常识

    针对集合操作的工具类、均是静态方法;
    与 Collection 的区别,Collections 是针对集合操作的工具类类、Collection 是单列集合的顶层接口

    2)、应该知道的方法

    public static <T> void sort(List<T> list);排序(默认情况下自然排序);
    public static <T> void binarySearch(List<?> list,T key) 二分查找;
    public static <T> T max (Collection<?> coll)最大值;
    public static void reverse(List<?> list)反转;
    public static void shuffle(List <?> list)随机置换;

    相关文章

      网友评论

        本文标题:Java集合看了这篇烂文 就够了(不要脸的说)

        本文链接:https://www.haomeiwen.com/subject/ppysgftx.html