【JAVA】集合

一、概述

集合类和数组?

数组元素既可以是基本类型的值,也可以是对象(实际上是对象的引用变量) 而集合里只能保存对象(实际上只是保存对象的引用变量)。

所有集合类都在java.util包下

体系结构

Java集合类由两个接口派生:CollectionMap
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、Collection和Iterator接口

Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。

2.1 基本使用

public class CollectionTest {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        Collection a = new ArrayList();
//        添加
        c.add("java");
        a.add("python");
        c.add(66);  //虽然集合不能放基本类型的值,但Java支持自动装箱
        System.out.println("c集合的元素个数为:"+c.size());  //2
        System.out.println("a集合的元素个数为:"+a.size());  //1
        System.out.println("-------------------------------------");
//        a加入c中
        c.addAll(a);
        System.out.println("c集合的元素个数为:"+c.size());  //3
        System.out.println("a集合的元素个数为:"+a.size());  //1
        System.out.println("-------------------------------------");
//        删除指定
        c.remove(66);
        System.out.println("c集合的元素个数为:"+c.size());  //2
        System.out.println("-------------------------------------");
//        是否包含python
        System.out.println("c集合是否包含python:"+c.contains("python"));  //true
        System.out.println("-------------------------------------");
        //        c是否包含a的元素
        System.out.println("c集合是否包含a:"+c.containsAll(a));  //true
        System.out.println("-------------------------------------");
        //        a是否为空
        System.out.println(" a是否为空:"+a.isEmpty());  //false
        System.out.println("-------------------------------------");
        //       清空
        c.clear();
        System.out.println("c集合的元素个数为:"+c.size());  //0
        System.out.println("-------------------------------------");
    }
}

2.2 遍历集合

2.2.1 Lambda表达式

public class CollectionEach {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        //1. 用lambda表达式:调用foreach
        books.forEach(o -> System.out.println("迭代集合:"+o));
        System.out.println("--------------------------------");
        }
        }

2.2.2 Iterator迭代器

public class IteratorTest {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        books.add("c");
        //获取迭代器
        Iterator it = books.iterator();
        //集合元素没被遍历完
        while (it.hasNext()){
            String book = (String) it.next();//it.hasNext()返回的是object类型,要强制类型转换
            System.out.println(book);   //打印出python、Java、c

            if(book.equals("c")){
                it.remove();
            }
            book = "javascript";   //不会影响外面
        }
        System.out.println(books);  //[python, java]
    }
}

2.2.3 使用Lambda表达式遍历Iterator

public class IteratorEach {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        books.add("c");

        Iterator it = books.iterator();
        it.forEachRemaining(o -> System.out.println(o));
    }
}

2.2.4 使用foreach循环遍历集合元素

public class ForeachTest {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        books.add("c");
        for (Object obj: books) {
            String book = (String) obj;
            System.out.println(book);
        }
    }
}

2.3 removeIf(Predicate filter)

Java8为Collection集合新增了一个removelf(Predicate filter)方法,该方法将会批量删除符合filter条件的所有元素。

public class ForeachTest {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        books.add("c");
        books.removeIf(o -> ((String)o).equals("c"));   //过滤掉C
        System.out.println(books);  //[python, java]
    }
}

2.4 Stream操作集合

public class PredicateTest2 {
    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("java");
        books.add("python");
        books.add("c");
        books.add("ajax");
        
        System.out.println(books.stream().filter(o -> ((String)o).contains("aj")).count()); //记录包含aj
        System.out.println(books.stream().filter(o -> ((String)o).contains("java")).count());//记录包含aj
        System.out.println(books.stream().filter(o -> ((String)o).length()>4).count());//记录大于四
        books.stream().filter(o -> ((String)o).length()>4).forEach(System.out::println); //打印
    }
}

三、Set集合

3.1 HashSet类

3.1.1 特点

不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
HashSet不是同步的,多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。
集合元素值可以是null

3.1.2 流程

向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置.如果有两个元素通过equals()比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。

3.1.3hashcode()对Hashset重要性

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

表面上看起来,HashSet集合里的元素都没有素引,实际上程序向HashSet集合中添加元素时,HashSet会根据该元素的hashCode值来计算它的存储位置,这样也可快速定位该元素。

3.1.4不用数组?用HashSet

因为数组元素的索引是连续的,而且数组的长度是固定的,无法自由增加数组的长度。而HashSet就不一样了,HashSet采用每个元素的hashCode值来计算其存储位置,从而可以自由增加HashSet的长度,并可以根据元素的hashCode值来访问元素。因此,省去从HashSet中访问元素时,HashSet先计算该元素hashCode值,然后直接到该hashCode值对应的位置去取出该元素—这就是HashSet速度
很快的原因。

也就是说,HashSet集合判断两个元素相等的标准是两个对象通过equals()比较相等,并且两个对象的hashCode()返回值也相等。

3.1.5 重写equals()和hashcode()

//类A的equals()方法总是返回true,但没有重写其hashCode()方法
class A{
    public boolean equals(Object obj){
        return true;
    }
}
//类B的hashCode()方法总是返回1,但没有重写其equals()方法
class B{
    public int hashCode(){
        return 1;
    }
}
//类C的equals()方法总是返回2,重写其hashCode()方法
class C{
    public boolean equals(Object obj){
        return true;
    }
    public int hashCode(){
        return 2;
    }
}
public class HashSetTest {
    public static void main(String[] args) {
        HashSet books = new HashSet();
        books.add(new A());
        books.add(new A());
        books.add(new B());
        books.add(new B());
        books.add(new C());
        books.add(new C());
        System.out.println(books); //B@1, B@1, C@2, A@74a14482, A@1540e19d]
        //A通过equals()比较返回true,hashset当成2个对象
        //B通过hashcode()都返回1,hashset当成2个对象
        //C返回2,hashset当成1个对象
    }
}

3.2 LinkedHashSet类

HashSet还有一个子类LinkedHashSet,LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。也就是说,当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。

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

public class LinkedHashSetTest {
    public static void main(String[] args) {
        LinkedHashSet book = new LinkedHashSet();
        book.add("java");
        book.add("python");
        System.out.println(book);   //[java, python]
        book.remove("python");
        System.out.println(book);   //[java]
    }
}

3.3 TreeSet类

TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
如果希望TreeSet能正常运作,TreeSet只能添加同一种类型的对象

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet();
        ts.add(-1);
        ts.add(7);
        ts.add(-8);
        ts.add(4);
        ts.add(10);
        System.out.println(ts.first());  //-8
        System.out.println(ts.last());  //10
        System.out.println(ts.headSet(4));  //[-8, -1]  小于不包含4
        System.out.println(ts.tailSet(8));    //[10]   大于8
    }
}

3.3.1 自然排序

TreeSet会调用集合元素的compare To(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序。默认也是。

Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类的对象就可以比较大小。

3.3.2 定制排序

如果需要实现定制排序,则需要在创建TreeSet集合对象时,提供一Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。由于Comparator是一个函数式接口,因此可使用Lambda表达式来代替Comparator对象。

3.4 EnumSet类

EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。

EnumSet的集合元素也是有序的,在Enum类内的定义顺序来决定集合元素的顺序。

EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用containsAll()和retainAll()方法)时,如果其参数也是EnumSet集合,则该批量操作的执行速度也非常快。

EnumSet集合不允许加入null元素,如果试图插入null元素,EnumSet将抛出NullPointerException异常。如果只是想判断EnumSet是否包含null元素或试图删除null元素都不会抛出异常,只是删除操作将返回false,因为没有任何null元素被删除。

EnumSet类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建EnumSet对象。

enum Season{
    spring,summer,fall,winter;
}
public class EnumSetTest {
    public static void main(String[] args) {
        //创建一个EnumSet集合,集合元素是Season枚举类全部枚举值
        EnumSet es1 = EnumSet.allOf(Season.class);
        System.out.println(es1);  //[spring, summer, fall, winter]

        //创建一个EnumSet空集合,指定集合元素是Season枚举类
        EnumSet es2 = EnumSet.noneOf(Season.class);
        System.out.println(es2);  //[]
        es2.add(Season.winter);
        es2.add(Season.spring);
        System.out.println(es2);  //[spring, winter]

        //创建EnumSe中Season枚举类
        EnumSet es3 = EnumSet.of(Season.summer,Season.spring);
        System.out.println(es3);    //[spring, summer]

        //创建EnumSe中Season枚举类
        EnumSet es4 = EnumSet.range(Season.spring,Season.fall);
        System.out.println(es4);    //[spring, summer, fall]

        //创建EnumSe中Season枚举类
        EnumSet es5 = EnumSet.complementOf(es4);
        System.out.println(es5);    //[winter]
        
		//创建EnumSe中Season枚举类
        EnumSet es6 = EnumSet.copyOf(es4);
        System.out.println(es6);    //[spring, summer, fall]
    }
}

3.5 各Set实现类的性能分析

HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。

HashSet还有一个子类:LinkedHashSet,对于普通的插入、删除操作LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快
EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
必须指出的是,Set的三个实现类HashSet、TreeSet和EnumSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来“包装”该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。

四、List集合

List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。Ls集合默认按元素的添加顺序设置元素的索引,例如第一次添加的元素索引为0,第二次添加的元素索引为1…

public class ListTest {
    public static void main(String[] args) {
        List books = new ArrayList();
        books.add(new String("python"));
        books.add(new String("java"));
        System.out.println(books);  //[python, java]
        books.add(1,new String("ajax"));  //在1的位置添加Ajax
        System.out.println(books);  //[python, ajax, java]
        books.remove(2);          //删除2位置元素
        System.out.println(books);     //[python, ajax]
        books.set(1,new String("java"));  //修改1位置
        System.out.println(books);   //[python, java]
        System.out.println(books.subList(0,1));  //[python] 截取0-1,不包括1
        System.out.println(books.indexOf("python"));  //0  python在集合第一次出现的位置
    }
}
public class ListTest2 {
    public static void main(String[] args) {
        List books = new ArrayList();
        books.add(new String("python"));
        books.add(new String("java"));
        books.add(new String("ajax"));
        books.sort(((o1, o2) -> ((String)o1).length()-((String)o2).length()));
        System.out.println(books);  //[java, ajax, python]  先比较字符串越长越大,一样长则看插入顺序
    }
}

既可正向迭代也可反向迭代

public class ListIteratorTest {
    public static void main(String[] args) {
        String[] books={"python","java"};
        List booklist = new ArrayList();
        for (int i = 0;i<books.length;i++){
            booklist.add(books[i]);
        }
        ListIterator listIterator = booklist.listIterator();
        while (listIterator.hasNext()){
            System.out.println(listIterator.next());
            listIterator.add("=================");
        }
        System.out.println("---------------");
        while (listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }
    }
}

在这里插入图片描述

4.1 ArrayList和Vector实现类

ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态的、允许再分配的Object()数组。ArrayList或Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超出了该数组的长度时,它们的initialCapacity会自动增加。

4.1.1 ArrayList和Vector的显著区别是?

ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性;

Vector集合则是线程安全的,无须程序保证该集合的同步性。因为Vector是线程安全的,所以Vector的性能比ArrayList的性能要低。实际上,即使需要保证List集合线程安全,也同样不推荐使用Vector实现类。

4.1.2 固定长度的List

public class FixedsizeList {
    public static void main(String[] args) {
        List fixedList = Arrays.asList("java","java ee");
        System.out.println(fixedList.getClass()); //class java.util.Arrays$ArrayList  获取fixedList实现类
        fixedList.forEach(System.out::println);    //遍历集合元素 java java ee
//        fixedList.add("ajax");  //报错:java.lang.UnsupportedOperationException 因为是固定长度不能加
    }
}

五、Queue集合

Queue用于模拟队列这种数据结构,队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列中存放时间最短的元素。元素插入(offr)到队列的尾部访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。

5.1、PriorityQueue实现类

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

public class PriorityQueueTest {
    public static void main(String[] args) {
        PriorityQueue pq = new PriorityQueue();
        pq.offer(6);
        pq.offer(-3);
        pq.offer(20);
        pq.offer(18);
        System.out.println(pq);  //[-3, 6, 20, 18]
        System.out.println(pq.poll());   //-3
    }
}

5.2、Deque接口与ArrayDeque实现类

Deque接口是Queue接口的子接口,它代表一个双端队列,允许从两端来操作队列的元素
在这里插入图片描述

public class ArrayDequestack {
    public static void main(String[] args) {
        ArrayDeque stack = new ArrayDeque();  //模拟栈
        stack.push("python");
        stack.push("java");
        System.out.println(stack);  //[java, python]
        System.out.println(stack.peek());  //java
        System.out.println(stack);   //[java, python]
        System.out.println(stack.pop());  //java
        System.out.println(stack);    //[python]

        ArrayDeque queue = new ArrayDeque();  //模拟队列
        queue.offer("python");
        queue.offer("java");
        System.out.println(queue);   //[python, java]
        System.out.println(queue.peek());   //python
        System.out.println(queue);       //[python, java]
        System.out.println(queue.pop());    //python
        System.out.println(queue);       //[java]
    }
}

5.3、LinkedList实现类

LinkedList类是List接口的实现类一一这意味着它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,可以被当成双端队列来使用,因此既可以被当成“栈”来使用,也可以当成队列使用。

public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList books = new LinkedList();
        books.offer("python");  //加到队列尾部
        books.push("java");    //加到栈的顶部
        books.offerFirst("c");   //加到队列头部,相当于栈的顶部
        for (int i =0;i<books.size();i++){
            System.out.println(books.get(i));  //C JAVA PYTHON
        }
        System.out.println(books.peekFirst());  //c
        System.out.println(books.peekLast());   //python
    }
}

LinkedList与ArrayList、ArrayDeque的实现机制完全不同,ArrayList、ArrayDeque内部以数组的形式来保存集合中的元素,因此随机访问集合元素时有较好的性能:而LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色(只需改变指针所指的地址即可)。虽然Vector也是以数组的形式来存储集合元素的,但因为它实现了线程同步功能(而且实现机制也不好),所以各方面性能都比较差。

5.4、线性表性能分析

Java提供的List就是一个线性表接口,而ArrayList、LinkedList又是线性表的两种典型实现:基于数组的线性表和基于链的线性表。Queue代表了队列,Deque代表了双端队列

LinkedList集合不仅提供了List的功能,还提供了双端队列、栈的功能就行,由于数组以一块连续内存区来保存所有的数组元素,所以数组在随机访问时性能最好,所有的内部以数组作为底层实现的集合在随机访问时性能都比较好:而内部以链表作为底层实现的集合在执行插入、删除操作时有较好的性能。但总体来说,ArrayList的性能比LinkedList的性能要好

关于使用List集合有如下建议。

如果需要遍历List集合元素,对ArrayList、.Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,则应该采用迭代器(Iterator)来遍历集合元素。
如果需要经常执行插入、删除操作来改变包含大量数据的Lst集合的大小,可考虑使用LinkedList集合。使用ArrayList、Vector集合可能需要经常重新分配内部数组的大小,效果可能较差。
如果有多个线程需要同时访问List集合中的元素,开发者可考虑使用Collections将集合包装成线程安全的集合。

六、Map集合

Map用于保存具有映射关系的数据,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false.

public class MapTest {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("java",109);
        map.put("python",12);
        map.put("c",56);
        System.out.println(map);     //{python=12, java=109, c=56}
        System.out.println("重新加一个python的值:"+ map.put("python",58));    //12,  若是和之前一样,返回原来值
        System.out.println("是否包含Java:" + map.containsKey("java"));   //是否包含Java:true
        System.out.println("是否包含58的值:" + map.containsValue(58));    //是否包含58的值:true,虽然覆盖掉,但是值还在
        for (Object key : map.entrySet()){
            System.out.println("遍历key:" + key + ":"+map.get(key));  //python=58  java=109   c=56
        }
        map.remove("c");
        System.out.println(map);  //{python=58, java=109}  删除c

        map.replace("xml",99);
        System.out.println(map);  //{python=58, java=109}  没有不做修改

        map.merge("python",10,(old,param) -> (Integer)old + (Integer) param);
        System.out.println(map);    //{python=68, java=109}   python+10了
        
        map.computeIfPresent("java",(key,value) ->(Integer)value*(Integer) value);
        System.out.println(map);  //{python=68, java=11881}   109*109
    }
}

6.1、HashMap和Hashtable实现类

HashMap和Hashtable都是Map接口的典型实现类,它们之间的关系完全类似于ArrayList和Vector的关系

Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所HashMap比Hashtable的性能高一点;但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好。

Hashtable不允许使用null作为key和value,如果试图把null值放进Hashtable中,将会引发NullPointerException异常;但HashMap可以使用null作为key或value

public class MapTest1 {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put(null,null);
        map.put(null,null);
        System.out.println(map);  //不报错,但只出现一个{null=null},
        map.put("a",null);
        System.out.println(map);  //{null=null, a=null} ,value可以多个null
        
    }
}

6.2、LinkedHashMap实现类

HashSet有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类,LinkedHashMap也使用双向链表来维护key-value对的次序,顺序与key-value对的插入顺序保持一致

LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序,同时又可避免使用TreeMap所增加的成本。

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

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap score = new LinkedHashMap();
        score.put("java",12);
        score.put("c",20);
        score.put("python",33);
        score.forEach((key,value) -> System.out.println(key + ":" + value));
// java:12
//c:20
//python:33
    }
}

6.3、使用Properties读写属性文件

Properties类是Hashtable类的子类,Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名=属性值”加载到Map对象中。

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        Properties p = new Properties();
        p.setProperty("username","ll");
        p.setProperty("password","qw"); //必须字符串型
        p.store(new FileOutputStream("a.ini"),"comment line");  //写入文件中
        System.out.println(p);
    }
}

6.4、SortedMap接口和TreeMap实现类

Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类。
TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。TreeMap也有两种排序方式:自然排序,定制排序
自然排序

class R implements Comparable{
    int count;
    public R(int count){
        this.count = count;
    }

    @Override
    public String toString() {
        return "R{" +
                "count=" + count +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        R r = (R) o;
        return count == r.count;
    }

    @Override
    public int compareTo(Object o) {
        R r = (R)o;
        return count > r.count ? 1: count < r.count ? -1:0;
    }
}
public class TreeMapTest {
    public static void main(String[] args) {
        java.util.TreeMap tm = new java.util.TreeMap();
        tm.put(new R(3),"spring");
        tm.put(new R(-5),"java");
        System.out.println(tm);   //{R{count=-5}=java, R{count=3}=spring}
        System.out.println("返回第一个entry对象:"+tm.firstEntry());   //返回第一个entry对象:R{count=-5}=java
        System.out.println("返回最后一个key值:"+tm.lastKey());  //返回最后一个key值:R{count=3}
        System.out.println("返回比new(2)大的值:"+tm.higherKey(new R(2)));  //返回比new(2)大的值:R{count=3}
        System.out.println("返回比new(2)小的值:"+tm.lowerKey(new R(2)));   //返回比new(2)小的值:R{count=-5}
        System.out.println(tm.subMap(new R(-1),new R(4)));  //{R{count=3}=spring}  子map
    }
}

6.5、WeakHashMap实现类

WeakHashMap与HashMap的用法基本相似。

与HashMap的区别在于,HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收,HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap的key只保留了对实际对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。

public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap whm = new WeakHashMap();
        //没被其他引用
        whm.put(new String("java"),new String("58"));
        whm.put(new String("c"),new String("51"));
        whm.put(new String("python"),new String("22"));
        //该key是一个系统缓存的字符串对象        
        whm.put("xml",new String("46"));
        System.out.println(whm);  //{xml=46, java=58, c=51, python=22}
        //垃圾回收
        System.gc();
        System.runFinalization();
        System.out.println(whm);   //{xml=46}
    }
}

6.6、IdentityHashMap实现类

在IdentityHashMap中,当且仅当两个key严格相等(keyl=key2)时,IdentityHashMap才认为两个key相等;对于普通的HashMap而言,只要keyl和key2通过equals()方法比较返回true,且它们的hashCode值相等即可。

public class IdentityHashMapTest {
    public static void main(String[] args) {
        IdentityHashMap ihm = new IdentityHashMap();
        ihm.put(new String("语文"),89);
        ihm.put(new String("语文"),79);
        ihm.put("java",90);
        ihm.put("java",22);
        System.out.println(ihm);  //{语文=89, java=22, 语文=79} new出来会当成2个处理,后面的会被当成1个
    }
}

6.7、EnumMap实现类

EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑、高效。
EnumMap根据key的定义顺序来维护key-value对的顺序。当程序通过keySet()、entrySet()、values()等方法遍历EnumMap时可以看到这种顺序。
.
EnumMap不允许使用nul作为key,但允许使用null作为value。使用null作为key时将抛出NullPointerException异常。如果只是查询是否包含值为null的key,或只是删除值为null的key,都不会抛出异常。

enum Season1{
    spring,summer,fall,winter;
}
public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap em = new EnumMap(Season1.class);
        em.put(Season1.summer,"夏日炎炎");
        em.put(Season1.spring,"春光明媚");
        System.out.println(em);  //{spring=春光明媚, summer=夏日炎炎}
    }
}

6.8、各Map实现类的性能分析

HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashMap通常比Hashtable要快。

TreeMap通常比HashMap、Hashtable要慢(尤其在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对(红黑树的每个节点就是一个key-value对)。TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。

当TreeMap被填充之后,就可以调用keySet(),取得由key组成的Set,然后使用toArray()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。

程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的(HashMap底层其实也是采用数组来存储key-value对)。但如果程序需要一个总是排好序的Map时,则可以考虑使用TreeMap。

LinkedHashMap比HashMap慢一点,因为它需要维护链表来保持Map中key-value时的添加顺序。
EnumMap的性能最好,但它只能使用同一个枚举类的枚举值作为key。

6.9、HashSet和HashMap的性能选项

hash表里可以存储元素的位置被称为“桶(bucket)”,在通常情况下,单个“桶”里存储一个元素,此时有最好的性能:hash算法可以根据hashCode值计算出“桶”的存储位置,接着从“桶”中取出元素。但hash表的状态是open的:在发生“hash冲突”的情况下,单个桶会存储多个元素,这些元素以链表形式存储,必须按顺序搜索
在这里插入图片描述
hash表里还有一个“负载极限”,“负载极限”是一个0-1的数值,“负载极限”决定了hash表的最大填满程度。当hash表中的负载因子达到指定的“负载极限”时,hash表会自动成倍地增加容量(桶的数量),并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashSet和HashMap、Hashtable的构造器允许指定一个负载极限,HashSet和HashMap、Hashtable默认的“负载极限”为0.75,这表明当该hash表的3/4已经被填满时,hash表会发生rehashing。

“负载极限”的默认值(0.75)是时间和空间成本上的一种折中:较高的“负载极限”可以降低hash表所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的操作,较低的“负载极限”会提高查询数据的性能,但会增加hash表所占用的内存开销。

七、Collections工具类

public class SortTest {
    public static void main(String[] args) {
        ArrayList nums = new ArrayList();
        nums.add (2);
        nums.add (-5);
        nums.add (3);
        nums.add(0);
        System.out.println(nums);//输出:[2, -5, 3, 0]
        Collections.reverse(nums);
        System.out.println("次序反转:"+ nums); // 次序反转:[0, 3, -5, 2]
        Collections.sort(nums);
        System.out.println("排序:"+ nums); // 排序:[-5, 0, 2, 3]
        Collections.shuffle(nums);
        System.out.println("混洗:"+ nums); //混洗:[3, -5, 0, 2]
        System.out.println("最大元素:"+ Collections.max(nums)); //最大元素:3
        System.out.println("nums中0用1代替:"+ nums); //nums中0用1代替:[3, 2, -5, 1]
        Collections.replaceAll(nums,0,1);
        System.out.println("nums中0用1代替:"+ nums); //nums中0用1代替:[3, 2, -5, 1]
        System.out.println("-5出现的次数:"+Collections.frequency(nums,-5)); //-5出现的次数:1
        Collections.sort(nums);  //只有排序才可二分
        System.out.println("二分法查-5位置:"+ Collections.binarySearch(nums,-5)); //二分法查-5位置:0
    }
}

7.1、线程同步

Java中常用的集合框架中的实现类HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap和TreeMap都是线程不安全的。如果有多个线程访问它们,而且有超过一个的线程试图修改它们,则存在线程安全的问题。Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。

public class SynchronizedTest {
    public static void main(String[] args) {
        Collection c = Collections.synchronizedCollection(new ArrayList());
        List list = Collections.synchronizedList(new ArrayList());
        Set set = Collections.synchronizedSet(new HashSet());
        Map map = Collections.synchronizedMap(new HashMap());
    }
}

7.2、设置不可变集合

public class UnmodifiableTest {
    public static void main(String[] args) {
        List l = Collections.emptyList();   //1.创建一个空的,不可变的list对象
        Set s = Collections.singleton("java"); //2.创建一个,不可变的set对象
        Map m = new HashMap();
        m.put("c",11);
        m.put("python",20);
        Map map = Collections.unmodifiableMap(m); //3.map对象不可变的版本
       //  l.add("1");  //java.lang.UnsupportedOperationException
       //s.add("1");    //java.lang.UnsupportedOperationException
       // map.put("1",1);  //java.lang.UnsupportedOperationException
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值