集合的了解和使用

第9章 集合

​ 集合平时使用的比较多,但是自己很少往深处去看看其实现和原理,这次趁着这个机会了解一下,完全搞懂还是要看自己后续的使用和深挖。

集合框架了解

​ 想要了解集合,就需要先了解集合框架,这里也以最熟悉的队列(queue)为例来介绍集合框架。

  • 集合的接口与实现分离

    和大多数的设计思想一样,集合的接口和实现也是分离的。队列接口指出了可以在队列的尾部添加元素,在头部删除元素,查找队列中元素的个数。队列的实现通常存在两种形式,一种是使用循环数组,一种是使用链表。这样设计的目的是为了:构建集合时不需要知道使用哪种实现,只有在使用集合对象时,才会确定具体的类。同时也是为了保证:一旦程序改变了想法,只需要在一个地方就可以快捷的改变,即改变调用构造器的地方。

  • Collection接口

    集合类的基本接口是Collection接口,这个接口有很多方法,详细可以看一下这个网址:https://docs.oracle.com/javase/8/docs/api/.

    同时,Collection接口又继承了Iterable接口。说到这里就不得不提IteratorIterable接口的关系,前者是迭代器对象,后者则是定义了如何返回迭代器方法的接口。

    迭代器接口有四个方法。hasNext();,next();,remove(),forEachRemaining(Consumer<? super E> action).方法的作用看名称就能看出来,不需要多讲什么。需要注意的就是:迭代器的指针是位于两个元素之间的,不是正好指向元素的位置。所以调用next()方法是先越过下一个元素,再返回越过元素的引用。

  • 范型使用方法

    不难看出,集合框架中的接口都是范型接口,所以也可以编写一些实用的范型方法,现实也确实这么做了。Collection接口中声明了很多方法。作为实现者来说可以通过实现AbstractCollection接口来定制化自己的需求,同时也不用提供其他不需要的例行方法。具体实现的细节可以看看源码。

  • 集合框架中的接口

    IMG_8073B96B2C46-1

    上图已经很明白这个接口之间的关系了。

    具体的集合

    IMG_2A63A75F65C0-1

    ​ 下面就来详细介绍一下各个集合的信息和部分集合的用法。

  • 链表

    链表的优点就是删除和增加元素时消耗不大(只是修改前后元素的引用),但是随机访问时的消耗要比数组大的多。

    在Java语言中,所有的链表实际都是双向链表,即后一个元素存放着前一个元素的引用。

    Java中的链表中的add方法是将元素添加到链表尾部,但是实际使用中很少遇到这种需求,都是在链表中部添加元素。所以这种对于指定位置添加元素的方法都可以交给迭代器去处理。但是使用迭代球也需要注意:可以给一个容器添加多个迭代器,但是应该只有一个迭代器能够读写,其他迭代器尽量不要读写,只给读的权限即可。光说也体现不出什么,实际撸代码吧。

    package collectiontrain;
    
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.ListIterator;
    
    /**
     * LinkedListTrain.java
     * Description:  链表练习
     *
     * @author Peng Shiquan
     * @date 2020/8/13
     */
    public class LinkedListTrain {
        public static void main(String[] args) {
            List<String> a = new LinkedList<>();
            a.add("A");
            a.add("C");
            a.add("E");
            List<String> b = new LinkedList<>();
            b.add("B");
            b.add("D");
            b.add("F");
            b.add("G");
            ListIterator<String> aIter = a.listIterator();
            Iterator<String> bIter = b.iterator();
    
            while (bIter.hasNext()) {
                /**
                 * 在当前位置前添加一个元素
                 */
                if (aIter.hasNext()) aIter.next();
                aIter.add(bIter.next());
            }
            System.err.println(a);
            bIter = b.iterator();
            while (bIter.hasNext()) {
                bIter.next();
                if (bIter.hasNext()) {
                    bIter.next();
                    //删除前一个元素
                    bIter.remove();
                }
            }
            System.err.println(b);
    
            a.removeAll(b);
            System.err.println(a);
        }
    }
    

    可以在敲这段代码的时候先猜一下实际会打印什么,我第一次猜的时候错的离谱。

  • 数组列表

    这个没有啥好讲的,ArrayList用的比较多,需要注意的就是:需要同步时使用Vector,不需要同步时使用ArrayList。

  • 散列集

    用处就是随机访问集合中的任意元素,并且概率相同。涉及到数据结构。

    原理就是散列表为每一个对象计算一个散列码(hash code),散列码是由对象的实例产生的一个整数。具体使用代码如下:

    package collectiontrain;
    
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.Scanner;
    import java.util.Set;
    
    /**
     * SetTrain.java
     * Description: Set练习
     *
     * @author Peng Shiquan
     * @date 2020/8/13
     */
    public class SetTrain {
        public static void main(String[] args) {
            Set<String> words = new HashSet<>();
            long l = 0;
            try {
                Scanner in = new Scanner(System.in);
                while (!in.hasNext("eof")) {
                    String word = in.next();
                    //因为电脑的性能不同,这里可能会是0
                    long callTime = System.currentTimeMillis();
                    System.err.println(callTime);
                    words.add(word);
                    callTime = System.currentTimeMillis() - callTime;
                    System.err.println(callTime);
                    l += callTime;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            Iterator<String> iterator = words.iterator();
            for (int i = 0; i <= 20 && iterator.hasNext(); i++) {
                System.err.println(iterator.next());
            }
            System.err.println("==============");
            System.err.println(words.size() + "时间花费" + l + "时间");
        }
    }
    
  • 树集

    涉及到数据结构,之前学的都忘的差不多了。和红黑树类似,后续可以算法那部分继续了解,这里只是简单的上代码。

    package collectiontrain;
    
    import java.util.Objects;
    
    /**
     * ItemTest.java
     * Description:  树集
     *
     * @author Peng Shiquan
     * @date 2020/8/16
     */
    public class ItemTest implements Comparable<ItemTest> {
    
        private String description;
        private int partNumber;
    
        public ItemTest(String adescription, int apartNumber) {
            description = adescription;
            partNumber = apartNumber;
        }
    
        public String getDescription() {
            return description;
        }
    
        @Override
        public String toString() {
            return "ItemTest{" +
                    "description='" + description + '\'' +
                    ", partNumber=" + partNumber +
                    '}';
        }
    
        @Override
        public boolean equals(Object obj) {
            /**
             * 比较,需要注意的是对象可能是个null对象,但是仍然要返回true
             */
            if (this == obj) return true;
            if (obj == null) return false;
            if (getClass() != obj.getClass()) return false;
            ItemTest other = (ItemTest) obj;
            return Objects.equals(description, other.description) && partNumber == other.partNumber;
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(description, partNumber);
        }
    
        @Override
        public int compareTo(ItemTest o) {
            /**
             * 比较int的大小和string的长度
             */
            int diff = Integer.compare(partNumber, o.partNumber);
            return diff != 0 ? diff : description.compareTo(o.description);
        }
    }
    
    package collectiontrain;
    
    import java.util.Comparator;
    import java.util.NavigableSet;
    import java.util.SortedSet;
    import java.util.TreeSet;
    
    /**
     * TreeSetTrain.java
     * Description: 树集练习
     *
     * @author Peng Shiquan
     * @date 2020/8/16
     */
    public class TreeSetTrain {
        public static void main(String[] args) {
            /**
             * 代码写完,还是不明白,后续了解。
             */
            SortedSet<ItemTest> itemTests = new TreeSet<>();
            itemTests.add(new ItemTest("A", 123));
            itemTests.add(new ItemTest("B", 456));
            itemTests.add(new ItemTest("C", 789));
            System.err.println(itemTests);
    
            NavigableSet<ItemTest> itemTests1 = new TreeSet<>(Comparator.comparing(ItemTest::getDescription));
            itemTests1.addAll(itemTests);
            System.err.println(itemTests1);
        }
    }
    
  • 优先级队列

    优先级队列就是一个可以自我调整的二叉树,对树执行添加和删除操作时,可以让最小的元素移动到根,而不必花费时间对元素进行排序。代码的使用很简单。

    package collectiontrain;
    
    import java.time.LocalDate;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.PriorityQueue;
    
    /**
     * PriorityQueueTest.java
     * Description: 优先级队列练习
     *
     * @author Peng Shiquan
     * @date 2020/8/16
     */
    public class PriorityQueueTest {
        public static void main(String[] args) {
            PriorityQueue<LocalDate> localDates = new PriorityQueue<>();
            localDates.add(LocalDate.MAX);
            localDates.add(LocalDate.MIN);
            localDates.add(LocalDate.of(1903, 12, 10));
            localDates.add(LocalDate.of(1902, 12, 10));
            localDates.add(LocalDate.of(1903, 11, 10));
            System.err.println("打印");
            for (LocalDate localDate : localDates) {
                System.err.println(localDate);
            }
            System.err.println("删除");
            /**
             * 只取出最小的删除,无论如何操作。
             */
            while (!localDates.isEmpty()) {
                System.err.println(localDates.remove());
            }
        }
    }
    

映射

​ 有时集合并不是很适合,例如我们知道某些键的信息,但是想查询与键对应的值。映射就是为了解决这类问题或场景而设计的。

  • 映射基本用法

    映射的两个通用实现分别为:HashMap,TreeMap。名字已经很清楚了。一个时散列映射,一个是树映射。散列只能将键散列,树映射也是对键的整体顺序排序。

    映射中有一些常用的方法,例如:get(k),put(k,v),remove(k),size(),forEach(),映射中键是唯一的,同一个键重复赋值,后一次的赋值会覆盖掉前一次的赋值。

    需要注意的就是如果代码中有更新映射的需求,需要先判断这个键是否存在,在执行对应的逻辑,代码中很容易就实现了,这里不在列举出详细代码。

    使用起来也是比较简单,这个还是比较常用的,代码如下。

            Map<String, String> stringStringMap = new HashMap<>();
            stringStringMap.put("1", "A");
            stringStringMap.put("2", "B");
            stringStringMap.put("3", "C");
            stringStringMap.put("4", "D");
    
            System.err.println(stringStringMap);
    
            stringStringMap.remove("4");
    
            stringStringMap.put("1", "AA");
            System.err.println(stringStringMap.get("1"));
    
            /**
             * 还是需要再了解和使用lambda表达式
             */
            stringStringMap.forEach((k, v) -> System.err.println("key" + k + "===" + "value" + v));
    
  • 视图和其他映射

    视图就是将映射中的元素存放到集合中(官方认为映射不是一个集合)。有三种映射,键集、值集合和键/值对集。需要注意的时键集中删除某个键,映射中也会删除这个键和其对应的值,但是添加则不行。

    弱散列映射,当对键的唯一引用来自于散列条目时,这一数据结构将与垃圾回收器协同工作一起删除键/值对。WeakHashMap使用弱引用来保存键。弱引用其实是将强引用包装,想要获得该对象需要用get方法,具体这里的实现原理可以看一下WeakHashMap的源码,这里水平太差,估计要放到后面留个坑了。

    链接散列集与映射,类似于一个双向链表,调用get和put时,会影响该元素在映射中的位置。其实可以修改这个映射,将用的多的元素放到缓存中。这里不再列举代码了。

    EnumSet是枚举元素集,实现是用位序列,如果对应的值在集中,则相应的位置被置位1。

    标志散列映射,IdentityHashMap类比较特殊,键的散列值不是用hashCode()计算的,是使用identityHashCode()计算的,这个方法和hashCode()的区别看一看看源码,大概就是比较时一个使用的是==,一个是equals()

视图和包装器

​ 这一部分感觉设计到设计模式,这一块目前还不是很熟悉,很多用法不知道为什么这样设计以及这样做的好处。就简单介绍记录一下吧。

​ 视图有几种,子范围的视图、不可修改的视图、同步视图、受查视图。作用名称已经很清楚了。

算法

​ 上面也说过了,映射和集合是一种数据结构,自然离不开算法,看到现在也大致了解了,这些的集合和映射的实现离不开算法。但是感觉现在自己学起来还是比较空,就再给自己留个坑吧。

就这样吧,结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值