应用开发--集合01、list和set概述

集合概述 Java 集合可分为 Collection 和 Map 两种体系
Collection接口: 单列数据, 定义了存取一组对象的方法的集合

  • List: 元素有序、可重复的集合
  • Set: 元素无序、不可重复的集合
应用开发--集合01、list和set概述
文章图片

Map接口: 双列数据,保存具有映射关系“key-value对”的集合
应用开发--集合01、list和set概述
文章图片

Collection 接口 Collection 接口是 List、 Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如: Set和List)实现。
在 Java5 之前, Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理; 从 JDK 5.0 增加了泛型以后, Java 集合可以记住容器中对象的数据类型。
接口方法:
添加 add(Object obj)addAll(Collection coll) 获取有效元素的个数 int size() 清空集合void clear() 是否是空集合boolean isEmpty() 是否包含某个元素 boolean contains(Object obj): 是通过元素的equals方法来判断是否是同一个对象 boolean containsAll(Collection c): 也是调用元素的equals方法来比较的。 拿两个集合的元素挨个比较。 删除 boolean remove(Object obj) : 通过元素的equals方法判断是否是要删除的那个元素。 只会删除找到的第一个元素 boolean removeAll(Collection coll): 取当前集合的差集 取两个集合的交集 boolean retainAll(Collection c): 把交集的结果存在当前集合中,不影响c 集合是否相等 boolean equals(Object obj) 转成对象数组 Object[] toArray() 获取集合对象的哈希值 hashCode() 遍历 iterator(): 返回迭代器对象,用于集合遍历

遍历数据 Iterator 接口遍历集合元素
  • Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
  • GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。 迭代器模式,就是为容器而生。 类似于“公交车上的售票员”、“火车上的乘务员”、 “空姐” 。
  • Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
  • Iterator 仅用于遍历集合, Iterator 本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合。
  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
应用开发--集合01、list和set概述
文章图片

应用开发--集合01、list和set概述
文章图片

实例:
Iterator iter = coll.iterator(); //回到起点 while(iter.hasNext()){ Object obj = iter.next(); if(obj.equals("Tom")){ iter.remove(); } }

注意:
  • Iterator可以删除集合的元素, 但是是遍历过程中通过迭代器对象的remove方法, 不是集合对象的remove方法。
  • 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
foreach 循环遍历集合
  • Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
  • 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
  • 遍历集合的底层调用Iterator完成操作。
  • foreach还可以用来遍历数组。
List接口 Java中数组用来存储数据的局限性,我们通常使用List替代数组,JDK API中List接口的实现类常用的有: ArrayList、 LinkedList和Vector。
  • List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
  • List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
List除了从Collection集合继承的方法外, List 集合里添加了一些根据索引来操作集合元素的方法:
  • void add(int index, Object ele):在index位置插入ele元素
  • boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
  • Object get(int index):获取指定index位置的元素
  • int indexOf(Object obj):返回obj在集合中首次出现的位置
  • int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
  • Object remove(int index):移除指定index位置的元素,并返回此元素
  • Object set(int index, Object ele):设置指定index位置的元素为ele
  • List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
ArrayList ArrayList 是 List 接口的典型实现类、主要实现类,本质上, ArrayList是对象引用的一个”变长”数组
ArrayList的JDK1.8之前与之后的实现区别?
  • JDK1.7: ArrayList像饿汉式,直接创建一个初始容量为10的数组
  • JDK1.8: ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
注意:Arrays.asList(…) 方法返回的 List 集合, 既不是 ArrayList 实例,也不是Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
LinkedList LinkedList由Node类型的结构体组成的双向链表, 内部没有声明数组,定义内部类Node,作为LinkedList中保存数据的基本结构。
Node类型中定义了first和last,用于记录首末元素。Node除了保存数据,还定义了两个变量:
  • prev变量记录前一个元素的位置
  • next变量记录下一个元素的位置
应用开发--集合01、list和set概述
文章图片

对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
新增方法:
  • void addFirst(Object obj)
  • void addLast(Object obj)
  • Object getFirst() ? Object getLast()
  • Object removeFirst() ? Object removeLast()
Vector Vector 是一个古老的集合, JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。
注意:
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList; Vector总是比ArrayList慢,所以尽量避免使用。
新增方法:
  • void addElement(Object obj)
  • void insertElementAt(Object obj,int index)
  • void setElementAt(Object obj,int index)
  • void removeElement(Object obj)
  • void removeAllElements()
List总结 ArrayList和LinkedList的异同二者都线程不安全,相对线程安全的Vector,执行效率高。此外, ArrayList是实现了基于动态数组的数据结构, LinkedList基于链表的数据结构。对于随机访问get和set, ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove, LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。 Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。 Vector还有一个子类Stack。
Set接口 Set接口是Collection的子接口, set接口没有提供额外的方法
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法
HashSet HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
HashSet 具有以下特点:
  • 不能保证元素的排列顺序?HashSet 不是线程安全的?集合元素可以是 null
  • HashSet 集合判断两个元素相等的标准: 两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
  • 对于存放在Set容器中的对象, 对应的类一定要重写equals()和hashCode(Objectobj)方法,以实现对象相等规则。即: “相等的对象必须具有相等的散列码” 。
向HashSet中添加元素的过程:
  • 当向 HashSet 集合中存入一个元素时, HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值, 然后根据 hashCode 值, 通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。 (这个散列函数会与底层数组的长度相计算得到在数组中的下标, 并且这种散列函数计算还尽可能保证能均匀存储元素, 越是散列分布,该散列函数设计的越好)
  • 如果两个元素的hashCode()值相等, 会再继续调用equals方法, 如果equals方法结果为true, 添加失败; 如果为false, 那么会保存该元素, 但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
  • 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等, hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
应用开发--集合01、list和set概述
文章图片

重写 hashCode() 方法的基本原则
  • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
  • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。
  • 对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
重写 equals() 方法的基本原则
以自定义的Customer类为例,何时需要重写equals()?
  • 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是, 根据Object.hashCode()方法,它们仅仅是两个对象。? 因此,违反了“相等的对象必须具有相等的散列码”。
  • 结论:复写equals方法的时候一般都需要同时复写hashCode方法。 通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
应用开发--集合01、list和set概述
文章图片

LinkedHashSet LinkedHashSet 是 HashSet 的子类,具有以下特点:
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet, 但在迭代访问 Set 里的全部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。
应用开发--集合01、list和set概述
文章图片

TreeSet TreeSet 是 SortedSet 接口的实现类, TreeSet 可以确保集合元素处于排序状态,TreeSet底层使用红黑树结构存储数据,有序,查询速度比List快。
新增的方法如下:
  • Comparator comparator()?Object first()
  • Object last()?Object lower(Object e)
  • Object higher(Object e)
  • SortedSet subSet(fromElement, toElement)
  • SortedSet headSet(toElement)?SortedSet tailSet(fromElement)
TreeSet 两种排序方法: 自然排序和定制排序。默认情况下, TreeSet 采用自然排序。
应用开发--集合01、list和set概述
文章图片

自然排序
自然排序: TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable接口。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。
Comparable 的典型实现:
  • BigDecimal、 BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
  • Character:按字符的 unicode值来进行比较
  • Boolean: true 对应的包装类实例大于 false 对应的包装类实例
  • String:按字符串中字符的 unicode 值进行比较
  • Date、 Time:后边的时间、日期比前面的时间、日期大
向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。
当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。否则,让人难以理解。
定义排序
TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。
定制排序,通过Comparator接口来实现。 需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。此时, 仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
?使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。
实例:
public static List duplicateList(List list) { HashSet set = new HashSet(); set.addAll(list); return new ArrayList(set); }public static void main(String[] args) { List list = new ArrayList(); list.add(new Integer(1)); list.add(new Integer(2)); list.add(new Integer(2)); list.add(new Integer(4)); list.add(new Integer(4)); List list2 = duplicateList(list); for (Object integer : list2) { System.out.println(integer); } }


【应用开发--集合01、list和set概述】

    推荐阅读