Java 对象集合

java 对象集合

1.JDK中对象集合和相关接口

JDk中主要集合类型主要分为以下四种;

  • set:无序不可重复集合
  • list:有序可重复集合
  • map:具有映射关系集合
  • queue:具有队列性质集合

集合相关接口和类:

Java 对象集合_第1张图片

Collection是JDK中集合类型上层接口,很多相关接口和集合类都派生自它。

对象集合的一个限制:Java集合不能保存原始数据类型的数据,传入原始数据类型将转换为包装类

集合主要操作:

  • 遍历集合中元素
  • 按照内容在集合中查找元素
  • 向集合中插入或删除元素

遍历操作:

1.使用迭代器对象(Iterator)

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class TestMain {

    public static void main(String[] args) {
        Collection books = new HashSet();
        books.add("One book");
        books.add("Two book");
        books.add("Three book");
        //获取books集合对应的迭代器
        Iterator it = books.iterator();
        while(it.hasNext())
        {
            String book = (String)it.next();
            System.out.println(book);
        }
        System.out.println("集合中的元素为:"+books);
    }
}

Iterator接口定义了三个方法:

public Iterator<E> {
    boolean hasNext(); // return true if the iteration has more elements
    E next(); // return the next element in the iteration
    void remove(); // remove the last element returned by the iterator
} 

所有实现了Collection接口的集合对象,都提供了一个iterator()方法,因此可以像上述方式遍历

如果非要在遍历时非要删除集合中的元素,则必须通过迭代器对象的remove方法,而不能通过集合 对象直接删除

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class TestMain {

    public static void main(String[] args) {
        //创建一个集合
        Collection books = new HashSet();
        books.add("One book");
        books.add("Two book");
        books.add("Three book");
        //获取books集合对应的迭代器
        Iterator it = books.iterator();
        while(it.hasNext())
        {
            String book = (String)it.next();
            System.out.println(book);
            if (book.equals("Three book"))
            {
                //使用Iterator迭代过程中,通过它来移除集合元素是安全的
                it.remove();//是it.remove()不是books.remove()
            }
        }
        System.out.println("移除元素之后:"+books);
    }
}

2.foreach:

这个比较简单,但要注意当使用foreach循环遍历集合时,注意不要再向集合中追加或删除元素,否则,会引发 ConcurrentModificationException

2.list和queue

List可以看成是一种动态改变大小的数组,接口方法如下:

Java 对象集合_第2张图片

注意,List只是一个接口,并不是类!!!

List为变量名,其真实对象需要是实现该接口的子类如ArrayList

ArrayList:可以看成一个动态数组

除了ArrayList,JDK中还有另一个类Vector同样实现了List接口,两者功能基本一样,但前者不是线程安全的。

下面实例以List接口变量名ArrayList为真实对象:

import java.util.ArrayList;
import java.util.List;

public class TestMain {

    public static void main(String[] args) {
        List books = new ArrayList();
        //向books集合中添加三个元素
        books.add(new String("One book"));
        books.add(new String("Two book"));
        books.add(new String("Three book"));
        System.out.println(books);
        //将新字符串对象插入在第二个位置
        books.add(1 , new String("new book"));
        for (int i = 0 ; i < books.size() ; i++ )
        {
            System.out.println(books.get(i));
        }
        //删除第三个元素
        books.remove(2);
        System.out.println(books);
        //判断指定元素在List集合中位置:输出1,表明位于第二位
        System.out.println(books.indexOf(new String("new book")));
        //将第二个元素替换成新的字符串对象
        books.set(1, new String("new book2"));
        System.out.println(books);
        //将books集合的第二个元素(包括)到第三个元素(不包括)截取子集合
        System.out.println(books.subList(1 , 2));
    }
}

List中的indexOf(Object o)和lastIndexOf(Object o)方法:传入的类需要实现equal方法,即需要能够判断两个对象的内容是否完全一致。

固定大小List:定义于Arrays类的内部类ArrayList,是一个固定长度的List集合,只能遍历访问,不能增加和删除元素,否则,会引发 UnsupportedOperationException。

Stack:JDK中提供了现成的堆栈类——Stack,注意它派生自Vector,所以它是线程安全的。

import java.util.Stack;

public class TestMain {

    public static void main(String[] args) {
        Stack names = new Stack<>();
        names.push("Raymond");
        names.push("David");
        System.out.println("Top of stack: " + names.peek());
        names.pop();
        System.out.println("Top of stack: " + names.peek());
        names.push("Cynthia");
        System.out.println("Top of stack: " + names.peek());
        //如果堆栈为空,再次pop会抛出EmptyStackException
        if (!names.empty()) {
           names.pop();
        }
        System.out.println("Top of stack: " + names.peek());
        names.pop();
        if (!names.empty()) {
           System.out.println("Top of stack: " + names.peek());
        } else {
           System.out.println("Stack empty.");
        }
    }
}

Vector更多参考:https://www.cnblogs.com/skywang12345/p/3308833.html

LinkedList更多参考:https://www.cnblogs.com/skywang12345/p/3308807.html

Queue接口代表的是“经典”的队列,JDK中还扩充定义了另一个Deque接口,代表“双向队列”,用途更广。

Java 对象集合_第3张图片

PriorityQueue:非线程安全,无容量限制。允许元素重复。其顺序由元素自己(如果实现了Comparable接口)或 Compartor提供,会将它所包容的元素自动排序。

import java.util.PriorityQueue;

public class TestMain {

    public static void main(String[] args) {
        PriorityQueue pq = new PriorityQueue();
        //下面代码依次向pq中加入四个元素
        pq.offer(6);
        pq.offer(-3);
        pq.offer(9);
        pq.offer(0);
        pq.offer(110);
        //输出pq队列,并不是按元素的加入顺序排列,而是按元素的二叉树顺序排列
        System.out.println(pq);
        //访问队列第一个元素,其实就是队列中最小的元素:-3
        System.out.println(pq.peek());

        pq.poll();
        System.out.println("\n 移除队列中的head元素之后");
        System.out.println(pq);
        pq.remove(9);
        System.out.println("\n 移除队列中的元素9之后");
        System.out.println(pq);
    }
}

CurrentLinkedQueue:这是一个线程安全的(FIFO:First In First Out)的容量不限的队列。其内部使用一种基于 “CAS(Compare and Swap)”的算法,能保证访问它的线程总能完 成它要执行的操作而不会被提前中断

BlockingQueue接口:此接口派生自Queue,最适合于实现生产者-消费者问题

Deque接口:双向队列。既像Queue,又像Stack。

3.Map

Java 对象集合_第4张图片

  • Map集合给每个元素定义了一个Key,通过Key来访问对象
  • 要求每个对象的Key应该不一样,否则,会带来混乱

Map接口成员:

Java 对象集合_第5张图片

HashMap:

  • 使用put(Key,Value)方法追加元素,使用get(Key)方法提取元素
  • 通过HashMap对象的keySet()方法引用其Key的集合,通过其values()方法引用其Value的集合。Key和Value之间的对应关系,是通过HashMap内部的一个Map.Entry对象集合实现的,HashMap对象的entrySet方法引用这一对象集合

HashMap简单使用(遍历):

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class TestMain {

    public static void main(String[] args) {
        HashMap hm = new HashMap<>();
        for (int i = 1; i < 10; i++) {
            hm.put("Key" + i, i);
        }
        // 使用KeySet遍历集合
        Set keySet = hm.keySet();

        for (String key : keySet) {

            System.out.println(key + ":" + hm.get(key));
            // 循环遍历过程中,不允许移除元素,否则,引发ConcurrentModificationException
        }

        System.out.println("\n使用Iterator遍历并移除元素");
        Set.Entry> entrySet = hm.entrySet();
        Iterator.Entry> itor = entrySet.iterator();
        while (itor.hasNext()) {
            Map.Entry entry = itor.next();
            if(entry.getValue() % 2!=0){
                itor.remove();
                System.out.println(entry.getKey() + ":" + entry.getValue()+" is removed");
            }
            else{
                System.out.println(entry.getKey() + ":" + entry.getValue());    
            }
        }
    }
}

HashMap可以使用null作为key,且只会有一个null作key的键值对,多的会覆盖掉。

HashTable:HashMap和HashTable两者功能基本一样,但HashTable是线程安全的

Map集合对Key要求:

  • 用作Key的对象必须覆盖hashCode和equals方法
  • Key相等:两个对象的hashCode一致,并且equals方法返回true
  • Value相等:equals方法返回true

上述特性对HashTable的containsValue()方法返回值的影响如实例:

import java.util.*;

public class TestMain {

    public static void main(String[] args) {
        Hashtable ht = new Hashtable();
        ht.put(new MyKey(60000) , "key值为60000所对应的Value");
        ht.put(new MyKey(87563) , "Key值为87563所对应的Value");
        ht.put(new MyKey(1232) , new B());
        System.out.println(ht);
        //只要两个对象通过equals比较返回true,Hashtable就认为它们是相等的value。
        //因为Hashtable中有一个B对象,它与任何对象通过equals比较都相等,所以下面输出true。
        System.out.println(ht.containsValue("测试字符串"));
        //只要两个A对象的count属性相等,它们通过equals比较返回true,且hashCode相等
        //Hashtable即认为它们是相同的key,所以下面输出true。
        System.out.println(ht.containsKey(new MyKey(87563)));
        //下面语句可以删除最后一个key-value对
        ht.remove(new MyKey(1232));
        for (Object key : ht.keySet())
        {
            System.out.print(key + "---->");
            System.out.print(ht.get(key) + "\n");
        }
    }
}
class MyKey
{
    int value;
    public MyKey(int count)
    {
        this.value = count;
    }
    public boolean equals(Object obj)
    {
        if (obj == this)
        {
            return true;
        }
        if (obj != null &&  obj.getClass() == MyKey.class)
        {
            MyKey a = (MyKey)obj;
            if (this.value == a.value)
            {
                return true;
            }
        }
        return false;
    }
    public int hashCode()
    {
        return this.value;
    }
    public String toString(){
        return "MyKey:"+this.value;
    }
}
class B
{
    public boolean equals(Object obj)
    {
        return true;
    }
}

Properties类:派生自HashTable,可以方便地处理“属性-值”对,并且可以很方便地将其保存到文件中

NavigableMap接口:,保证在遍历集合时,一定是按照升序进行的

实现NavigableMap接口最常用的类型是 TreeMap,它的用法与标准的Map没什么两样

TreeMap的独特之处在于:它内部基于红黑树 (red-black tree)对Key进行排序。因此,它的元素是“有序”的。

import java.util.*;

public class TestMain {

    public static void main(String[] args) {
        TreeMap tm = new TreeMap();
        tm.put(new R(3) , "Key值为3的value");
        tm.put(new R(-5) , "Key值为-5的value");
        tm.put(new R(9) , "Key值为9的value");
        System.out.println("TreeMap集合中的所有元素:\n"+tm);
        //返回该TreeMap的第一个Entry对象
        System.out.println("\n第一个Entry对象:"+tm.firstEntry());
        //返回该TreeMap的最后一个key值
        System.out.println("最后一个key值:"+tm.lastKey());
        //返回该TreeMap的比new R(2)大的最小key值。
        System.out.println("比new R(2)大的最小key值:"+tm.higherKey(new R(2)));
        //返回该TreeMap的比new R(2)小的最大的key-value对。
        System.out.println("比new R(2)小的最大的key-value对:"+tm.lowerEntry(new R(2)));
        //返回该TreeMap的子TreeMap
        System.out.println("该TreeMap的子TreeMap:"+tm.subMap(new R(-1) , new R(4)));
    }
}

//R类,重写了equals方法,如果value属性相等返回true
//重写了compareTo(Object obj)方法,如果value属性相等返回0;
class R implements Comparable
{
    int value;
    public R(int value)
    {
        this.value = value;
    }
    public String toString()
    {
        return "R(value属性:" + value + ")";
    }
    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj != null && obj.getClass() == R.class)
        {
            R r = (R)obj;
            if (r.value == this.value)
            {
                return true;
            }
        }
        return false;
    }
    public int compareTo(Object obj)
    {
        R r = (R)obj;
        if (this.value > r.value)
        {
            return 1;
        }
        else if (this.value == r.value)
        {
            return 0;
        }
        else
        {
            return -1;
        }
    }
}

JDK中提供了以下Map类型实现多线程环境下的安全访问:

ConcurrentHashMap
ConcurrentNavigableMap
ConcurrentSkipListMap 

4.Set

Set也是一种Collection,其中不允许有重复元素。Set通过调用对象的equals方法确定两个对象是否相同。

HashSet原理:

  • HashSet将对象的哈希码作为访问对象 的“索引(index)”。
  • 当向HashSet存入一个对象时,它会同时调用hashCode()和equals()两个方法确定此对象是否己在集合中,如果发现己经有了,则不会把此对象加入到集合中
  • HashSet中每个存储元素的“Slot(槽)”通常被称为“bucket”,如果有多个元素的hashCode相等,但它们 equals()方法返回False,这就导致同一个槽中放置多个元素,这会导致性能下降
  • HashSet在内部包容一个HashMap,以对象的 hashCode作为这一Map的Key
import java.util.*;

public class TestMain {

    public static void main(String[] args) {
        HashSet books = new HashSet();
        //分别向books集合中添加2个A对象,2个B对象,2个C对象
        books.add(new A());
        books.add(new A());//hash值不同,故可以添加,位于不同槽
        books.add(new B());
        books.add(new B());//hash值相同,但equal返回false,可添加位于同一个槽
        books.add(new C());
        books.add(new C());//hash值相同,equal返回true,故不可再添加
        //输出:[B@1, B@1, C@2, A@15db9742, A@6d06d69c]
        System.out.println(books);
    }
}
//类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的hashCode()方法总是返回2
class C
{
    public int hashCode()
    {
        return 2;
    }
    public boolean equals(Object obj)
    {
        return true;
    }
}

使用HashSet集合(TreeSet类似)保存对象时要注意,尽量不要修改对象的属性值,否则,可能会出现很奇怪的现象。推荐在HastSet中保存不可变类的实例。

LinkedHashSet:派生自HashSet,也是按照对象的hashCode来保存,但它同时使用双向链表来维护对象的顺序,当遍历LinkedHashSet时,将按插入顺序来显示集合中的对象

SortedSet接口:SortedSet保证总是按照升序方式访问集合中的数据。使用SortedSet的一个最大好处是:如果合并两个这样的集合,合并之后,集合仍然是有序的。JDK中的TreeSet实现了SortedSet接口。

TreeSet:

  • 在任何时刻访问TreeSet时,总能得到一个”己经排好序的对象集合”,TreeSet内部调用compareTo(Object obj)方法来比较集合 中各个对象的大小,然后在内部使用红黑树进行排序
  • TreeSet要求其元素必须实现Comparable接口,并且应该是同一个类型的,否则,将引发ClassCastException
import java.util.*;

public class TestMain {

    public static void main(String[] args) {
        TreeSet nums = new TreeSet();
        //向TreeSet中添加四个Integer对象
        nums.add(5);
        nums.add(2);
        nums.add(10);
        nums.add(-9);
        //输出集合元素,看到集合元素已经处于排序状态
        System.out.println("集合元素:"+nums);
        //输出集合里的第一个元素
        System.out.println("集合里的第一个元素:"+nums.first());
        //输出集合里的最后一个元素
        System.out.println("集合里的最后一个元素:"+nums.last());
        //可以输出指定元素的“前一个”和“后一个”
        System.out.println("10之前的那个元素是:"+nums.lower(10));
        System.out.println("10之后的那个元素是:"+nums.higher(10));
        //返回小于4的子集,不包含4
        System.out.println("返回小于4的子集,不包含4:"+nums.headSet(4));
        //返回大于5的子集,如果Set中包含5,子集中还包含5
        System.out.println("返回大于5的子集:"+nums.tailSet(5));
        //返回大于等于-3,小于4的子集。
        System.out.println("返回大于等于-3,小于4的子集:"+nums.subSet(-3 , 4));
    }
}

参考:

金旭亮Java编程系列(大部分内容摘自该教程)

你可能感兴趣的:(Java)