Java 类集框架

概念

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

Java 类集框架_第1张图片

Java 类集框架_第2张图片

注: 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 Listextends Collection

虽然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 list = new ArrayList();
        // 往集合中添加数据
        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 list = new Vector();
        // 往集合中添加数据
        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 extends Collection

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 set = new HashSet();
        // 往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 set = new TreeSet();
        // 往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{
    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 set = new TreeSet();
        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 set = new HashSet();
        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 set = new HashSet();
        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 map = new HashMap();
        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 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 map = new Hashtable();
        map.put("张三", 19);          // 保存数据
        map.put("张三", 18);          // key重复,value会被新值覆盖
        map.put("李四", 19);          // 保存数据
        map.put("王五", 16);          // 保存数据
        map.put("赵六", 13);          // 保存数据
        System.out.println(map.get("张三")); // 取得指定key的数据
        Set 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 iterator()“,所以一般情况下会很少关注Iterator接口,直接使用Collection接口定义的Iterator 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 list = new ArrayList();
        list.add("Hello");      // 添加数据
        list.add("World");
        list.add("Hello");
        // 使用Iterator输出是数据
        Iterator 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 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 list = new ArrayList();
        // 添加数据
        list.add("Hello");
        list.add("World");
        list.add("nihao");
        list.add("shijie");
        // 取得ListIterator实例化对象
        ListIterator 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 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 vector = new Vector();
        // 添加数据
        vector.add("Hello");
        vector.add("World");
        vector.add("nihao");
        vector.add("shijie");
        // 取得Enumeration实例化对象
        Enumeration 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 list = new ArrayList();
        // 添加数据
        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

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

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

Java 类集框架_第3张图片

通过上图的对比可以发现在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 map = new HashMap();
        // 添加数据
        map.put("张三", 20);
        map.put("李四", 23);
        map.put("王五", 19);
        map.put("赵六", 21);
        // 将Map集合变为Set集合
        Set.Entry> set = map.entrySet();
        // 使用iterator()方法取得Iterator的实例化对象
        Iterator.Entry> iterator = set.iterator();     
        // 进行key和value分离
        while(iterator.hasNext()){
            Map.Entry me = iterator.next();
            System.out.println(me.getKey() + "-->" + me.getValue());
        }
    }
}

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


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

类集转换

  1. List和Set的相互转换

使用public boolean addAll(Collection c)方法完成.

  1. Set和Map的相互转换

    • Map ——> Set

    • 使用public Set> entrySet()方法完成将Map转换成Set集合

    • 使用public Set 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 s = new Stack();
        // 入栈操作
        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");

    }
}

程序运行结果

Java 类集框架_第4张图片


  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


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

你可能感兴趣的:(java学习笔记,java)