Java中的集合

集合类是Java中一项很有用的工具类,它很像数组但又远远超过数组。它不仅可以存储数量不等的多个对象还可以实现不同的数据结构!

其中集合主要分为Set、List、Map三个体系!

简单说明

Java API中的位置:java.util。

主要分类

Collection

本图展示了主要的结构图。集合主要由两个接口派生:CollectionMap

Collection:Set与List的父接口,其继承了Iterable接口(用于迭代,其内部只有一个方法iterator)。

Set:无序集合,内部数据不可重复。查找数据主要依赖于元素本身数据内容。

List:有序集合,内部数据可重复。查找数据主要依赖于元素索引。

Map:具有映射关系(key-value对)的集合,内部数据查找主要依赖于key值,因此key值不可重复,但数据内容可重复。

关于Iterator与Iterable的联系与区别,请自行搜索,我们使用时一般认为集合用Iterator来迭代

另外,关于子接口:

ArrayList:使用数组结构存储,可变长,查询快,增删慢,线程不同步

LinkedList:使用链表数组结构存储,查询慢,增删快

Vector:最先出现的集合,与ArrayList相同,但线程同步,速度慢(弃)

Collection中主要的方法

集合的成员方法从根本上来说就是对数据进行增删改查,但是因为各个集合内部数据结构各异,因此其增删改查的内部实现原理不同。

API中collection的方法

Modifier and TypeMethod and Description
boolean add(E e)Ensures that this collection contains the specified element (optional operation).
boolean addAll(Collection c)Adds all of the elements in the specified collection to this collection (optional operation).
void clear()Removes all of the elements from this collection (optional operation).
boolean contains(Object o)Returns true if this collection contains the specified element.
boolean containsAll(Collection c)Returns true if this collection contains all of the elements in the specified collection.
boolean equals(Object o)Compares the specified object with this collection for equality.
int hashCode()Returns the hash code value for this collection.
boolean isEmpty()Returns true if this collection contains no elements.
Iterator iterator()Returns an iterator over the elements in this collection.
default Stream parallelStream()Returns a possibly parallel Stream with this collection as its source.
boolean remove(Object o)Removes a single instance of the specified element from this collection, if it is present (optional operation).
boolean removeAll(Collection c)Removes all of this collection’s elements that are also contained in the specified collection (optional operation).
default boolean removeIf(Predicate filter)Removes all of the elements of this collection that satisfy the given predicate.
boolean retainAll(Collection c)Retains only the elements in this collection that are contained in the specified collection (optional operation).
int size()Returns the number of elements in this collection.
default Spliterator spliterator()Creates a Spliterator over the elements in this collection.
default Stream stream()Returns a sequential Stream with this collection as its source.
Object[] toArray()Returns an array containing all of the elements in this collection.
T[] toArray(T[] a)Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.

代码示例

这里使用ArrayList来完成Collection的成员方法的演示。

import java.util.*;
public class CollectionDemo {
    public static void print(Object o) {
        System.out.println(o);
    }

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList al1 = new ArrayList();
        ArrayList al2 = new ArrayList();

        // 1、添加元素
        al1.add("Hello");
        al1.add("World");
        al1.add("Java");
        al2.add("Hello");
        al2.add("World");
        al2.add("Java");
        print("al1中存在的元素:" + al1);

        // 2、删除元素
        al1.remove("Java");
        print("删除了元素的al1为:" + al1);

        // 3、判断是否为空
        print("al1是否为空?" + al1.isEmpty());

        // 4、是否含有指定元素
        print("al1是否含有Hello元素?" + al1.contains("Hello"));

        // 5、元素个数
        print("al1中的元素个数:" + al1.size());

        // 6、只保留与指定集合的交集
        al1.retainAll(al2);
        print(al1);

        // 7、移除指定集合也有的全部元素
        al1.removeAll(al2);
        print(al1);

        // 8、添加指定集合的所有元素
        al1.addAll(al2);
        print(al1);

        // 9、将集合转换成Object型数组
        print(al1.toArray());

        // 10、指定对象与之是否相等
        print("al1是否和al2相等?" + al1.equals(al2));

        // 11、是否包含指定集合的所有元素
        print("al1是否包含al2的所有元素:" + al1.containsAll(al2));

        // 12、移除所有元素
        al1.clear();
        print("al1移除所有元素后:" + al1);
    }
}

运行结果

al1中存在的元素:[Hello, World, Java]
删除了元素的al1为:[Hello, World]
al1是否为空?false
al1是否含有Hello元素?true
al1中的元素个数:2
[Hello, World]
[]
[Hello, World, Java]
[Ljava.lang.Object;@15db9742
al1是否和al2相等?true
al1是否包含al2的所有元素?true
al1移除所有元素后:[]

说明:代码第七行@SuppressWarnings("unchecked")是Java中的注解,此处主要是抑制有关”不受检查异常”的警告信息!如果没有此行代码,编译时将会出现以下信息(当然也可以不加此代码并人为忽略此信息)。

$ javac CollectionDemo.java -encoding utf8
注: CollectionDemo.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

如果你使用的是eclipse,将会跳过编译结果显示,直接生成运行结果。

使用Iterator遍历集合元素

Iterator接口主要有三种方法

Modifier and TypeMethod and Description
boolean hasNext()如果被迭代集合中还有元素没有被遍历则返回true
E next()返回集合中的下一个元素
default void remove()删除集合中上一次next方法返回的元素

代码示例

import java.util.*;
public class CollectionDemo {

    public static void print(Object o) {
        System.out.println(o);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList al2 = new ArrayList();

        al2.add("Hello");
        al2.add("World");
        al2.add("Java");
        // 1、使用foreach遍历集合
        for (Object obj : al2)
            print(obj);

        print("");
        // 2、使用while遍历集合
        Iterator it = al2.iterator();
        while (it.hasNext()) {
            String al2Value = (String)it.next();
            print(al2Value);
            if (al2Value.equals("World")) {
                it.remove();
            }
        }
        print("");
        // 3、使用for遍历集合
        for (Iterator it1 = al2.iterator(); it1.hasNext();)
            print(it1.next());
    }
}

运行结果

Hello
World
Java

Hello
World
Java

Hello
Java

说明:就遍历来讲,本代码中使用了三种方法实现集合的遍历。for循环的遍历所占据的内存随着for循环的结束而消亡,但是while产生的部分内存不会。foreach循环的遍历并不属于Iterator的遍历,我们应该知道其更加简洁,该遍历下的集合元素不可改变!(也不能删除元素)

综上所述,Iterator中存在的方法使得集合在遍历时是没法被修改的(只能删除,不能增加、更改。当然,不推荐在迭代中删除元素),否则将会引发异常(ConcurrentModificationException)。另外,Iterator迭代器采用的是快速失败机制,一旦在迭代中检测到集合被修改就立即引发异常,这样可以避免(多线程)资源共享引发潜在问题。

Set集合

首先我们看以下代码

import java.util.*;
public class CollectionDemo {

    public static void print(Object o) {
        System.out.println(o);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        Set hs = new HashSet();
        hs.add(new String("Hello"));
        boolean hsResult = hs.add(new String("Hello"));
        print(hsResult);
        print(hs);
    }
}

输出结果

false
[Hello]

分析:我们已经知道Set集合中无法存储相同的元素。这是因为Set本身是无序的,我们每次查找元素时,都是直接查找元素内容本身,如果元素存在相同的,那就没法查找了。我们从上方代码中可以知道:

  • Set是通过equals来区分元素是否相同的,因为此处创建了两个String对象,两者只有值相同,其他不同
  • 存放相同元素时,add会返回false并存放失败

所以,Set也没比它的父接口Collection多什么东西,只是限制了元素不能相同。

HashSet

看如下代码

import java.util.*;
public class CollectionDemo {

    public static void print(Object o) {
        System.out.println(o);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        String str1 = new String("Hello1");
        String str2 = new String("Hello2");
        String str3 = new String("Hello3");
        String str4 = new String("Hello4");
        hs.add(str1);
        hs.add(str2);
        hs.add(str3);
        hs.add(str4);
        hs.add(null);
        print(hs);
    }
}

输出结果

[null, Hello1, Hello4, Hello2, Hello3]

分析:从此代码中,我们能够得出:

  • HashSet不能够保存元素的排列顺序,它每次的输出顺序都不相同,因为它是根据hash(哈希,散列)排布的
  • 集合的元素可以是null
  • 还有一点:HashSet不是同步的,当我们使用多线程操控HashSet时,需要通过代码维持同步(此处代码没法体现)

另外,我们应该知道在Java中两个对象的hashCode值相同时,其equals值不一定是true;但其equals值为true时,其hashCode一定相等。

因为hash是通过计算对象地址转换成的int值,这个int值面对庞大的数据迟早会相同

hash的价值在于它的速度,当我们想访问HashSet的某个元素时,HashSet会通过hashCode直接计算出元素的地址并提取,此时hashCode起到了”索引”的功能(但我们知道Set是无序的,没有索引)。这和数组的工作非常相似,我们提供索引时,数组能够迅速找到该元素的值。但数组和HashSet相比的缺陷是:元素位置连续,数组长度不可变长。

另外,如果我们想让HashSet能够按照我们想要的方式排序,可以使用LinkedHashSet,它是HashSet的子类,它使用链表来维护元素的次序,维护链表需要损失一部分性能。

TreeSet

TreeSet类实现了SortedSet接口,它能够确保集合元素处于排序状态。因为此集合是有序的,因此其方法必然存在访问指定位置(或指定范围)的元素或集合。下面介绍一些该类特有常见的方法:

Modifier and TypeMethod and Description
Comparator comparator()返回TreeSet采用的定制排序所使用的Comparator,没有就返回null
E first()返回集合的第一个元素
E last()返回集合的最后一个元素
E lower(E e)返回小于指定元素的最大元素
E higher(E e)返回大于指定元素的最小元素
NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive)返回Set的指定子集合,范围是[start,end)
SortedSet headSet(E toElement)返回小于指定元素的其他元素组成的子集
NavigableSet tailSet(E fromElement, boolean inclusive)返回大于等于指定元素的其他元素组成的子集

代码示例

import java.util.*;
public class CollectionDemo {
    public static void print(Object o) {
        System.out.println(o);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        TreeSet ts = new TreeSet();
        ts.add(5);
        ts.add(15);
        ts.add(12);
        ts.add(25);
        print(ts);
        print(ts.comparator());
        print(ts.first());
        print(ts.last());
        print(ts.lower(14));
        print(ts.higher(18));
        print(ts.subSet(10, 18));
        print(ts.headSet(14));
        print(ts.tailSet(14));
    }
}

输出结果

[5, 12, 15, 25]
null
5
25
12
25
[12, 15]
[5, 12]
[15, 25]

分析:从代码中可以看出,排序结果是由大小决定的,而不是插入的顺序。

因为TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后按元素升序排列,这就是TreeSet的自然排序。

自然排序

Java提供了一个Comparable接口,该接口定义了int compareTo(Object obj)方法,Java中很多常见的类也已经实现了Comparable接口。比如BigDecimal、BigInteger、Character、Boolean、String、Date、Time(它们都是比较其数值大小、字符的UNICODE值、true>false、时间大小来排序的)。

  • 如果我们想将一个以上的对象添加到TreeSet时,该对象必须已经实现了Comparable接口,否则比较时会抛出异常
  • 当我们添加对象时,程序会通过红黑树确定其存储位置,因此相同对象(compareTo()返回值为0)无法同时保存
  • 添加对象时,程序会自动与已存在的对象比较大小,因此应该尽量保证TreeSet中元素类型相同
  • 如果添加了可变元素,那么它将变得不可删除,所以请尽量添加常量元素

定制排序

我们已经知道obj1.compareTo(obj2)方法返回0时代表两个元素值相同,负数代表obj1\

import java.util.*;
class M {
    int age;
    public M(int age) {
        this.age = age;
    }
    public String toString() {
        return "M[age:" + age + "]";
    }
}
public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new Comparator() {
            public int compare(Object obj1, Object obj2) {
                M m1 = (M)obj1;
                M m2 = (M)obj2;
                return m1.age > m2.age ? 1 : m1.age < m2.age ? -1 : 0;
            }
        });
        ts.add(new M(20));
        ts.add(new M(-6));
        ts.add(new M(0));
        System.out.println(ts);
    }
}

运行结果

[M[age:-6], M[age:0], M[age:20]]

分析:由代码可以看出我们在创建TreeSet集合对象的时候同时创建了一个匿名内部类对象,该对象负责ts对象的排序,但是仍然不能向集合中添加不同类型的元素!

List集合

Collection的介绍中,我使用的是ArrayList,它是List的一个典型的子接口,除了上面讲到的内部方法外,本节我还会使用它来记录List的特有方法。

List中特有的部分方法

Modifier and Type功能
void add(int index, E element)在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection c)将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
E get(int index)返回列表中指定位置的元素。
int indexOf(Object o)返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o)返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
boolean remove(Object o)从此列表中移除第一次出现的指定元素(如果存在)(可选操作)。
E set(int index, E element)用指定元素替换列表中指定位置的元素(可选操作)。
List subList(int fromIndex, int toIndex)返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。

代码示例

import java.util.*;
public class ListDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    public static void main(String[] args) {
        List al = new ArrayList();
        List al1 = new ArrayList();

        //一般添加元素到集合中
        al.add("Hello");
        al.add("world");
        al.add("java");
        al.add("Hello");
        al1.add("Hello");
        al1.add("world");
        al1.add("java");
        print(al);

        // 1、添加元素到指定索引
        al.add(1, "Test");
        print(al);

        // 2、移除指定索引的元素
        al.remove(1);
        print(al);

        // 3、添加集合到指定索引处
        al.add(1, al1);
        print(al);

        // 4、返回指定索引处的元素
        print(al.get(2));

        // 5、返回指定元素第一次出现的位置
        print(al.indexOf(new String("Hello")));

        // 6、返回指定元素最后一次出现的位置
        print(al.lastIndexOf("Hello"));

        // 7、更改指定索引处的元素为指定元素
        al.set(4, "Test");
        print(al);

        // 8、获取列表的子列表
        print(al.subList(1, 3));
    }
}

输出结果

[Hello, world, java, Hello]
[Hello, Test, world, java, Hello]
[Hello, world, java, Hello]
[Hello, [Hello, world, java], world, java, Hello]
world
0
4
[Hello, [Hello, world, java], world, java, Test]
[[Hello, world, java], world]

分析:从代码中我们可以看出,

  • List的方法一般是以其索引来操控指定元素的
  • 集合加入指定索引处后是以整个集合当做一个元素来处理的
  • 代码36行,新建了一个String对象“Hello”能够找到原集合中指定元素的位置,说明他是通过equals()来判断元素是同一个元素的

遍历访问List元素与迭代

在Collection中已经通过三种方式来遍历集合元素,但第三种与第二种遍历其实是相同的。真正的for循环遍历应该是控制它的索引来完成的:

import java.util.*;
public class ListDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    public static void main(String[] args) {
        List al = new ArrayList();
        List al1 = new ArrayList();

        al.add("Hello");
        al.add("world");
        al.add("java");
        for (int i = 0; i < al.size() ; i++ ) {
            print(al.get(i));
        }
    }
}

输出结果

Hello
world
java

另外,我们已经知道集合在使用Iterator接口遍历元素时不能更改元素(除了remove())。因此List接口提供了Iterator的子接口ListIterator接口来增加遍历的功能!

代码示例

import java.util.*;
public class ListDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    public static void main(String[] args) {
        List al = new ArrayList();
        List al1 = new ArrayList();

        al.add("Hello");
        al.add("world");
        al.add("java");
        ListIterator lit = al.listIterator();
        while (lit.hasNext()) {
            Object obj = lit.next();
            print(obj);
            if (obj == "java") {
                lit.add("listIterator");
            }
            if (obj == "world") {
                al.set(0, "test");
            }
        }
        print(al);
        print("-------分割符---------");
        while (lit.hasPrevious()) {
            Object obj = lit.previous();
            print(obj);
        }
    }
}

输出结果

Hello
world
java
[test, world, java, listIterator]
-------分割符---------
listIterator
java
world
test

说明 :从代码中我们可以看出

  • ListIterator能够让集合在遍历时添加、删除或者更改元素
  • ListIterator添加了与next()对应的previous()方法,与hasNext()对应的hasPrevious()方法,来反向遍历!

ArrayList

前面一直使用ArrayList来展示集合,因此大部分功能已经讲完。来说一下它本身。

ArrayList与Vector都是基于数组实现的List类,他们的实例化对象使用initialCapacity参数来设置数组的长度(默认长度为10),当长度超过后就会自动增加长度(ArrayList增加50%,Vector增加100%)。另外,我们也可以使用ensureCapacity(int minCapacity)来手动一次性增加长度,还可以使用trimToSize()调整去除两个集合的多余空间。

LinkedList

LinkedList是一个List集合,因此它可以通过索引来访问,同时它也实现了Deque接口,因此它可以当做双端队列使用。因此一般对LinkedList的操作方法类似于对于堆栈的操作!

代码示例

import java.util.*;
public class LinkedListDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    public static void main(String[] args) {
        LinkedList lld = new LinkedList();
        lld.add("Hello");
        lld.add("world");
        lld.add("java");
        print(lld);

        // 1、将元素元素加入队列的尾部
        lld.offer("test1");
        print(lld);

        // 2、将元素元素加入队列的顶部
        lld.push("lld");
        print(lld);

        // 3、将元素元素加入队列的顶部
        lld.offerFirst("test2");
        print(lld);

        // 4、弹出队列顶部元素(删除)
        print(lld.pop());
        print(lld);

        // 5、访问但不删除队列顶部的元素
        print(lld.peekFirst());
        print(lld);

        // 6、访问但不删除队列底部的元素
        print(lld.peekLast());
        print(lld);

        // 7、删除并访问队列最后一个元素
        print(lld.pollLast());
        print(lld);
    }
}

输出结果

[Hello, world, java]
[Hello, world, java, test1]
[lld, Hello, world, java, test1]
[test2, lld, Hello, world, java, test1]
test2
[lld, Hello, world, java, test1]
lld
[lld, Hello, world, java, test1]
test1
[lld, Hello, world, java, test1]
test1
[lld, Hello, world, java]

Map集合

简单介绍

因为Map集合保存的是具有映射关系的数据,因此它可以类比成一个函数系统f(x),函数系统中保存着两组数值,一组是变量(Map里面的key),一组是函数值(Map里面的value),我们知道在函数系统中变量x是不可相同的,而函数值f(x)可以相同,Map也是如此。

Map的key集存储形式和Set接口完全相同,不可重复,没有顺序。Map中存在一个keySet()方法返回Map里所有key值组成的Set集。

Map的value非常类似于List:元素与元素之间可以重复,元素根据索引来查找,只是索引由一个整数改为对象。

代码示例

import java.util.*;
public class HashMapDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        HashMap hm = new HashMap<>();
        HashMap hm1 = new HashMap<>();
        // 1、向Map中添加映射关系
        hm.put(0, "hello");
        hm.put(11, "world");
        hm.put(2, "java");
        hm.put("test", "java");
        hm.put(5, null);
        print(hm);
        hm1.put(2, "test");
        hm1.put(null, "hashmap");

        // 2、查询Map中是否包含指定的key值
        print(hm.containsKey(11));

        // 3、查询Map中是否包含指定的value值
        print(hm.containsValue("hello"));

        // 4、返回Map中包含key-value对所组成的Set集合
        Set s1 = hm.entrySet();
        print(s1);

        // 5、返回对应key值对应的value,如果没有该key,返回null
        print(hm.get(2));

        // 6、查询Map是否为空
        print(hm.isEmpty());

        // 7、返回所有key值组成的Set集合
        print(hm.keySet());

        // 8、将指定Map中的键值对添加到本Map中
        hm.putAll(hm1);
        print(hm);

        // 9、移除指定key值的键值对
        hm.remove(11);
        print(hm);

        // 10、返回Map的元素个数
        print(hm.size());

        // 11、清除Map中所有键值对
        hm1.clear();
        print(hm1);

        // 12、返回Map中所有values组成的Collection
        print(hm.values());


    }
}

运行结果

{0=hello, 2=java, test=java, 5=null, 11=world}
true
true
[0=hello, 2=java, test=java, 5=null, 11=world]
java
false
[0, 2, test, 5, 11]
{0=hello, null=hashmap, 2=test, test=java, 5=null, 11=world}
{0=hello, null=hashmap, 2=test, test=java, 5=null}
5
{}
[hello, hashmap, test, java, null]

说明 :

  • key或者value都可以是null
  • 如果没有泛型规范,key值可以是任意值
  • Map提供一个Entry内部类来封装key-value对,计算Entry时只需要考虑它封装的key值就行
  • HashMap重写的toString()方法
  • 3中通过equals来判定是否包含指定value值

遍历访问Map

我们可以通过key值当做索引访问每一个键值对

for (Object o : hm.keySet()) {
  print(o + "=" + hm.get(o));
}
for (Iterator it = hm.keySet().iterator(); it.hasNext();) {
  Object o = it.next();
  print(o + "=" + hm.get(o));
}

HashMap与Hashtable

两者都是Map的典型实现类,两者之间的关系可以类比于ArrayList与Vector的关系:

  • Hashtable很古老,从JDK1.0就存在了,它包括了两个操控elements的古老方法
  • Hashtable是一个线程安全的Map实现,且不允许使用null作为key和value
  • HashMap线程不安全,但其效率更高一点

我们应该尽量不使用Hashtable。

如果我们使用自定义类作为key值,那我们重写该类的equals与hashCode时应该保证其判断标准一致:equals输出为true时hashCode也是一样的。

LinkedHashMap实现类就像是Set中的LinkedHashSet一样可以记住元素的存放顺序,其他属性可以类比LinkedHashSet。

SortedMap接口与TreeMap实现类

TreeMap

正如Sort接口->SortedSet子接口->TreeSet实现类,Map->SortedMap子接口->TreeMap实现类。

TreeMap通过红黑树数据结构根据key对节点进行排序,保证所有的key-value处于有序状态。TreeMap有两种排序方式

  • 自然排序:所有的key都必须实现了Comparator接口,而且所有key应该是同一个类的对象,否则会抛出ClassCastException异常!
  • 定制排序:创建TreeMap时传入一个Comparator对象,该对象负责对TreeMap进行key排序,此时不要求Map的key值实现了Comparator接口。
  • 使用自定义的类当做key时必须保证equals(true/false)返回的值与compareTo(0/非0)相同

根据key值访问TreeMap的键值对:

代码实例

import java.util.*;
public class TreeMapDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        TreeMap tm = new TreeMap();

        tm.put(1, "hello");
        tm.put(21, "java");
        tm.put(5, "world");
        tm.put(2, "test");

        // 1、返回该Map中最小key对应的key-value对,如果为空,返回null
        print(tm.firstEntry());

        // 2、返回Map中最小key值
        print(tm.firstKey());

        // 3、返回该Map中最大key对应的key-value对,如果为空,返回null
        print(tm.lastEntry());

        // 4、返回Map中最大key值
        print(tm.lastKey());

        // 5、返回大于指定key值的最小key的值
        print(tm.higherKey(2));

        // 6、返回大于指定key值的最小key值所在的键值对
        print(tm.higherEntry(2));

        // 7、返回小于指定key值的最大key的值
        print(tm.lowerKey(5));

        // 8、返回小于指定key值的最大key值所在的键值对
        print(tm.lowerEntry(5));

        // 9、返回Map的子Map,默认左闭右开
        print(tm.subMap(2, 21));

        // 10、返回Map的子Map,可以指定左右是否闭
        print(tm.subMap(2, true, 21, true));

        // 11、返回大于(包括)指定key值的所有key值所在键值对组成的子Map
        print(tm.tailMap(2));

        // 12、返回大于(是否包括取决于第二个参数)指定key值的所有key值所在键值对组成的子Map
        print(tm.tailMap(2, false));

        // 13、返回小于(不包括包括)指定key值的所有key值所在键值对组成的子Map
        print(tm.headMap(5));

        // 14、返回小于(是否包括取决于第二个参数)指定key值的所有key值所在键值对组成的子Map
        print(tm.headMap(5, true));
    }
}

运行结果

1=hello
1
21=java
21
5
5=world
2
2=test
{2=test, 5=world}
{2=test, 5=world, 21=java}
{2=test, 5=world, 21=java}
{5=world, 21=java}
{1=hello, 2=test}
{1=hello, 2=test, 5=world}

分析 :这和TreeSet的操作真的很像!

工具类Collections

Collections是Java中提供了操作Set、List、Map等集合的工具类,我们可以使用它来对集合元素进行排序、查询、修改等。

针对List集合的用法:

代码实例

import java.util.*;
public class ListTest {

    public static void print(Object obj) {
        System.out.println(obj);
    }

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList al = new ArrayList();
        al.add(-2);
        al.add(0);
        al.add(2);
        al.add(5);
        al.add(10);
        al.add(-6);
        print("原排序" + al);

        // 1、翻转集合元素次序
        Collections.reverse(al);
        print("翻转次序:" + al);

        // 2、将list集合元素按照随机排序排序
        Collections.shuffle(al);
        print("随机排序:" + al);

        // 3、将list集合元素按自然排序
        Collections.sort(al);
        print("自然排序:" + al);


        // 4、将指定    的两个位置的元素相互交换
        Collections.swap(al, 1, 4);
        print("交换1,4位置:" + al);

        //5、移动后面指定个数的元素到集合前面(+),移动前面指定个数的元素到集合后面(-),
        Collections.rotate(al, 2);
        print("移后到前2:" + al);

        Collections.rotate(al, -3);
        print("移前到后3:" + al);
    }
}

运行结果:

原排序[-2, 0, 2, 5, 10, -6]
翻转次序:[-6, 10, 5, 2, 0, -2]
随机排序:[-2, 10, 2, -6, 5, 0]
自然排序:[-6, -2, 0, 2, 5, 10]
交换1,4位置:[-6, 5, 0, 2, -2, 10]
移后到前2:[-2, 10, -6, 5, 0, 2]
移前到后3:[5, 0, 2, -2, 10, -6]

说明:sort还有一个方法static void sort(List list,Comparator c)根据指定的Comparator产生顺序对list集合元素排序。

除了上面的一些操作,Collections还提供了一些查找、替换集合元素的常见方法

代码实例

import java.util.*;
public class CollectionsDemo {
    public static void print(Object obj) {
        System.out.println(obj);
    }

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        ArrayList al = new ArrayList();
        ArrayList al1 = new ArrayList();
        al.add(-2);
        al.add(-3);
        al.add(0);
        al.add(12);
        al.add(-3);

        al1.add(12);
        al1.add(-3);
        print("原集合:" + al);

        // 1、查找指定元素的索引位置(只适用于List列表)
        print("列表中0所在的索引:" + Collections.binarySearch(al, 0));

        // 2、第一次出现的位置
        print(Collections.indexOfSubList(al, al1));

        // 3、最后一次出现的位置
        print(Collections.lastIndexOfSubList(al, al1));

        // 4、查找集合中最大的元素
        print("集合中的最大元素:" + Collections.max(al));

        // 5、查找集合中最小的元素
        print("集合中的最小元素:" + Collections.min(al));

        // 6、返回指定元素出现的次数
        print("元素-3出现的次数:" + Collections.frequency(al, -3));

        // 7、使用新值替换旧值
        print("用-1替换-3:" + Collections.replaceAll(al, -3, -1));

        // 8、使用指定元素替换List所有元素
        Collections.fill(al, "hello");
        print("使用hello替换list所有元素:" + al);
    }
}

运行结果

原集合:[-2, -3, 0, 12, -3]
列表中0所在的索引:2
3
3
集合中的最大元素:12
集合中的最小元素:-3
元素-3出现的次数:2
用-1替换-3true
使用hello替换list所有元素:[hello, hello, hello, hello, hello]

同步控制

因为集合框架中的实现类HashSet、TreeSet、ArrayList、ArrayDeque、LinkedList、HashMap、TreeMap都是线程不安全的,所以Java在Collections中引入了同步控制来保证线程安全。

代码实例

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

说明 :以上代码将会创建对应的线层同步的集合

设置不可变集合

我们一般使用的集合都是可以随时添加元素的,这在某些场合不太安全,因此创建一些不可变的集合至关重要。Collections中提供了三种方法来解决这一问题。

  • emptyXxx():返回一个空的、不可变的集合对象
  • singletonXxx():返回一个只有包含指定对象(一个元素)的、不可变的对象
  • unmodifiableXxx():返回指定集合对象的不可变视图

这三个方法都对List、Set和Map有效。

代码实例

import java.util.*;
public class UnmodifiableTest {
    public static void main(String[] args) {
        List unmodifiableList = Collections.emptyList();
        Set unmodifiableSet = Collections.singleton("hello");
        Map hp = new HashMap();
        hp.put(1, "hello");
        hp.put(2, "java");
        Map unmodifiableMap = Collections.unmodifiableMap(hp);
        unmodifiableList.add("world");
        unmodifiableSet.add("world");
        unmodifiableMap.put(3, "test");
    }
}

运行结果

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)
    at UnmodifiableTest.main(UnmodifiableTest.java:10)

说明

我们在编译时不会出现错误,但是运行时将会出现以上错误,这是因为三个集合都是不可变的,如果我们试图向其中加入其他元素,将会报错!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值