面经JAVA学习之路

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

2017-06-18  本文已影响317人  阿敏其人

集合类简介

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

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

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

集合类简单结构图

集合类总的来说可以分类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的基本方法

.
.
.

二、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一些自己的方法

.
.
.

二.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的两个重要方法

三.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对象,而不是普通对象。

五.1 Hashtable实现类

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

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

五.2 HashMap实现类

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实现类

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

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


参考

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

上一篇下一篇

猜你喜欢

热点阅读