【Java学习笔记】 类集框架

前言: Java 类集框架是Java API的重要内容。

本文分四个部分记录一下我的学习的过程。

概述

类集实际上就属于动态的对象数组,最原始的数组本身有一个最大的缺陷:数组的长度是固定的。主要有以下四个内容:

  • List接口
  • Set接口
  • Map接口
  • 集合输出

java.util包提供有两个最为核心的操作接口:Collection集合接口(包括List和Set接口)、Map接口,Collection的操作形式与链表的形式相似,每一次进行数据操作的时候只能够对单个对象进行处理。Collection不能区分存储类型:Collection的子接口:List(允许重复)、Set(不允许重复)


3-1

1. List接口的基本使用

List接口是Collection的子接口,其最大的特点在于有一个get()方法,可以根据索引取得内容(数据结构中的有序概念)。
但List本身还属于一个接口,而如果要想取得接口的实例化对象,就必须要有子类,在List接口下有三个常用子类:ArrayListVectorLinkedList

3-2


1.1 ArrayList子类

ArrayList是一个针对于List接口的数组操作实现。首先利用ArrayList做一些List的基本操作
范例:观察List基本处理

import java.util.*;;

public class TestDemo1 {

    public static void main(String[] args) throws Exception{
        List<String> all = new ArrayList<String>();
        System.out.println(all.size() + "、" + all.isEmpty());
        all.add("hello");
        all.add("hello");
        all.add("world");
        System.out.println(all.size() + "、" + all.isEmpty());
        all.remove("hello");
        System.out.println(all.contains("world"));
        System.out.println(all.contains("abc"));
        System.out.println(all);    
        for (int i = 0; i < all.size(); i++) {
            System.out.println(all.get(i));
        }

    }
}

1.2 Vector子类

代码同上,将ArrayList改为Vector即可

Vector和ArrayList的区别:

No.区别ArrayListVector
1历史时间JDK 1.2JDK 1.0
2处理形式异步处理,性能更高同步处理,性能降低
3数据安全非线程安全线程安全
4输出形式Iterator、ListIterator、foreachIterator、ListIterator、foreach、Enumeration

3-3


1.3 LinkedList子类

代码同上,将ArrayList改为LinkedList即可

ArrayList和LinkedList区别:

  • ArrayList里面存放的是一个数组,如果在实例化此类对象的时候,默认传入了数组的大小,则里面保存的数组会开辟一个定长的数组。如果后面再进行数据保存的时候发现数组的长度不够了。那么会进行数组的动态扩充。所以在实际开发之中,如果要使用ArrayList最好的做法就是设置好初始化的大小
  • LinkedList是纯粹的链表实现,与之前编写的链表程序的实现完全一样。
  • 总结: ArrayList封装的是一个数组,LinkedList封装的是一个链表实现。ArrayList时间复杂度是1,LinkedList时间复杂度为n

2. Set接口的基本使用

3-4

2.1 HashSet子类

import java.util.*;
public class TestDemo1 {

    public static void main(String[] args) throws Exception{
        Set<String> all = new HashSet<String>();
        all.add("hello");
        all.add("hello");//重复元素
        all.add("world");
        all.add("abcd");
        all.add("hash");

        System.out.println(all);
    }
}

2.2 TreeSet子类

import java.util.*;
public class TestDemo1 {

    public static void main(String[] args) throws Exception{
        Set<String> all = new TreeSet<String>();
        all.add("A");
        all.add("B");
        all.add("A");
        all.add("D");
        all.add("C");

        System.out.println(all);
    }
}

2.3 重复元素的判断

HashSet判断重复元素的依靠的是Object类中的两个方法:

• Hash码:public int hashCode()
• 对象比较:public boolean equals(Object obj)

对象比对分两步:第一步要通过一个对象的唯一编码找到一个对象的信息,当编码匹配之后再调用equals()方法进行内容的比较

范例:使用HashSet来观察对象:

import java.util.*;

class Person implements Comparable<Person>{
    private String name;
    private Integer age;
    public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((age == null) ? 0 : age.hashCode());
        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 == null) {
            if (other.age != null)
                return false;
        } else if (!age.equals(other.age))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    @Override
    public int compareTo(Person o) {
        if (this.age > o.age) {
            return 1;
        }else if (this.age < o.age) {
            return -1;
        }else {
            return this.name.compareTo(o.name);
        }
    }
}

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Set<Person> all = new HashSet<Person>();
        all.add(new Person("张三", 20));
        all.add(new Person("张三", 20));//数据重复
        all.add(new Person("李四", 20));//年龄重复
        all.add(new Person("王五", 19));

        System.out.println(all.contains(new Person("李四", 20)));
        System.out.println(all);
    }
}

保存自定义类对象一定用List接口,保存系统类的信息一定使用Set接口(过滤重复)


3. 集合输出

在之前集合输出的时候都利用了toString(),或者是利用了List接口中get()方法,但是这些都不是集合的标准输出模式
如果从标准上来讲,集合的输出一共有四种手段:IteratorListIteratorEnumerationforeach

3.1 迭代输出:Iterator

这里写图片描述

• 判断是否有下一个元素:public boolean hasNext()
• 取得当前元素:public E next()
• 删除元素:public default void remove()
• 取得Iteratior接口对象:public Iterator<E> iterator()

范例:标准的Iteratior使用

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        List<String> all = new ArrayList<String>();

        all.add("hello");
        all.add("hello");
        all.add("world");
        Iterator<String> iter = all.iterator(); //实例化Iterator接口
        while (iter.hasNext()) {
            String string = (String) iter.next();
            System.out.println(string);
        }
    }
}

3.2 双向迭代输出:ListIterator

这里写图片描述

如Iterator输出接口有一个特点:只能够由前向后进行内容的迭代。而如果想要进行双向的迭代输出就使用ListIterator
有两个方法:

• 判断是否有上一个元素:public boolean hasPrevious()
• 取得当前元素:public E previous()

3.3 枚举输出:Enumeration

Enumeration有如下方法:

• 是否有下一个元素:public boolean hasMoreElements()
• 取得元素:public E nextElement()

范例:使用Enumeration来输出

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Vector<String> all = new Vector<String>();

        all.add("a");
        all.add("b");
        all.add("c");
        ListIterator<String> iter = all.listIterator(); //实例化Iterator接口
        Enumeration<String> enu = all.elements();
        while (enu.hasMoreElements()) {
            String string = (String) enu.nextElement();
            System.out.println(string);
        }

    }
}

一些操作类库上依然只支持Enumeration,而不支持Iteratior,所以必须会使用到Enumeration

3.4 foreach

范例:使用foreach来输出

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        List<String> all = new ArrayList<String>();
        all.add("a");
        all.add("b");
        all.add("c");
        for (String string : all) {
            System.out.println(string);
        }
    }
}

4. Map接口的基本使用

Collection集合的特点是每次进行单个对象的保存,如果现在要进行一对对象的保存(偶对象),只能使用Map集合来完成。Map集合一次性保存两个,两个对象的关系:key = value 的接口。最大特点是可以通过key找到value。

Map集合本身是一个接口,要使用Map必须通过子类进行对象实例化,而子类有如下几个:HashMapHashTableTreeMap(用作排序)ConcurrentHashMap

3-7

4.1 HashMap子类

范例:Map的基本操作

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(3, "d");
        map.put(1, "a");
        map.put(1, "b");//key重复了
        map.put(2, "c");
        System.out.println(map);
        System.out.println(map.get(1));//根据key取得值
        System.out.println(map.get(99));
        }
}

HashMap的原理:
在数据量小的时候,Hash Map是按照链表的模式存储的,当数据量变大之后为了进行快速的查找,那么会将这个链表变为红黑树(均衡二叉树),用hash码作为数据的定位,来进行保存。

4.2 HashTable 子类

HashTable 和 HashMap 的区别*:

No.区别HashMapHashTable
1历史时间JDK 1.2JDK 1.0
2处理形式异步处理,性能更高同步处理,性能降低
3数据安全非线程安全线程安全
4null操作允许存放null不允许为空

3-8

4.3 ConcurrentHashMap

操作代码同上,改成ConcurrentHashMap即可。

ConcurrentHashMap = HashTable的线程安全 + HashMap的高性能,在使用ConcurrentHashMap 处理的时候既可以保证多个线程更新数据的同步、又可以保证很高效的查询速度。

ConcurrentHashMap的操作原理:
数据分桶:如果说现在采用一定的算法,将保存的大量数据平均分在不同的桶(数据区域中),在进行数据查找的时候就可以避免全部的数据扫描。采用分桶之后每一个数据必须有一个明确的分桶的标记(通过使用hashcode())
3-9
3-10
3-11
3-12

4.4 使用Iterator输出Map集合

如果存储数据是为了输出,那么优先考虑的一定是Collection,使用Map的主要操作是为了设置内容,而后使用get()进行查找。
明确一点:Map接口没有iterator方法。

Collection与Map数据保存的区别:
3-13

Map内部有嵌套类 Map.Entry<K,V>,把数据封装成Entry
接口 Map.Entry<K,V>有如下方法需要关注:

• public K getKey()
• public V getValue()

在Map接口有一个重要方法:将Map集合转为Set集合: public Set<Map.Entry<K,V>> entrySet()

3-14

范例:通过iterator输出Map集合(必须熟练使用)

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(3, "d");
        map.put(1, "a");
        map.put(1, "b");//key重复了
        map.put(2, "c");
        Set<Map.Entry<Integer, String>> set = map.entrySet();//1.将Map集合变为Set集合
        Iterator<Map.Entry<Integer, String>> iter = set.iterator();//2.实例化iterator接口
        while (iter.hasNext()) {
            Map.Entry<Integer, String> entry = (Map.Entry<Integer,String>) iter.next();//3.取出Map.Entry
            System.out.println("key: " + entry.getKey() + "  value: " + entry.getValue()); //4.取得key和value
        }
        }
}

4.5 Map中key的说明

可以使用自定义key类型,不过要覆写hashcode和equeals
范例: 覆写hashcode和equeals

class Person{
    private String name;
    private Integer age;
    public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((age == null) ? 0 : age.hashCode());
        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 == null) {
            if (other.age != null)
                return false;
        } else if (!age.equals(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 TestDemo1 {
    public static void main(String[] args) throws Exception{
        Map<Person, String> map = new HashMap<Person, String>();
        map.put(new Person("张三", 20),new String("zs"));
        System.out.println(map.get(new Person("张三", 20)));
}
}

4.6 TreeMap子类

范例:无实例化对象

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Map<Integer, String> map = new TreeMap<Integer, String>();
        map.put(19, "a");
        map.put(2253, "b");
        map.put(14, "c");
        map.put(865, "d");
        System.out.println(map);
}   
}

范例:实例化对象(无hashcode和equals)

class Person implements Comparable<Person>{
    private String name;
    private Integer age;
    public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    @Override
    public int compareTo(Person o) {
        // TODO Auto-generated method stub
        return this.name.compareTo(o.name);
    }
}

public class TestDemo1 {
    public static void main(String[] args) throws Exception{
        Map<Person, String> map = new TreeMap<Person, String>();
        map.put(new Person("张三", 20),new String("zs"));
        System.out.println(map.get(new Person("张三", 20)));
}
}

结论:有comparable的地方过滤重复数据就依靠compareTo()方法,这类操作一般不常见


个人博客链接:xvanning.cn

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值