Java 类集框架

来源:互联网 发布:淘宝卖家如何借贷 编辑:程序博客网 时间:2024/05/17 19:17

概念

类集是一种动态的对象数组,属于各个数据结构的实现类,整个类集的主要组成是一些核心的操作接口:Collection,List,Set,Map,Iterator和Enumeration.以下是部分类集框架的继承结构图:

这里写图片描述

这里写图片描述

注:CollectionMap

单值保存

Collection接口

所谓单值保存指的是每一次操作只会保存一个对象.单值保存的最大父类是Collection.在Collection接口中定义了如下的一些常用方法:

No. 方法 描述 1 public boolean add(E e) 添加一个数据 2 public void clear() 清除集合中所有数据 3 public boolean contains(Object o) 查找数据是否存在 4 public boolean isEmpty() 判断集合是否为空 5 public Iterator iterator() 为Iterator接口实例化 6 public boolean remove(Object o) 删除指定数据 7 public int size() 取得集合中数据的个数 8 public Object [] toArray() 将集合变为对象数组

一般在开发中很少会直接使用Collection接口,一般会使用它的两个子接口:List和Set.

List接口

List接口是Collection的一个最为常用的允许重复的子接口,此接口的定义如下:

public interface List<E>extends Collection<E>

虽然List接口直接继承了Collection接口,但是List接口对Collection接口进行了大量的扩充,扩充后的主要方法如下:

No. 方法 描述 1 public E get(int index) 取得索引位置上的数据 2 public E set(int index, E element) 修改指定索引位置上的数据 3 public ListIterator listIterator() 为ListIterator接口实例化

List接口有两个常用的子类:

  • ArrayList
  • Vector

ArrayList

ArrayList是List接口中使用最多的一个子类.按照面向对象的概念,使用ArrayList的主要目的是为List接口实例化,而所有的操作方法都以List接口中定义的为主.

代码示例

import java.util.List;import java.util.ArrayList;public class TestDemo{    public static void main(String [] args){        // 实例化List接口        List<String> list = new ArrayList<String>();        // 往集合中添加数据        list.add("Hello");        list.add("World");        list.add("Hello");        System.out.println(list);    }}

程序运行结果
[Hello, World, Hello]


通过本程序可以发现,List集合中即使存在了重复数据,也可以正常保存,而且数据保存的顺序是存入数据的顺序.

Vector

Vector类是在JDK1.0时就推出的一个实现动态数组的操作类,相对于ArrayList是一个旧的子类.与ArrayList的使用类似.
代码示例

import java.util.List;import java.util.ArrayList;public class TestDemo{    public static void main(String [] args){        // 实例化List接口        List<String> list = new Vector<String>();        // 往集合中添加数据        list.add("Hello");        list.add("World");        list.add("Hello");        System.out.println(list);    }}

程序运行结果
[Hello, World, Hello]


本程序的运行结果和使用ArratLis子类的程序没有任何区别,唯一不同的地方就是使用Vector子类完成List接口的实例化,但是由于所有的操作都是针对接口完成的,只要接口定义的方法不变,子类可以随意更改.

ArrayList和Vector的区别

No. 区别 ArrayList Vector 1 推出时间 JDK1.2 JDK1.0 2 性能 采用异步处理方式,性能更高 采用同步处理方式,性能相对较低 3 安全性 非线程安全 线程安全 4 输出方式 Iterator,ListIterator,foreach Iterator,ListIterator,foreach,Enumeration

Set接口

Set接口也是Collection接口常用的不允许重复的子接口,此接口的定义如下:

public interface Set<E> extends Collection<E>

Set接口也是直接继承了Collection接口,但是与List接口不同.Set接口只是完整的继承了Collection接口,而没有进行任何方法的扩充.所以Set子类接口中肯定无法使用get()方法取得指定索引的数据.Set接口常用的两个子类:

  • HahSet
  • TreeSet

散列排放的子类

HashSet使用一种散列(无序)的方式保存集合数据.
代码示例

import java.util.HashSet;import java.util.Set;public class TestDemo{    public static void main(String [] args){        // 实例化Set接口        Set<String> set = new HashSet<String>();        // 往set中添加数据        set.add("January");        set.add("February");        set.add("March");        set.add("February");        System.out.println(set);    }}

程序运行结果
[March, January, February]


通过本程序可以发现:使用Set集合保存数据时,集合还总重复的数据并没有被保存,并且保存的数据也是无序的(不是按输入顺序保存)

排序排放的子类

HashSet使用一种排序的方式保存集合数据.
代码示例

import java.util.Set;import java.util.TreeSet;public class TestDemo{    public static void main(String [] args){        // 实例化Set接口        Set<String> set = new TreeSet<String>();        // 往set中添加数据        set.add("January");        set.add("February");        set.add("March");        set.add("February");        System.out.println(set);    }}

程序运行结果
[February, January, March]


此时程序只是更换一个Set接口的子类,运行之后,集合中没有重复数据,并且按照数据的大小排序.

重复元素和TreeSet排序的说明
  • 在使用TreeSet实例化Set接口时保存自定义类的对象数据时,要正确排序自定义对象的大小,那么对象所在的类必须实现Comparable接口,设置比较规则.需要注意的是:使Comparable
    代码示例
import java.util.Set;import java.util.TreeSet;class Person implements Comparable<Person>{    private String name;    private int age;    public Person(){}    public Person(String name, int age){        this.name = name;        this.age = age;    }    @Override    public String toString(){        return "姓名:" + this.name + ",年龄:" + this.age + "\n";    }    @Override    public int compareTo(Person per){        if(this.age != per.age){            return per.age > this.age?1:0;        }        else{            if(!per.name.equals(this.name)){                return per.name.compareTo(this.name);            }            else{                return 0;            }        }    }}public class TestDemo{    public static void main(String [] args){        Set<Person> set = new TreeSet<Person>();        set.add(new Person("张三", 20));        set.add(new Person("王五", 19));        set.add(new Person("李四", 20));        set.add(new Person("赵六", 18));        set.add(new Person("张三", 20));        System.out.println(set);    }}

程序运行结果
[姓名:李四,年龄:20
, 姓名:张三,年龄:20
, 姓名:王五,年龄:19
, 姓名:赵六,年龄:18
]

在此程序中,Person类实现了Comparable接口,所以Set集合中可以正确的进行排序(由年龄从大到小排序,年龄相同再根据姓名排序),而对于重复的数据,由于通过compareTo()方法比较后的结果为0,所以就不再进行保存.所以有一个结论:TreeSetComparablecompareTo()

  • 虽然TreeSet可以依靠Comparable进行重复元素判断,但是HashSet子类却无法依靠Comparable接口进行重复元素判断.实际上所有重复元素的判断依赖于Object类的两个方法.
     1. hash码:public int hashCode();
     2. 对象比较:public boolean equals(Object obj).
     这两个方法可以由Eclipse自动生成.
     在进行对象的比较过程中,首先会使用hasCode()与已经保存在集合中的对象的hashCode()进行比较,如果代码相同,则在与equals()方法进行属性的依次判断,如果全部相同,则为相同元素.
     代码示例
     
    没有实现equals()和hashCode()方法的覆写
import java.util.HashSet;import java.util.Set;class Person{    private String name;    private int age;    public Person(String name, int age){        this.name = name;        this.age = age;    }    @Override    public String toString(){        return "姓名:" + this.name + ", 年龄:" + this.age + "\n";    }}public class TestDemo{    public static void main(String [] args){        Set<Person> set = new HashSet<Person>();        set.add(new Person("张三", 20));        set.add(new Person("李四", 21));        set.add(new Person("王五", 18));        set.add(new Person("张三", 20));        System.out.println(set);    }}

 

程序运行结果
[姓名:王五, 年龄:18
, 姓名:张三, 年龄:20
, 姓名:李四, 年龄:21
, 姓名:张三, 年龄:20
]


此程序没有实现equals()和hashCode()方法的覆写,在HashSet集合中出现重复数据.

实现equals()和hashCode()方法的覆写
 

 import java.util.HashSet;import java.util.Set;class Person{    private String name;    private int age;    public Person(String name, int age){        this.name = name;        this.age = age;    }    @Override    public String toString(){        return "姓名:" + this.name + ", 年龄:" + this.age + "\n";    }    @Override    public int hashCode() {        final int prime = 31;        int result = 1;        result = prime * result + age;        result = prime * result + ((name == null) ? 0 : name.hashCode());        return result;    }    @Override    public boolean equals(Object obj) {        if (this == obj)            return true;        if (obj == null)            return false;        if (getClass() != obj.getClass())            return false;        Person other = (Person) obj;        if (age != other.age)            return false;        if (name == null) {            if (other.name != null)                return false;        } else if (!name.equals(other.name))            return false;        return true;    }}public class TestDemo{    public static void main(String [] args){        Set<Person> set = new HashSet<Person>();        set.add(new Person("张三", 20));        set.add(new Person("李四", 21));        set.add(new Person("王五", 18));        set.add(new Person("张三", 20));        System.out.println(set);    }}

程序运行结果
[姓名:李四, 年龄:21
, 姓名:王五, 年龄:18
, 姓名:张三, 年龄:20
]

此程序利用了Object类的equals()和hashCode()方法真正实现了重复元素的判断.

偶对象保存

偶对象指的是一对对象,即两个对象同时保存.这两个对象是按照key = value的形式进行定义的,即可以通过key找到对应的value数据.Map接口就实现这样一个操作的数据结构.

Map接口

Map接口中定义的常用方法

No. 方法 描述 1 public V put(K key, V value) 向集合中保存数据 2 public V get(Object o) 通过指定的key取得对应的value 3 public SetkeySet() 将Map中的所有key以Set集合的方式返回 4 public Set

新的子类

代码示例

import java.util.Map;import java.util.Set;import java.util.HashMap;public class TestDemo{    public static void main(String [] args){        Map<String, Integer> map = new HashMap<String, Integer>();        map.put("张三", 19);          // 保存数据        map.put(null,  null);       //key为null        map.put("张三", 18);          // key重复,value会被新值覆盖        map.put("李四", 19);          // 保存数据        map.put("王五", 16);          // 保存数据        map.put("赵六", 13);          // 保存数据        System.out.println(map.get("张三")); // 取得指定key的数据        Set<String> set = map.keySet();     // 将Map中的所有key以Set集合的方式返回        System.out.println(set);            // 输出Set中所有的key值    }}

程序运行接结果
18
[null, 李四, 张三, 王五, 赵六]


本程序使用Map保存数据时设了两个内容(key, value),然后使用get()方法根据指定的key取得对应的value,而且可以发现Map集合中的key不允许重复,若key有重复,其对应的value值会被新值覆盖.接着使用keySet()方法将Map中的所有key以Set的方式返回,并打印.

旧的子类:Hashtable

HashTable是JDK1.0时推出的一种数据结构,相对于HashMap来说是一个比较旧的子类,使用上极为相似,但也存在着区别.

代码示例

import java.util.Map;import java.util.Set;import java.util.Hashtable;public class TestDemo{    public static void main(String [] args){        Map<String, Integer> map = new Hashtable<String, Integer>();        map.put("张三", 19);          // 保存数据        map.put("张三", 18);          // key重复,value会被新值覆盖        map.put("李四", 19);          // 保存数据        map.put("王五", 16);          // 保存数据        map.put("赵六", 13);          // 保存数据        System.out.println(map.get("张三")); // 取得指定key的数据        Set<String> set = map.keySet();     // 将Map中的所有key以Set集合的方式返回        System.out.println(set);            // 输出Set中所有的key值    }}

程序运行结果
18
[赵六, 王五, 张三, 李四]


本程序只是将实例化Map接口的子类替换成了Hashtable类,和将Key值为null 的添加语句删除(Hashtable不能设置key为null)否则运行时会出现”NullPointorException”

HashMap和Hashtable的区别

No. 区别 HashMap Hashtable 1 推出时间 JDK1.2 JDK1.0 2 性能 采用异步处理方式,性能更高 采用同步处理方式,性能相对较低 3 安全性 非线程安全 线程安全 4 设置null 允许将key或value设置为null 不允许出现null,否则会出现空指针异常

集合的输出

Java的类集框架中给出了4中输出方式:
- Iterator在Collection接口中定义
- ListIterator在List接口中定义
- Enumeration在Vector子类中定义
- foreachJDK1.5的支持

迭代输出

Iterator是专门迭代输出的接口,所谓的迭代输出就是对元素逐个进行输出,判断其是否有内容,如果有内容则把内容取出.
取得Iterator接口的实例化对象的方法:这一操作在Collection接口中已经明确定义,因为Collection继承了一个Iterator接口,在这个Iterator接口中定义了一个方法”Iterator<E> iterator()“,所以一般情况下会很少关注Iterator接口,直接使用Collection接口定义的Iterator<E> iterator()方法即可.
Iterator接口中常用方法

No. 方法 描述 1 public boolean hasNext() 判断是否有下一个值 2 public E next() 取出当前元素 3 public void remove() 移除当前元素

代码示例

import java.util.List;import java.util.Iterator;import java.util.ArrayList;public class TestDemo{    public static void main(String [] args){        List<String> list = new ArrayList<String>();        list.add("Hello");      // 添加数据        list.add("World");        list.add("Hello");        // 使用Iterator输出是数据        Iterator<String> iterator = list.iterator();        while(iterator.hasNext()){            System.out.println(iterator.next());        }    }}

程序运行结果
Hello
World
Hello


此程序利用了Iterator接口进行了输出,而对于Collection的所有子接口,都会存在iterator()方法,即Collection接口的所有子接口都支持Iterator接口输出.

双向迭代输出:ListIterator

Iterator接口可以完成由前向后的单向输出操作,要想完成由前向后和由后向前输出需要由Iterator的子接口ListIterator,ListIterator接口主要扩充了以下两个方法:

  1. public boolean hasPrevious() : 判断是否由前一个元素
  2. public E previous() : 取出前一个元素

获得ListIterator实例化对象的方法:使用List接口中定义的方法:

public ListIterator<E> listIterator()

代码示例

import java.util.List;import java.util.ArrayList;import java.util.ListIterator;public class TestDemo{    public static void main(String [] args){        // 使用ArrayList子类实例化List接口        List<String> list = new ArrayList<String>();        // 添加数据        list.add("Hello");        list.add("World");        list.add("nihao");        list.add("shijie");        // 取得ListIterator实例化对象        ListIterator<String> iterator = list.listIterator();        // 由前向后输出数据        System.out.println("由前向后迭代输出数据:");        while(iterator.hasNext()){            System.out.println(iterator.next());        }        // 由后向前输出数据        System.out.println("由后向前输出数据");        while(iterator.hasPrevious()){            System.out.println(iterator.previous());        }    }}

程序运行结果
由前向后迭代输出数据:
Hello
World
nihao
shijie
由后向前输出数据
shijie
nihao
World
Hello


本程序使用ListIterator接口完成数据由前向后迭代输出和由后向前迭代输出.但是需要注意的是:

废弃的接口:Enumeration

Enumeration是一个最早的输出接口,成为枚举输出,在JDK1.0推出,在JDK1.5进行扩充,主要是添加了泛型.获得Enumeration接口的实例化对象:依靠Vector子类中定义的方法完成:

public Enumeration<E> elements()

Enumeration接口中定义的两个方法:
1. public boolean hasMoreElements() : 判断是否有下一个元素
2. public E nextElement() : 取出当前元素

代码示例

import java.util.List;import java.util.Vector;import java.util.Enumeration;public class TestDemo{    public static void main(String [] args){        // 使用Vector子类实例化vector接口        Vector<String> vector = new Vector<String>();        // 添加数据        vector.add("Hello");        vector.add("World");        vector.add("nihao");        vector.add("shijie");        // 取得Enumeration实例化对象        Enumeration<String> iterator = vector.elements();        while(iterator.hasMoreElements()){            System.out.println(iterator.nextElement());        }    }}

程序运行结果
Hello
World
nihao
shijie


本程序使用Enumeration接口完成Vector集合数据的迭代输出.但由于Enumeration本身只能通过Vector类对象实例化,所以 在开发中很少使用Enumeration进行开发,优先考虑的是Iterator接口.

JDK1.5的支持:foreach

对于foreach输出,除了可以进行数据数组的内容输出外,也可以针对集合类完成输出.
代码示例

import java.util.List;import java.util.ArrayList;public class TestDemo{    public static void main(String [] args){        // 使用Vector子类实例化list接口        List<String> list = new ArrayList<String>();        // 添加数据        list.add("Hello");        list.add("World");        list.add("nihao");        list.add("shijie");        // 使用foreach完成集合内容的输出        for(String str : list){            System.out.println(str);        }    }}

程序运行结果

Hello
World
nihao
shijie

本程序使用foreach完成集合内容的输出,代码看起来比较简单,但是还是推荐使用Iterator接口完成集合内容的输出.

Map集合的输出

之前一直在强调:使Iterator,但是Map接口中并没有提供Collection接口中iterator()方法.要实现Map接口通过Iterator输出,需要Map.Entry接口的支持.它的定义如下:

public static interface Map.Entry<K, V>

很明显,这是一个在Map接口中使用static定义的一个内部接口.Map.Entry接口中定义的两个常用方法:
1. public K getKey() : 取得当前的key
2. public V getValue() : 取得当前的value

下面通过一个图形来对比一下Collection和Map接口保存的数据形式.

这里写图片描述

通过上图的对比可以发现在Map集合和Collection集合中保存的最大区别是:Collection直接保存的是操作对象,而Map集合是将保存的key和value变成一个Map.Entry对象,通过这个对象包装了key和value.根据这一特性,给出Map使用Iterator输出的操作步骤:
1. 使用Map接口中的entrySet()方法,将Map集合变为Set集合
2. 取得了Set接口实例化后就可以使用iterator()方法取得Iterator的实例化对象
3. 使用Iterator迭代找到每一个Map.Entry对象,并进行key和value的分离.

代码示例

import java.util.HashMap;import java.util.Map;import java.util.Set;import java.util.Iterator;public class TestDemo{    public static void main(String [] args){        // 实例化Map接口        Map<String, Integer> map = new HashMap<String, Integer>();        // 添加数据        map.put("张三", 20);        map.put("李四", 23);        map.put("王五", 19);        map.put("赵六", 21);        // 将Map集合变为Set集合        Set<Map.Entry<String, Integer>> set = map.entrySet();        // 使用iterator()方法取得Iterator的实例化对象        Iterator<Map.Entry<String, Integer>> iterator = set.iterator();             // 进行key和value分离        while(iterator.hasNext()){            Map.Entry<String, Integer> me = iterator.next();            System.out.println(me.getKey() + "-->" + me.getValue());        }    }}

程序运行结果
李四–>23
张三–>20
王五–>19
赵六–>21


本程序按照之前给出的输出步骤进行代码,完成Map集合数据的输出.

类集转换

  1. List和Set的相互转换

使用public boolean addAll(Collection<? extends E> c)方法完成.

  1. Set和Map的相互转换

    • Map ——> Set

    • 使用public Set<Map.Entry<K, V>> entrySet()方法完成将Map转换成Set集合

    • 使用public Set<K> keySet()方法完成Map集合中的key转换成Set集合

Stack类

栈是采用先进后出的数据存储方式,每一个栈都包含一个栈顶,每次出栈是将栈顶数据取出.
Stack类的常用方法

No. 方法 描述 1 public E push(E item) 入栈操作 2 public E pop() 出栈操作

代码示例

import java.util.Stack;public class TestDemo{    public static void main(String [] args){        Stack<String> s = new Stack<String>();        // 入栈操作        s.push("Hello");        s.push("World");        // 出栈操作        System.out.println(s.pop());    }}

程序运行结果
World


通过程序的运行结果可以发现,出栈的顺序和入栈的顺序正好相反(先进后出).

属性操作类:Properties

属性一般针对于字符串数据,并且所有的字符数据都会按照”key = value”的形式保存,属性操作类主要针对于属性文件完成.

Properties类常用方法

No. 方法 类型 描述 1 public Properties() 构造 构造一个空的属性类 2 public Properties(Properties defaults) 构造 构造一个指定属性内容的属性类 3 public String getProperty(Stirng key) 普通 根据属性的key取得属性的value 4 public Object setProperty(String key, String value) 普通 设置属性 5 public void load(InputStream inStream) 普通 从输入流中取出全部属性内容 6 public void store(OutputStream out, Stirng coments) 普通 将属性内容通过输出流输出,同时声明属性的注释

代码示例

  1. 属性的设置与取得
import java.util.Properties;public class TestDemo{    public static void main(String [] args){        // 构造空的属性类        Properties p = new Properties();        // 保存属性        p.setProperty("张三", "20");        p.setProperty("李四", "21");        p.setProperty("王五", "19");        // 取得属性        System.out.println(p.getProperty("张三"));        System.out.println(p.getProperty("李四"));        System.out.println(p.getProperty("赵六"));    }}

程序运行结果
20
21
null


通过本程序,设置完属性之后,可以利用key查找属性,如果属性不存在便返回null.

  1. 将属性保存到文件中
import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.util.Properties;public class TestDemo{    public static void main(String [] args) throws IOException{        // 构造空的属性类        Properties p = new Properties();        // 保存属性        p.setProperty("zhangsan", "20");        p.setProperty("lisi", "21");        p.setProperty("wangwu", "19");        // 将属性保存到指定属性文件中        OutputStream out = new FileOutputStream(new File("/home/linyimin/DaSi/java/test.properties"));        p.store(out, "Test Info");    }}

程序运行结果

这里写图片描述


  1. 通过属性文件读取内容
import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.Properties;public class TestDemo{    public static void main(String [] args) throws IOException{        // 构造空的属性类        Properties p = new Properties();        // 从指定属性文件中读取属性内容        InputStream in = new FileInputStream(new File("/home/linyimin/DaSi/java/test.properties"));        p.load(in);        // 读取属性        System.out.println(p.getProperty("zhangsan"));        System.out.println(p.getProperty("lisi"));        System.out.println(p.getProperty("wangwu"));    }}

程序运行结果
20
21
19


本程序从一个已经保存好的资源文件中加载所有的属性内容.除此之外,属性的来源还可能是其他的输入流.

0 0