Java基础——集合类 左Collection,右Map。

集合类简介

为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就要对对象进行存储,集合就是存储对象最常用的一种方式。

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

集合与数组
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储任意数据类型,集合只能存储对象。

集合类简单结构图

集合类总的来说可以分类2大类,一类是Collection,另外一类是Map。

jhl.png

Collection 接口是最基本的集合接口,它不提供直接的实现,Java SDK提供的类都是继承自 Collection 的“子接口”如 List 和 Set。

在 Java 中所有实现了 Collection 接口的类都必须提供两套标准的构造函数,
一个是无参,用于创建一个空的 Collection,
一个是带有 Collection 参数的有参构造函数,用于创建一个新的 Collection,这个新的 Collection 与传入进来的 Collection 具备相同的元素。

实现了Collection接口的List接口
List 接口为 Collection 直接接口。List 所代表的是有序的 Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现 List 接口的集合主要有:ArrayListLinkedListVectorStack

实现了Collection接口的Set接口
Set 是一种不包括重复元素的 Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与 List 一样,它同样运行 null 的存在但是仅有一个。由于 Set 接口的特殊性,所有传入 Set 集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。实现了 Set 接口的集合有:EnumSetHashSetTreeSet

关于Collection的子接口List和Set后文会详细描述,现在我们先来看一下 Collection 接口里面有那些基本方法

Collection的基本方法

  • boolean add(Object o):该方法用于向集合里面添加一个元素,若集合对象被添加操作改变了,返回true.

  • boolean addAll(Collection c):把集合c里面的所有元素添加到指定集合里面去,如果集合对象被添加操作改变了返回true.

  • void clear():清除集合里面的所有元素,将集合长度变为0。

  • boolean contains(Object o):返回集合里是否包含指定的元素。

  • boolean containsAll(Collection c):返回集合里是否包含集合c内所有的元素。

  • boolean isEmpty():返回集合是否为空(长度是否为0)。

  • Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。

  • boolean remove(Object o):删除集合中指定元素o。

  • boolean removeAll(Collection c):从集合中删除集合c里面的元素。若删除一个或以上返回true。

  • boolean retainAll(Collection c):从集合中删除集合c里不包含的元素。

  • int size():得到集合元素的个数。

  • Object[] toArray():把集合转成一个数组,所有集合元素编程数组元素。

.
.
.

二、Colletion —— List

二.1 List 接口 (有序可重复)

List 是有序的 Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在 List 中的位置,类似于数组下标)来访问 List 中的元素,这类似于 Java 的数组。

和下面要提到的 Set 不同,List 允许有相同的元素。

除了具有 Collection 接口必备的 iterator()方法外,List 还提供一个 listIterator()方法,返回一个 ListIterator 接口,和标准的 Iterator 接口相比,ListIterator 多了一些 add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。   
实现 List 接口的常用类有 LinkedList,ArrayList,Vector 和 Stack。

.
.
.

二.1.1、ArrayList

  • 查询速度快,适合随机访问
  • 不同步

ArrayList 是一个动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入甚至·包括 null。每一个 ArrayLst 都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率

size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间运行,也就是说,添加 n 个元素需要 O(n) 时间(由于要考虑到扩容,所以这不只是添加元素会带来分摊固定时间开销那样简单)。

ArrayList 擅长于随机访问。同时 ArrayList 是非同步的。

ArrayList一些自己的方法

  • void add(int index,Object e):将元素e添加到List集合中的index处;
  • boolean addAll(int index,Collection c):将集合c所包含的所有元素都插入在List集合的index处;
  • Object get(int index):返回集合index索引处的元素;
  • int indexOf(Object o):返回对象o在List集合中第一次出现位置的索引;
  • int lastIndexOf(object o):返回对象o在List集合中最后一次出现的位置索引;
  • Object remove(int index):删除并返回index索引处的元素;
  • Object set(int index,Object e):把集合index处的元素替换为e对象,返回以前在指定位置的元素;
  • List subList(int fromIndex,int toIndex):返回从所有fromIndex(包括)到toIndex(不包括)处所有集合元素的子集合。

.
.
.

二.1.2、LinkedList

  • 增删速度快,涉及增删频繁的数据
  • 非同步

LinkedList 实现了 List 接口,允许 null 元素。
ArrayList是一个动态数组,
而 LinkedList 是一个双向链表

LinkedList 除了有 ArrayList 的基本操作方法外还额外提供了 get,remove,insert 方法在 LinkedList 的首部或尾部。

由于实现的方式不同,LinkedList 不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这样做的好处就是可以通过较低的代价在 List 中进行插入和删除操作。

与 ArrayList 一样,LinkedList 也是非同步的。如果多个线程同时访问一个 List,则必须自己实现访问同步。一种解决方法是在创建 List 时构造一个同步的 List:

List list = Collections.synchronizedList(new LinkedList(…));

.
.
.

二.1.3、Vector

  • 线程安全

与 ArrayList 相似,但是 Vector 是同步的。所以说 Vector 是线程安全的动态数组。它的操作与 ArrayList 几乎一样。

这个用的少,基本都是ArrayList多
.
.
.

二.1.4、Stack

Stack 继承自 Vector,实现一个后进先出的堆栈。Stack 提供 5 个额外的方法使得 Vector 得以被当作堆栈使用。基本的 push 和 pop 方法,还有 peek 方法得到栈顶的元素,empty 方法测试堆栈是否为空,search 方法检测一个元素在堆栈中的位置。Stack 刚创建后是空栈。

三、Collection —— Set

1、Set 接口

Set最大的特性就是不允许在其中存放的元素是重复的。Set 可以被用来过滤在其他集合中存放的元素,从而得到一个没有包含重复新的集合。

Set 是一种不包含重复的元素的 Collection,即任意的两个元素 e1 和 e2 都有 e1.equals(e2)=false,Set 最多有一个 null 元素

很明显,Set 的构造函数有一个约束条件,传入的 Collection 参数不能包含重复的元素。

Set的实现原理是基于Map的。Set中很多实现类和Map中的一些实现类的使用上非常的相似。Map中的“键值对”,其中的 “键”是不能重复的。这个和Set中的元素不能重复一致,其实Set利用的就是Map中“键”不能重复的特性来实现的。

请注意:必须小心操作可变对象(Mutable Object)。如果一个 Set 中的可变元素改变了自身状态导致 Object.equals(Object)=true 将导致一些问题

Set的两个重要方法

  • public boolean add(Object o) :如果set中不存在指定元素,则向set加入 ;

  • public boolean addAll(Collection c) :过滤集合中的重复元素,向set加入 【将c中所有元素添加到Set中,如果Set中已有某一元素,则不添加,因Set不允许有重复值】;

三.1.1 HashSet

对于 HashSet 而言,它是基于 HashMap 实现的,底层采用 HashMap 来保存元素,所以如果对 HashMap 比较熟悉了,那么学习 HashSet 也是很轻松的。

HashSet的元素存放顺序和添加进去时候的顺序没有任何关系【HashSet类按照哈希算法来存取集合中的对象,存取速度比较快】【允许包含值为null的元素,但最多只能有一个null元素】;

HashSet的巧妙实现:就是建立一个“键值对”,“键”就是我们要存入的对象,“值”则是一个常量。这样可以确保, 我们所需要的存储的信息之是“键”。而“键”在Map中是不能重复的,这就保证了我们存入Set中的所有的元素都不重复。而判断是否添加元素成功,则是通 过判断我们向Map中存入的“键值对”是否已经存在,如果存在的话,那么返回值肯定是常量:PRESENT ,表示添加失败。如果不存在,返回值就为null 表示添加成功。

利用Set去重Demo

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;

public class TestClass {
    public static void main(String[] args) {
        Set<String> nameSet = new HashSet<String>();
        nameSet.add("张三");
        nameSet.add("李四");
        nameSet.add("王五");
        nameSet.add("张三");

        // 输出结果 张三李四王五
        for(String name : nameSet){
        System.out.print(name + "\t");
        }

        // List集合去除重复基础数据
        List<String> nameList = new ArrayList<String>();
        nameList.add("张三");
        nameList.add("李四");
        nameList.add("王五");
        nameList.add("赵六");
        nameSet.addAll(nameList);

        // 输出结果 张三李四王五赵六
        for(String name : nameSet){
        System.out.print(name + "\t");
        }

        // 去除编号和用户名一样的 对象,需要重写 equals 方法 和 hashCode方法
        User admin = new User(1, "admin");
        User user = new User(2, "user");
        User user1 = new User(2, "user");
        User admin1 = new User(3, "admin");

        Set<User> userSet = new HashSet<User>();
        userSet.add(admin);
        userSet.add(user);
        userSet.add(admin1);
        userSet.add(user1);

        // 输入结果 admin1admin3user2
        for(User u : userSet){
        System.out.print(u.username + u.id + "\t");
        }
        System.out.println(user.equals(null));
        }
        }

        class User{
        protected Integer id;
        protected String username;
        public User(Integer id, String username){
        this.id = id;
        this.username = username;
        }

        /**
         * 如果对象类型是User 的话 则返回true 去比较hashCode值
         */
        @Override
        public boolean equals(Object obj) {
        if(obj == null) return false;
        if(this == obj) return true;
        if(obj instanceof User){ 
        User user =(User)obj;
        //if(user.id = this.id) return true; // 只比较id
        // 比较id和username 一致时才返回true 之后再去比较 hashCode
        if(user.id == this.id && user.username.equals(this.username)) return true;
        }
        return false;
        }

        /**
         * 重写hashcode 方法,返回的hashCode 不一样才认定为不同的对象
         */
        @Override
        public int hashCode() {
        //return id.hashCode(); // 只比较id,id一样就不添加进集合
        return id.hashCode() * username.hashCode();
        }
        
}

三1.2 TreeSet

以红黑树的形式存储集合元素,使用元素的自然顺序对元素进行排序。整体性能比HashSet低

HashSet 和 TreeSet ,HashSet的整体性能总比TreeSet好,特别是添加和查询操作,只有当一个保持排序的Set时才使用TreeSet。

TreeSet则是对Set中的元素进行排序存放(TreeSet类实现了SortedSet接口,能够对集合中的对象进行排序)。

三1.3 LinkedHashSet

保持元素的添加顺序;
增删快

四、Queue

队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
(队列是一种数据结构.它有两个基本操作:在队列尾部加人一个元素,和从队列头部移除一个元素)

Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Queue接 口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法),以使得只有恰当的方法才可以使用。BlockingQueue 继承了Queue接口。

demo

public class Main {
    public static void main(String[] args) {
        //add()和remove()方法在失败的时候会抛出异常(不推荐)
        Queue<String> queue = new LinkedList<String>();
        //添加元素
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        queue.offer("d");
        queue.offer("e");
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("element="+queue.element()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
        System.out.println("===");
        System.out.println("peek="+queue.peek()); //返回第一个元素 
        for(String q : queue){
            System.out.println(q);
        }
    }
}

输出

a
b
c
d
e
===
poll=a
b
c
d
e
===
element=b
b
c
d
e
===
peek=b
b
c
d
e

.
.

五、Map

Map 提供了一个更通用的元素存储方法。Map 集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值。

Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false

如果使用自定义的类作为Map的key,应重新该类的equals方法和compareTo方法时应有一致的返回结果:即两个key通过equals方法比较返回true时,它们通过compareTo方法比较应该返回0。

Map集合与Set集合元素的存储形式很像,如Set接口下有HashSet、LinkedHashSet、SortedSet(接口)、TreeSet、EnumSet等实现类和子接口,而Map接口下则有HashMap、LinkedHashMap、SortedMap(接口)、TreeMap、EnumMap等实现类和子接口。
Map有时也称为字典,或关联数组。

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

Object getkey():返回该Entry里包含的key值。
Object getValue():返回该Entry里包含的value值。
Object setValue():设置该Entry里包含的value值,并返回新设置的value值。
可以把Map理解成一个特殊的Set,只是该Set里包含的集合元素是Entry对象,而不是普通对象。

  • HashMap:非线程安全,速度快,无序,适用于在Map中插入、删除和定位元素。
  • TreeMap: 线程安全,速度慢,有序,适用于按自然顺序或自定义顺序遍历键(key)。

五.1 Hashtable实现类

HashMap和Hashtable都是Map接口的实现类,Hashtable是一个古老的Map实现类,它从JDK1.0起就有,它包含两个烦琐的方法:elements()(类似于Map接口定义的values()方法)和keys()(类似于Map接口定义的keySet()方法),现在很少使用这两种方法。

  • 同步,线程安全,如果多线程访问同一个Map对象,使用Hashtable实现类更好。
  • 不允许null值,key和value都不可以

HashMap、Hashtable判断两个key相等的标准是:两个key通过equasl方法比较返回ture,两个key的hashCode值相等。

五.2 HashMap实现类

  • 线程不安全,速度快
  • HashMap可以使用null作为key或value。
    (由于HashMap里的可以不能重复,所以HashMap里最多只有一对key-value值为null,但可以有无数多项key-value对的value为null。)
  • 适用于在Map中插入、删除和定位元素

HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()]

LinkedHashMap (Linked了,当然就有序了)

HashMap有一个子类:LinkedHashMap,它也是双向链表来维护key-value对的次序,该链表定义了迭代顺序,该迭代顺序与key-value对的插入顺序保持一致。
LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序(只要插入key-value对时保持顺序即可)。同时又可避免使用TreeMap所增加的成本。

LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能,但在迭代访问Map里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。

五.3 TreeMap实现类

  • 线程安全,速度慢
  • 有序
  • 适用于按自然顺序或自定义顺序遍历键(key)。

Map接口派生了一个SortedMap子接口,TreeMap为其实现类。类似TreeSet排序,TreeMap也是基于红黑树对TreeMap中所有key进行排序,从而保证TreeMap中所有key-value对处于有序状态。TreeMap两种排序方法:

  • 自然排序:TreeMap的所有key必须实现Comparable接口,而且所有key应该是同一个类的对象,否则将会抛出ClassCastExcepiton异常。
  • 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有key进行排序。采用定制排序时不要求Map的key实现Comparable接口。

TreeMap中判断两个key相等的标准也是两个key通过equals比较返回true,而通过compareTo方法返回0,TreeMap即认为这两个key是相等的。


参考

Java中的Set集合类
java中queue的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 本篇文章带你从Java源码深入解析关于Java容器的概念。 参考文献: Java容器相关知识全面总结 Java官方...
    Tsy远阅读 19,719评论 13 142
  • Collection ├List │├LinkedList │├ArrayList │└Vector │└Stac...
    AndyZX阅读 820评论 0 1
  • 1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:V...
    Oneisall_81a5阅读 892评论 0 11
  • 概述 Java集合框架由Java类库的一系列接口、抽象类以及具体实现类组成。我们这里所说的集合就是把一组对象组织到...
    absfree阅读 1,219评论 0 10
  • 天堂里灯火通明 照耀秋日黄昏 宁静而华美 这是一只幸福遗落的鞋子 时光陈旧 永远怀念着另一只 四面而来的风擦洗她的...
    忧伤没有伤口阅读 236评论 0 0