Java集合


2019-07-05  12:39:09  by冲冲

1. 集合的由来

通常情况下,程序直到运行时,才知道需要创建多少个对象。但在开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型。为了满足这些常规的编程需要,我们要求能在任何时候,任何地点创建任意数量的对象,而这些对象用什么来容纳呢?我们首先想到了数组,但是数组只能放统一类型的数据,而且其长度是固定的,那该怎么办呢?集合便应运而生了!Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。

2. 集合的概念

Java集合类,存放于 java.util 包中,是一个用来存放对象的容器。

集合只能存放对象。比如你存一个 int 型数据 1 放入集合中,其实它是自动转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。

② 集合存放的是对象的引用,对象本身还是放在堆内存中。

③ 集合可以存放不同类型、不限数量的对象。

④ 任何对象加入集合类后,自动转变为Object类型,所以在取出的时候,需要进行强制类型转换。(解决方案:使用泛型 )

3. 集合的框架

阅读Java集合框架图,不难发现:

① 所有的集合类(除Map系列)都实现了 Iterator 接口。 Iterator 接口定义了遍历集合元素的方法,主要是 hasNext(),next(),remove()三种方法。它的子接口 ListIterator 在它的基础上又添加三种方法,分别是 add(),previous(),hasPrevious()。也就是说如果实现 Iterator 接口,那么在遍历集合中元素的时候,只能往后遍历,被遍历后的元素不会再被遍历到,通常无序集合实现的都是这个接口,比如HashSet;而那些元素有序的集合,实现的一般都是 ListIterator接口,实现这个接口的集合可以双向遍历,既可以通过next()访问下一个元素,又可以通过previous()访问前一个元素,比如ArrayList。

② 在Java 1.2之前,Java提供特设类,有 Dictionary, Vector, Stack, Properties 这些类用来存储和操作对象组。虽然这些类都非常有用,但是它们缺少一个核心的统一主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。对此,当前整个Java集合框架都围绕一组标准接口来设计。你可以直接使用这些接口的标准实现(ArrayList、HashSet等等)。当然,除此之外你也可以通过这些接口来实现满足自己需求的集合。

③ 抽象类的意义。如果要自己实现一个集合类,去实现那些抽象的接口会非常麻烦,工作量很大。这个时候就可以使用抽象类,这些抽象类中给我们提供了许多现成的实现,我们只需要根据自己的需求重写一些方法或者添加一些方法就可以实现自己需要的集合类,工作量大大降低。

④ 用于学习背诵的集合框架图

4. 集合的详解

(1)Iterator接口

Iterator 迭代器,它是Java集合的顶层接口(不包括 map 系列的集合,Map接口是 map 系列集合的顶层接口)。因此,除了 map 系列的集合,我们都能通过迭代器来对集合中的元素进行遍历。

Iterator 接口定义的三个方法有:
① Object next():返回迭代器刚越过的元素的引用,返回值是 Object,需要强制转换成自己需要的类型
② boolean hasNext():判断容器内是否还有可供访问的元素
③ void remove():删除迭代器刚越过的元素

追溯到Collection接口,发现其继承的是Iterable接口。

Iterable接口与Iterator接口的关系,发现Iterable接口封装了Iterator接口。

 1 public class Collection {     //测试Iterable接口的遍历
 2     @SuppressWarnings({ "rawtypes", "unchecked" })
 3     public static void main(String[] args) {
 4         // 产生一个 List 集合,典型实现为 ArrayList。
 5         List list = new ArrayList();
 6         // 添加三个元素
 7         list.add("黑奴");
 8         list.add("肥猪");
 9         list.add("渣狗");
10         list.add("憨熊");
11         // 构造 List 的迭代器
12         Iterator it = list.iterator();
13         // 通过迭代器遍历元素
14         while (it.hasNext()) {
15             Object obj = it.next();
16             System.out.println(obj);
17         }
18     }
19 }

输出结果:

1 黑奴
2 肥猪
3 渣狗
4 憨熊

(2)Collection接口

源码: public interface Collection<E> extends Iterable<E> {...}

 1 public class Collection {
 2     public static void main(String[] args) {
 3         // 将 ArrayList集合作为 Collection的实现类
 4         Collection collection = new ArrayList();
 5
 6         // 添加元素
 7         collection.add("A");
 8         collection.add("B");
 9
10         // 删除指定元素
11         collection.remove("A");
12         // 删除所有元素
13
14         // 检测是否存在某个元素
15         collection.removeAll(c);
16         collection.add("C");
17         collection.contains("C");
18
19         // 判断是否为空
20         collection.isEmpty();
21
22         // 利用增强for循环遍历集合
23         for (Object obj : collection) {
24             System.out.println(obj);
25         }
26
27         // 利用迭代器 Iterator遍历集合
28         Iterator iterator = collection.iterator();
29         while (iterator.hasNext()) {
30             Object obj = iterator.next();
31             System.out.println(obj);
32         }
33     }
34 }

(3)List接口:有序、元素可以重复

源码:public interface List<E> extends Collection<E> {...}

由于 List 接口是继承于 Collection 接口,所以基本的方法如上所示。① void add(Object obj)
② void remove(Object obj)
③ void removeAll()
④ boolean contains(Object obj)
⑤ boolean isEmpty()

List 接口的三个典型实现:
① List list1 = new ArrayList();  //底层数据结构是数组,查询快,增删慢;线程不安全,效率高。动态增长为原来的1.5倍。
② List list2 = new Vector();     //底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合。动态增长为原来2倍。
③ List list3 = new LinkedList(); //底层数据结构是链表,查询慢,增删快;线程不安全,效率高。允许元素为null。

理解:
① 数组就像一排已经编号站好的人,要找出第10个人很容易,根据编号就能找到。但插入、删除慢,往某个位置插入或删除一个人时,后面的人身上的编号都要变。当然,加入或删除的人是在末尾的话就快。
② 链表就像手牵着手站成一圈的人,要找第10个人不容易,必须从第一个人一个个数过去。但插入、删除快。插入时只要解开两个人的手,并重新牵上新加进来的人的手就可以。删除一样的道理。

public class Collection {      //List接口其他方法:指定位置插入元素,替换元素,遍历还可以使用普通for循环。public static void main(String[] args) {// 产生一个 List 集合,典型实现为 ArrayListList list = new ArrayList();// 在指定地方添加元素list.add(2,"A");// 在指定地方替换元素list.set(2, "a");// 获得指定对象的索引int i = list.indexOf("a");System.out.println("索引为:" + i);// 遍历:普通for循环for (int j = 0; j < list.size(); j++) {System.out.println(list.get(j));}}
}

 1 // ArrayList的三种遍历
 2
 3 public class Collection {
 4     public static void main(String[] args) {
 5         List<String> list = new ArrayList<String>();
 6         list.add("AA");
 7         list.add("BB");
 8         list.add("CC");
 9
10         // 第一种遍历:使用for-each
11         for (String str : list) { // 等价于for(int i=0;i<list.size();i++)
12             System.out.println(str);
13         }
14
15         // 第二种遍历:把线性表表变为数组
16         String[] strArray = new String[list.size()];
17         list.toArray(strArray);
18         for (int i = 0; i < strArray.length; i++) //等价于 for(String str:strArray)
19         {
20             System.out.println(strArray[i]);
21         }
22
23         // 第三种遍历:使用迭代器
24         Iterator<String> it = list.iterator();
25         while (it.hasNext())  // 判断下一个元素之后有值
26         {
27             System.out.println(it.next()); //输出当前越过的元素
28         }
29     }
30 } 

(4)Set接口:无序、元素不能重复

Set 接口的三个典型实现:
① Set hashSet = new HashSet();  //无序,元素不重复。底层是哈希表。元素可以是null,但是只能一个null。
② Set linkedHashSet = new LinkedHashSet();  //有序,元素不重复。底层是哈希表和链表,哈希表保证元素唯一性,链表保证元素有序插入。
③ Set treeSet = new TreeSet();  //有序,元素不重复。底层是红黑树。元素不能是null。默认进行排序(升序),所以元素必须相同类型。

(4.1)HashSetHashSet的底层是哈希表,本质是一个数组,存在的意义是加快查询速度。
原因:通常元素在数组中的索引位置是随机的,即元素的取值和元素的位置之间不存在确定的关系。
因此,在数组中查找特定值时,需要把查找值和其他元素进行比较,此时的查询效率取决于查找过程中比较的次数 n 。
而 HashSet 集合底层数组的索引和值存在一个确定的关系:index=hash(value)。我们只需要调用这个公式,就能快速的找到元素或者索引。
对于 HashSet,如果两个对象通过 equals()方法返回 true,这两个对象的 hashCode 值也应该相同。
原因:当向HashSet集合中存入一个元素时,HashSet会先调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置。
如果 hashCode 值不同,直接把该元素存储到 hashCode()指定的位置。
如果 hashCode 值相同,那么会继续判断该元素和集合对象的 equals()作比较。
当 hashCode 相同,equals()为 true,则视为两者同一个对象,所以不保存在 hashSet 中。
当 hashCode 相同,equals()为 false,则存储在之前对象同槽位的链表上,但是这会非常麻烦,我们应该约束这种情况,即保证:如果两个对象通过 equals()方法返回 true,这两个对象的 hashCode 值也应该相同。
因此,每一个存储到哈希表中的对象,都得提供 hashCode()和 equals()方法的实现,用来判断是否是同一个对象。
对于 HashSet 集合,我们要保证如果两个对象通过 equals()方法返回 true,这两个对象的 hashCode 值也应该相同。

(4.2)TreeSet① 必须放入同类的对象。因为默认会进行排序,否则可能会发生类型转换异常.我们可以使用泛型来进行限制。Set treeSet = new TreeSet();
treeSet.add(1);     //添加一个 Integer 类型的数据
treeSet.add("a");   //添加一个 String 类型的数据
System.out.println(treeSet);  //会报类型转换异常的错误② 如果使用 TreeSet() 无参数的构造器创建一个 TreeSet 对象, 则要求放入其中的元素的类必须实现 Comparable 接口,所以在其中不能放入null元素。

TreeSet的排序:
① 自动排序:添加自定义对象的时候,必须要实现 Comparable 接口,并要覆盖 compareTo(Object obj) 方法来自定义比较规则。
如果 this > obj,返回正数 1;
如果 this < obj,返回负数 -1;
如果 this = obj,返回 0 ,则认为这两个对象相等。
其中,数值类型比较数值大小,char和string类型比较unicode值的数值大小。

 1 ② 定制排序: 当需要把一个对象放入 TreeSet 中,需要重写该对象对应的 equals() 方法,并保证该方法与 compareTo(Object obj) 方法有一致的结果。
 2
 3 public class TreeSetTest {
 4     public static void main(String[] args) {
 5         Person p1 = new Person(1);
 6         Person p2 = new Person(2);
 7         Person p3 = new Person(3);
 8
 9         Set<Person> set = new TreeSet<>(new Person());
10         set.add(p1);
11         set.add(p2);
12         set.add(p3);
13         System.out.println(set);  //结果为[1, 2, 3]
14     }
15 }
16
17 class Person implements Comparator<Person>{
18     public int age;
19     public Person(){}
20     public Person(int age){
21         this.age = age;
22     }
23     @Override
24     /***
25      * 根据年龄大小进行排序
26      */
27     public int compare(Person o1, Person o2) {
28         // TODO Auto-generated method stub
29         if(o1.age > o2.age){
30             return 1;
31         }else if(o1.age < o2.age){
32             return -1;
33         }else{
34             return 0;
35         }
36     }
37
38     @Override
39     public String toString() {
40         // TODO Auto-generated method stub
41         return ""+this.age;
42     }
43 }

三个 Set 接口的实现类比较:共同点:
① 都不允许元素重复。
② 都不是线程安全的。解决办法:Set set = Collections.synchronizedSet(set 对象);
不同点:
① HashSet:不保证元素的添加顺序,底层采用哈希表算法,查询效率高。判断两个元素是否相等,equals()方法返回true且hashCode()值相等。因此,存入HashSet中的元素要覆盖equals()方法和hashCode()方法。
② LinkedHashSet:HashSet 的子类,底层采用哈希表算法以及链表算法,既保证了元素的添加顺序,也保证了查询效率。但是整体性能要低于 HashSet。 
③ TreeSet:不保证元素的添加顺序,但是会对集合中的元素进行排序。底层采用 红-黑树算法(树结构比较适合范围查询)。

(5)Map接口:key-value 类型的键值对,key不允许重复,value可以重复

① 严格来说 Map 并不是一个集合,而是两个集合之间的映射关系。

② 两个集合之间通过构成映射关系的两个数据,可以看作是map的一条数据。即 Entry( key,value ) 。Map 可以看成是由多个 Entry 组成。

③ 因为 Map 集合即没有实现于 Collection 接口,也没有实现 Iterable 接口,所以不能对 Map 集合进行 for-each 遍历。

 1 public class Collection {
 2     public static void main(String[] args) {
 3         Map<String, Object> hashMap = new HashMap<>();
 4         // 添加元素到Map中
 5         hashMap.put("key1", "value1");
 6         hashMap.put("key2", "value2");
 7         hashMap.put("key3", "value3");
 8         hashMap.put("key4", "value4");
 9         hashMap.put("key5", "value5");
10
11         // 删除Map中的元素,通过key的值
12         hashMap.remove("key1");
13         // 通过get(key)得到Map中的value
14         Object str1 = hashMap.get("key2");
15
16         // 可以通过添加方法来修改Map中的元素
17         hashMap.put("key2", "value22");
18
19         // 第一种遍历:通过map.values()得到value集合。缺点是不能遍历key。
20         java.util.Collection<Object> value = hashMap.values();
21         for (Object obj : value) {
22             System.out.println(obj);
23         }
24
25         // 第二种遍历:通过map.keySet()得到key集合,然后通过get(key)得到value。
26         Set<String> set = hashMap.keySet();
27         for (String str : set) {
28             System.out.println(str + "=" + hashMap.get(str));
29         }
30
31         // 第三种遍历:通过Map.entrySet()得到 Map的Entry集合,然后遍历。(推荐)
32         Set<Map.Entry<String, Object>> entrys = hashMap.entrySet();
33         for (Map.Entry<String, Object> entry : entrys) {
34             String key = entry.getKey();
35             Object value2 = entry.getValue();
36             System.out.println(key + "=" + value2);
37         }
38
39         // 第四种遍历:通过Map.entrySet使用iterator遍历key和value。
40         Iterator<Entry<String, Object>> it = hashMap.entrySet().iterator();
41         while (it.hasNext()) {
42             Entry<String, Object> entry = it.next();
43             System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
44         }
45
46         System.out.println(hashMap);
47     }
48 }

输出结果:

value22
value5
value3
value4
----------------
key2=value22
key5=value5
key3=value3
key4=value4
----------------
key2=value22
key5=value5
key3=value3
key4=value4
----------------
key= key2 and value= value22
key= key5 and value= value5
key= key3 and value= value3
key= key4 and value= value4
----------------
{key2=value22, key5=value5, key3=value3, key4=value4}

Map接口的常用实现类:
① HashMap:底层是哈希表。特点是key是无序插入,且key不能重复。key判断重复标准:key1和key2是否equals为true且hashCode相等。非线程安全。
② TreeMap:底层是红黑树。特点是key是无序插入,但是会自动升序排序,且key不能重复。key判断重复标准是:compareTo/compare的返回值是0。非线程安全。
③ LinkedHashMap:底层是链表和哈希表。特点是按添加的先后顺序进行有序插入,且key不能重复。key判断重复标准同HashMap。非线程安全。
④ HashTable:底层是哈希表,是HashMap的前身(类似于Vector是ArrayList的前身)。打死都不要用。线程安全。
⑤ Properties:HashTable的子类,要求键、值都为String类型。用来加载资源文件(.properties类型文件)。注意:
① 定义Map时,key通常使用不可变类string,把key作为value的唯一值。
② HashMap、TreeMap、LinkedHashMap都是非线程安全的,但是性能高。解决方案:Map map = Collections.synchronizedMap( Map对象 );
③ HashTable线程安全,但是性能低。

Map 和 Set 集合的关系:
① 都有几个类型的集合。HashMap和HashSet的底层都是哈希表,TreeMap和TreeSet的底层都是红-黑树,LinkedHashMap和LinkedHashSet的底层都是哈希表和红-黑树。
② 分析 Set 的底层源码,我们可以看到,Set 集合就是由Map集合的Key组成。

参考:

https://www.cnblogs.com/ysocean/p/6555373.html

https://www.runoob.com/java/java-collections.html

转载于:https://www.cnblogs.com/yadiel-cc/p/11135543.html

【JavaSE】集合相关推荐

  1. JavaSE基础篇---processon在线脑图分享

    JavaSE基础篇学习笔记脑图 按照学习阶段按java基础知识.变量与运算符.流程控制.数组再到面向对象,结合课程以及学习笔记做成的脑图,内容较多,仅供个人学习.详细见链接脑图.

  2. JAVA-SE基础篇-非静态内部类

    JAVA-SE基础篇-非静态内部类 /*** 非静态内部类* 知识点* 1. 非静态内部类 BattleScore "战斗成绩"* 非静态内部类可以直接在一个类里面定义* 比如:* ...

  3. JavaSE基础篇--Java SE语法02--基本语法

    JavaSE基础篇 Java SE语法02 HelloWorld 基本语法 流程控制 方法 数组 基本语法 标识符 关键字 数据类型 运算符 数据类型转换 运算后结果的数据类型 Java SE语法02 ...

  4. JavaSE基础篇——超详细,Java入门,这一篇就够了

    第一节:Java语言概述 一.java背景知识 1.Java是美国sun公司(Stanford University Network)在1995年推出的一门计算机高级编程语言. 2.Java早期被称为 ...

  5. Java面试基础篇之集合

    文章目录 你知道的集合都有哪些? 哪些集合是线程安全的? Collection 集合类和数组有什么不同? Collection和Collections有什么区别? 如何确保一个集合不能被修改? Lis ...

  6. Linux基础篇大集合

    目录 一.基础篇 (一)基本常识 1.linux的三种网络连接方式 2.虚拟机的克隆 3.虚拟机的快照 4.虚拟机的迁移和删除 (二)目录结构 (三)基本实操 1.远程连接Linux操作系统 2.vi ...

  7. 【JavaSE】《基础篇005》集合

    文章目录 遍历集合的方式 集合与数组之间转换 List接口 ArrayList Vector LinkedList CopyOnWriteArrayList List各子类的区别与对比 说一下aray ...

  8. 【JAVA基础篇】集合框架

    一.集合框架图 Java集合框架主要包含两种类型的容器,一是集合(Collection),存储元素集合,二是图(Map),存储键(key)-值(value)对.Collection接口下面有两个重要的 ...

  9. java如何把查到的对象集合放入 展示对象list中_Java面试整理-基础篇8.集合1

    1.Java中常见的集合及其关系? 2.ArrayList.LinkedList.Vector的区别? 1. LinkedList.ArrayList.Vector都是List接口的子类:Linked ...

  10. 【Java基础篇】集合排序

    所谓集合排序是指对集合内的元素进行排序. 集合工具类Collections中提供了两种排序算法,分别是: Collections.sort(List list) Collections.sort(Li ...

最新文章

  1. include/filter.inc.php,dedecms /include/filter.inc.php 变量覆盖注入漏洞及解决方案
  2. C#未来新特性:静态委托和函数指针
  3. python程序员在公司都是做什么的-为什么企业很难招聘到好的python程序员?
  4. 想知道黑苹果什么味道?来咬一口尝尝~
  5. timestamp列的有关知识
  6. c#endread怎么打印出来_打印机打印出来是白板是怎么回事
  7. 【转】DevOps到底是什么意思?
  8. jdbc显示mysql的数据_JDBC链接mysql插入数据后显示问号的原因及解决办法
  9. diybox路由器设置教程_家庭无线路由器怎么设置 家庭无线路由器设置教程【详细方法】...
  10. Nginx集群之基于Redis的WebApi身份验证
  11. securecrt安装mysql_centos安装mysql(for 小白)
  12. 2019年5月的Flag!
  13. Mac的反编译工具一:otool (objdump工具的OSX对应工具)。
  14. PHP5.5 ~ PHP7.2新特性总结
  15. 虚拟机下Ubuntu打开摄像头是黑屏问题
  16. 程序员博客是否应该晒代码(由摄影而感)
  17. 算法设计——基姆拉尔森计算公式:计算几月几号是星期几
  18. 分享133个ASP源码,总有一款适合您
  19. php+矩阵,PHP实现简单矩阵算法
  20. 算法动态规划之杂交水果取名问题

热门文章

  1. 福建理工学校计算机类专业有哪些专业吗,福建理工学校
  2. 天津市发布节能降碳工作方案,到2025年数据中心PUE不超过1.5
  3. java小游戏之飞机大战
  4. iTOP-4418嵌入式开发板接口基本介绍
  5. OpenGL二 - 画一个五角星 pentagram
  6. linux系统命令vi存盘退出的其他方法
  7. Properties --- C++读配置信息的类
  8. mysql squence分布式,xsequence分布式序列号生成组件1.0版本终于发布了
  9. 世界类脑AI巨系统研究综述,论ET大脑是不是全球首个类脑架构AI
  10. win10html怎么创建后打不开,Win10打不开路由器设置页面(设置界面)怎么办?