浅枫

努力努力再努力~~~

《疯狂JAVA讲义》第7章 Java集合 学习笔记

集合和数组的区别:数组元素既可以是基本类型的值,也可以说对象。而集合里只能保存对象。Java集合类主要有两个接口派生而出:Collection和Map

1.Collection和Iterator接口

Collection接口是List、Set、Queue接口的父接口,具有add、remove、clear等方法来操作集合元素。

Iterator接口主要用于遍历Collection集合中的元素,Iterator对象也被称为迭代器,隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口,接口里定义了如下4个方法:

  • boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回true
  • Object next():返回集合里的下一个元素
  • void remove():删除集合里上一次next方法返回的元素
  • void forEachRemaining(Consumer action):可使用Lambda表达式来遍历集合元素

Iterator必须依附于Collection对象。用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove()方法删除上一次next方法返回的元素才可以,否则将会引发java.util.ConcurrentModificationException异常

使用Predicate简化集合的运算

例如:以下程序统计书名中出现“疯狂”字符串的图书数量,其中Predicate是一个函数式接口:

public class PredicateTest2
{
    public static void main(String[] args)
    {
        //创建集合(略)
        ...
        System.out.println(calAll(books,ele->((String)ele).contains("疯狂")));
    }
    public static int calAll(Collection books,Predicate p)
    {
        int total = 0;
        for(Object obj : books)
        {
            //使用Predicate的test()方法判断该对象是否满足Predicate指定的条件
            if(p.test(obj))
            {
                total++;
            }
        }
        return total;
    }
}
Stream流式API

Stream提供商大量的方法进行聚集操作,对于大部分聚集方法而言,每个Stream只能执行一次

  • 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法
  • 末端方法:是对流的最终操作,当对某个Stream执行末端方法后,该流将会被“消耗”且不再可用

常用的中间方法:

  • filter(Predicate predicate):过滤Stream中所用不符合predicate的元素
  • mapToXxx(ToXxxFunction mapper):使用ToXxxFunction对流中的元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction转换生成的所有元素
  • peek(Consumer action):依次对每一个元素执行一些操作,该方法返回的流与原有流包含相同的元素,该方法主要用于调试
  • distinct():该方法用于排序流中所有重复元素(判断标准是使用equals()比较返回true),这是一个有状态的方法
  • sorted():该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法
  • limit(long maxSize):该方法用于保证对流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法

常用的末端方法:

  • forEach(Consumer action):遍历流中所有元素,对每个元素执行action
  • toArray():将流中所有元素转换为一个数组
  • reduce():有三个重载版本,都用于通过某种操作来合并流中的元素
  • anyMatch(Predicate predicate):判断流中是否至少包含一个元素符合Predicate条件
  • allMatch(Predicate predicate):判断流中是否每个元素都符合Predicate条件
  • noneMatch(Predicate predicate):判断流中是否所有元素都不符合Predicate条件
  • findFirst():返回流中的第一个元素
  • findAny():返回流中任意一个元素

通过使用Collection接口提供的stream()默认方法简化代码:

public class CollectionStream
{
     public static void main(String[] args)
    {
        System.out.println(books.stream().filter(ele->((String)ele).contains("疯狂")).count());
        books.stream().mapToInt(ele->((String)ele.length()).forEach(System.out::println);
    }
}

2.Set集合

HasnSet类

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。所以当把一个对象放入HashSet时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法,规则是如果两个对象通过equals()方法返回true,这两个对象的hashCode值也应该相同

补充:hash算法的功能

它能保证快速查找被检索的对象,hash算法的价值在于速度。当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从而快速定位该元素。

当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算的hashCode()、equals()的实例变量,否则将会导致HashSet无法正确操作这些集合元素

LinkedHashSet类

HashSet的子类,使用链表维护元素的次序,是的元素看起来是以茶入的顺序保存的,遍历LinkedHashSet集合里的元素时,将会按元素的添加顺序来访问集合里的元素

TreeSet类

是SortedSet接口的实现类,可以确保集合元素处于排序状态,与HashSet集合相比,TreeSet还提供了如下几个额外的方法:

  • Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator,如果TreeSet采用了自然排序则返回null
  • Object first():返回第一个元素
  • Object last():返回最后一个元素
  • Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要时TreeSet集合里的元素),相反使用Object higher(Object e)
  • SortedSet subSet(Object fromElement,Object toElement):返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)
  • SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素组成
  • SortedSet tailSet(Object fromElement):返回此Set的子集,由大于或等于fromElement的元素组成

如果试图把一个对象添加到TreeSet时,该对象的类必须实现Comparable接口,否则程序将会抛出异常。已经实现了Comparable接口的常用类有:

  • BigDecimal、BigInteger以及所以的数值类型对应的包装类:按它们对应的数值大小进行比较
  • Character:按字符的UNICODE值进行比较
  • Boolean:true对应的包装类实例大于false对应的包装类实例
  • String:按字符串中字符的UNICODE值进行比较
  • Date、Time:后面的时间、日期比前面的大

如果希望TreeSet能正常运作,只能添加同一种类型的对象

3.List集合

List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。与Set集合相比,List增加了根据索引来插入、替换和删除集合元素的方法。除此之外还增加了两个默认方法:

  • void replaceAll(UnaryOperator operator):根据operator指定的计算规则重新设置List集合的所有元素
  • void sort(Comparator c):根据Comparator参数对List集合的元素排序

关于remove(Object o):查阅源码发现List将会调用o所在类的equals()方法依次与集合元素进行比较,该equals()方法以某个集合元素作为参数时返回true,List将会删除该元素。

List还额外提供了一个listIterator()方法,返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。

  • boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素
  • Object previous():返回该迭代器的上一个元素
  • void add(Object o):在指定位置插入一个元素

所以ListIterator增加了向前迭代的功能,但一开始也需要采用正向迭代

ArrayList和Vector实现类

ArrayList和Vector在用法上几乎完全相同,其显著区别是:ArrayList是县城不安全的,当多个线程访问同一个ArrayList集合时如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性。但Vector集合是线程安全的,锁业性能比ArrayList性能要低。但是还是不推荐使用Vector实现类。

固定长度的List

工具类Arrays提供了asList(Object...a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。,否则会引发UnsupportedOperationException异常

4.Queue集合

  • void add(Object e):将指定元素加入此队列的尾部
  • Object element():获取队列头部的元素,但不删除该元素
  • boolean offer(Object e):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Object e)方法更好
  • Object peek():获取队列头部的元素,但是不删除该元素。如果此队列为空,则返回null
  • Object poll():获取队列的头部元素并删除该元素,如果此队列为空,则返回null
  • Object remove():获取队列头部的元素,并删除该元素
PriorityQueue实现类

保存队列元素的顺序不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此调用peek()或者poll()取出的是队列中最小的元素。

Deque接口与ArrayDeque实现类

Deque是Queue接口的子接口,代表一个双端队列。

Deque不仅可以当成双端队列使用,还可以被当成栈来使用,因为该类里还包含了pop、push两个方法

Deque接口提供了一个典型的实现类ArrayDeque。创建Deque时可以指定一个numElements参数,如果不指定,Deque底层数组的长度为16。

LinkedList实现类

它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外LinkedList还实现了Deque接口,可以当成双端队列来使用,因此既可以被当成“栈”来使用,也可以当成队列使用。内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色。但总的来说,ArrayList的性能比LinkedList的性能要好,因此大部分时候都考虑使用ArrayList。

5.Map集合

Map用于保存具有映射关系的数据,key和value之间存在单向一对一关系。从Java的源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。Map有时也被称为字典或关联数组。

常用方法:

  • void clear():删除该Map对象中所有key-value对
  • Set entrySet():返回Map中包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry对象
  • Object get(Object key):返回指定key所对应的value;如果此Map中不包含该key,则返回null
  • boolean isEmpty():查询该Map是否为空
  • Set keySet():返回该Map中所有key组成的Set集合
  • Object put(Object key,Object value):添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对
  • void putAll(Map m):将指定Map中的key-value对复制到本Map中
  • Object remove(Object key):删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null
  • boolean remove(Object key,Object value):删除指定key、value所对应的key-value对。如果成功删除返回true
  • int size():返回key-value对的个数
  • Collection values():返回该Map里所有value组成的Collection

Map中包括一个内部类Entry,该类封装了一个key-value对包含如下三个方法:

  • Object getKey():返回该Entry里包含的key值
  • Object getValue():返回该Entry里包含的value值
  • Object setValue(V value):设置该Entry里包含的value值,并返回新设置的value值
HashMap和Hashtable实现类

它们之间的关系完全类似于ArrayList和Vector的关系:Hashtable是一个古老的Map实现类。典型区别:

  • Hashtable是一个线程安全的Map实现,但HastMap线程不安全,所以HashMap比Hashtable的性能高一点。但如果有多个线程访问同一个Map对象时,Hashtable实现类会更好
  • Hashtable不允许使用null作为key和value,而HashMap可以

由于HashMap里的key不能重复,所以HashMap里最多只有一个key-value对的key为null,但可以有无数多个key-value对的value为null。除此之外,与HashSet集合不能保证元素的顺序一样,HashMap、Hashtable也不能保证其中key-value对的顺序。

LinkedHashMap实现类

LindedHashMap也使用双项链表来维护key-value对的次序,迭代顺序与key-value对的插入顺序保持一致

使用Properties读写属性文件

Properties类是Hashtable类的子类,夜不保证key-value对之间的次序,key、value都是字符串类型。

String getProperty(String key):获取Properties中指定属性名对应的属性值,类似于Map的get(Object key)方法

String getProperty(String key,String defaultValue):如果Properties中不存在指定的key时,则该方法指定默认值

Object setProperty(String key,String value):设置属性值,类似于Hashtable的put()方法

void load(InputStream inStream):从属性文件中加载key-value对,把加载到的key-value对追加到Properties里

void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件中

6.操作集合的工具类:Collections

排序操作

  • void reverse(List list):反转指定List集合中元素的顺序
  • void shuffle(List list):对List集合元素进行随机排序(模拟“洗牌”)
  • void sort(List list):按升序进行排序
  • void sort(List list,Comparator c):根据指定Comparator产生的顺序对List集合元素进行排序
  • void swap(List list,int i, int j):交换元素
  • void rotate(List list, int distance):当distance为正数时,将list集合的后distance个元素“整体”移到前面;当distance为负数时,将list集合的前distance个元素“整体”移到后面,该方法不会改变集合的长度
查找、替换操作(略)
同步控制

Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

设置不可变集合

emptyXxx():返回一个空的、不可变的集合对象,此处的集合既可以说List,也可以是SortedSet、Set,还可以是Map、SortedMap等

singletonXxx():返回一个只包含指定对象的、不可变的集合对象,此处的集合既可以是List,还可以是Map

unmodifiableXxx():返回指定集合对象的不可变视图,此处的集合既可以是List,也可以是Set、SortedSet,还可以是Map、SorteMap等

阅读更多
个人分类: 学习笔记
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

《疯狂JAVA讲义》第7章 Java集合 学习笔记

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭