Java 类集框架

概念

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

这里写图片描述

这里写图片描述

注: CollectionMap

单值保存

Collection接口

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

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

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

List接口

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

public interface List<E>extends Collection<E>

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

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

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

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.区别ArrayListVector
1推出时间JDK1.2JDK1.0
2性能采用异步处理方式,性能更高采用同步处理方式,性能相对较低
3安全性非线程安全线程安全
4输出方式Iterator,ListIterator,foreachIterator,ListIterator,foreach,Enumeration

Set接口

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

public interface Set<E> extends Collection<E>

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

散列排放的子类

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.方法描述
1public V put(K key, V value)向集合中保存数据
2public V get(Object o)通过指定的key取得对应的value
3public SetkeySet()将Map中的所有key以Set集合的方式返回
4public 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.区别HashMapHashtable
1推出时间JDK1.2JDK1.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.方法描述
1public boolean hasNext()判断是否有下一个值
2public E next()取出当前元素
3public 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.方法描述
1public E push(E item)入栈操作
2public 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.方法类型描述
1public Properties()构造构造一个空的属性类
2public Properties(Properties defaults)构造构造一个指定属性内容的属性类
3public String getProperty(Stirng key)普通根据属性的key取得属性的value
4public Object setProperty(String key, String value)普通设置属性
5public void load(InputStream inStream)普通从输入流中取出全部属性内容
6public 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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值