Java集合框架

一、集合

  • 概念:对象的容器,定义了许多对对象进行操作的的常用方法。可实现数组的功能。

  • 和数组的区别

    1. 数组长度固定,集合长度不固定。
    2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。
  • 位置: java.util.*;


二、Collection体系

img


1.Collection父接口

  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。

    • 创建集合:Collection collection = new ArrayList();
  • 常用方法

    • 添加元素:collection.add();
    • 将一个集合中的所有元素添加到此集合中:collection.addAll(Collection c);
    • 清空集合的所有元素:collection.clear();
    • 在集合中删除o元素。collection.remove(Object o);
    • 检查集合中是否包含o元素:collection.contains(Object o);
    • 判断集合是否为空:collection.isEmpty();
    /**
     * Collection接口的使用
     * 1.添加元素
     * 2.删除元素
     * 3.遍历元素
     * 4.判断
     */
    public class Test01 {
        public static void main(String[] args){
            //创建集合
            Collection collection=new ArrayList();
            //1.添加元素
            collection.add("苹果");
            collection.add("西瓜");
            collection.add("榴莲");
            System.out.println("元素个数:"+collection.size());
            System.out.println(collection);
            //2.删除元素
            collection.remove("榴莲");
            System.out.println("删除之后:"+collection.size());
            //3.遍历元素
            //3.1 使用增强for
            for(Object object : collection){
                System.out.println(object);
            }
            //3.2 使用迭代器(迭代器专门用来遍历集合的一种方式)
            //hasnext();判断是否有下一个元素
            //next();获取下一个元素
            //remove();删除当前元素
            Iterator iterator=collection.iterator();
            while(iterator.hasNext()){
                String object=(String)iterator.next();
                System.out.println(object);
                //删除操作
                //collection.remove(object);引发错误:并发修改异常
                //iterator.remove();应使用迭代器的方法
                // 4.判断
                System.out.println(collection.contains("西瓜"));//true
                System.out.println(collection.isEmpty());//false
            }
        }
    }
    

2.Collection子接口

List子接口
  • 特点:有序、有下标、不唯一、元素可以重复。

    • 创建方法:List list = new ArrayList<>();
  • 常用方法

    • 添加元素: list.add();会对基本类型进行自动装箱。

    • 删除元素:list.remove(0);

    • 获取指定的元素:list.indexOf();

    • 获取第一个和第二个元素(左闭右开):list.subList(1, 3);

/**
 * List子接口的使用(一)
 * 特点:1.有序有下标 2.可以重复,不唯一
 *
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.获取位置
 */
public class TestList {
    public static void main(String[] args) {
        List list=new ArrayList<>();
        //1.添加元素
        list.add("zhang");
        list.add("li");
        list.add(0,"wang");//插入操作
        System.out.println("元素个数:"+list.size());
        System.out.println(list.toString());
        //2.删除元素
        list.remove(0);
        //list.remove("yu");结果同上
        System.out.println("删除之后:"+list.size());
        System.out.println(list.toString());
        //3.遍历元素
        //3.1 使用for遍历
        for(int i=0;i<list.size();++i) {
            System.out.println(list.get(i));
        }
        //3.2 使用增强for
        for(Object object:list) {
            System.out.println(object);
        }
        //3.3 使用迭代器
        Iterator iterator=list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //3.4使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。
        ListIterator listIterator=list.listIterator();
        //从前往后
        while (listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }
        //从后往前(此时“遍历指针”已经指向末尾)
        while(listIterator.hasNext()) {
            System.out.println(listIterator.previous());
        }
        //4.判断
        System.out.println(list.isEmpty());
        System.out.println(list.contains("zhang"));
        //5.获取位置
        System.out.println(list.indexOf("li"));
    }
}
/**
 * List子接口的使用(二)
 * 1.添加元素
 * 2.删除元素
 * 3.获取元素
 */
public class TestList02 {
    public static void main(String[] args) {
        List list=new ArrayList();
        //1.添加数字数据(自动装箱)
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        System.out.println("元素个数:"+list.size());
        System.out.println(list.toString());
        //2.删除元素
        list.remove(0);
        //list.remove(20);很明显数组越界错误,改成如下
        //list.remove((Object)20);
        //list.remove(new Integer(20));
        System.out.println("元素个数:"+list.size());
        System.out.println(list.toString());
        //3-5不再演示,与之前类似
        //6.补充方法subList,返回子集合,含头不含尾
        List list2=list.subList(1, 3);
        System.out.println(list2.toString());
    }
}

List实现类
1、ArrayList【重点】
  • 特点
    • 数组结构实现,必须要连续空间,查询块、增删慢。
    • JDK1.2版本,运行效率快、线程不安全。
  • 常用方法
/**
 * ArrayList的使用
 * 存储结构:数组;
 * 特点:查找遍历速度快,增删慢。
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.查找
 */
public class TestArrayList02 {
    public static void main(String[] args) {
        ArrayList arrayList=new ArrayList<>();
        //1.添加元素
        Student s1=new Student("张", 21);
        Student s2=new Student("李", 22);
        Student s3=new Student("王", 21);
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        System.out.println("元素个数:"+arrayList.size());
        System.out.println(arrayList.toString());
        //2.删除元素
        arrayList.remove(s1);
        //arrayList.remove(new Student("唐", 21));
        //注:这样可以删除吗(不可以)?显然这是两个不同的对象。
        //假如两个对象属性相同便认为其是同一对象,那么如何修改代码?
        //3.遍历元素
        //3.1使用迭代器
        Iterator iterator=arrayList.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //3.2使用列表迭代器
        ListIterator listIterator=arrayList.listIterator();
        //从前往后遍历
        while(listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }
        //从后往前遍历
        while(listIterator.hasPrevious()) {
            System.out.println(listIterator.previous());
        }
        //4.判断
        System.out.println(arrayList.isEmpty());
        System.out.println(arrayList.contains(new Student("李", 22)));//false
        //注:与上文相同的问题。
        //5.查找
        System.out.println(arrayList.indexOf(s1));
    }
}

:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:

@Override
public boolean equals(Object obj) {
	//1.是否为同一对象
	if (this==obj) {
		return true;
	}
	//2.判断是否为空
	if (obj==null) {
		return false;
	}
	//3.判断是否是Student类型
	if (obj instanceof Student) {
		Student student=(Student) obj;
		//4.比较属性
		if(this.name.equals(student.getName())&&this.age==student.age) {
			return true;
		}
	}
	//不满足,返回false
	return false;
}
//学生类
public class Student {

    private String name;
    private Integer age;

    public Student() {
    }

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
ArrayList源码分析
  • 默认容量大小:private static final int DEFAULT_CAPACITY = 10;
  • 没有向集合中添加任何元素时,集合容量为0,添加一个元素后,集合容量为10。
  • 存放元素的数组:transient Object[] elementData;
  • 实际元素个数:private int size;
  • 每次扩容是原来的1.5倍。

2、Vector
  • 特点
    • 数组结构实现,查询快、增删慢。
    • JDK1.0版本,运行效率慢、线程安全。
  • 常用方法
/**
 * Vector的使用
 *
 *1.添加数据
 *2.删除数据
 *3.遍历
 *4.判断
 */
public class TestVector {
    public static void main(String[] args) {
        Vector vector=new Vector<>();
        //1.添加数据
        vector.add("张");
        vector.add("李");
        vector.add("王");
        System.out.println("元素个数:"+vector.size());
        //2.删除数据
        //vector.remove(0); vector.remove("tang");
        //3.遍历
        //使用枚举器
        Enumeration elements = vector.elements();
        while (elements.hasMoreElements()) {
            String s = (String) elements.nextElement();
            System.out.println(s);
        }
        //4.判断
        System.out.println(vector.isEmpty());
        System.out.println(vector.contains("李"));
        //5. Vector其他方法
        //firstElement()  lastElement()  ElementAt();
    }
}

3、LinkedList
  • 特点
    • 双向链表结构实现,无需连续空间,增删快,查询慢。
  • 常用方法
/**
 * LinkedList的用法
 * 存储结构:双向链表
 * 1.添加元素
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class TestLinkedList02 {

    public static void main(String[] args) {
        LinkedList linkedList=new LinkedList<>();
        Student s1=new Student("张", 21);
        Student s2=new Student("李", 22);
        Student s3=new Student("王", 21);
        //1.添加元素
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        linkedList.add(s3);
        System.out.println("元素个数:"+linkedList.size());
        System.out.println(linkedList.toString());
        //2.删除元素
        //linkedList.remove(new Student("唐", 21));
        //System.out.println(linkedList.toString());
        //3.遍历
        //3.1 使用for
        for(int i=0;i<linkedList.size();i++) {
            System.out.println(linkedList.get(i));
        }
        //3.2 使用增强for
        for(Object object:linkedList) {
            Student student=(Student) object;
            System.out.println(student.toString());
        }
        //3.3 使用迭代器
        Iterator iterator =linkedList.iterator();
        while (iterator.hasNext()) {
            Student student = (Student) iterator.next();
            System.out.println(student.toString());
        }
        //3.4 使用列表迭代器
        ListIterator listIterator = linkedList.listIterator();
        while (listIterator.hasNext()){
            Student student = (Student) listIterator.next();
            System.out.println(student.toString());
        }
        //4. 判断
        System.out.println(linkedList.contains(s1));
        System.out.println(linkedList.isEmpty());
        System.out.println(linkedList.indexOf(s3));
    }
}

ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢,数组结构实现。
  • LinkedList:无需开辟连续空间,查询慢,增删快,双向链表结构实现。

img


Set子接口
  • 特点:无序、无下标、唯一的、元素不可重复。
  • 方法:全部继承自Collection中的方法。
/**
 * 测试Set接口的使用
 * 特点:1.无序,没有下标;2.不能重复,
 * 1.添加数据
 * 2.删除数据
 * 3.遍历【重点】
 * 4.判断
 */
public class TestSet {
    public static void main(String[] args) {
        Set<String> set=new HashSet<String>();
        //1.添加数据
        set.add("张");
        set.add("李");
        set.add("王");
        System.out.println("数据个数:"+set.size());
        System.out.println(set.toString());//无序输出
        //2.删除数据
        /*
         * set.remove("tang"); System.out.println(set.toString());
         */
        //3.遍历【重点】
        //3.1 使用增强for
        for (String string : set) {
            System.out.println(string);
        }
        //3.2 使用迭代器
        Iterator<String> iterator=set.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //4.判断
        System.out.println(set.contains("张"));
        System.out.println(set.isEmpty());
    }
}

Set实现类
1、HashSet【重点】
  • 存储结构:哈希表(数组+链表+红黑树)
  • 存储过程(重复依据)
    1. 根据hashCode计算保存的位置,如果位置为空,则直接保存,位置不为空,执行第二步。
    2. 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。
  • 特点
    • 基于HashCode计算元素存放位置:
      1. 利用31这个质数,在计算时可以尽量减少散列冲突。
      2. 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点。
    • 当存入元素的哈希码相同时,会调用equals进行确认,如果结果为true,则拒绝后者存入。
/**
 * HashSet集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 * 1.添加元素
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class TestHashSet02 {
    public static void main(String[] args) {
        HashSet<Person> hashSet=new HashSet<>();
        Person p1=new Person("张",21);
        Person p2=new Person("李", 22);
        Person p3=new Person("王", 21);
        //1.添加元素
        hashSet.add(p1);
        hashSet.add(p2);
        hashSet.add(p3);
        //重复,添加失败
        hashSet.add(p3);
        //直接new一个相同属性的对象,依然会被添加,因为这是俩个对象。只不过他们俩属性相同。
        //hashSet.add(new Person("yu", 21));
        //注:假如相同属性便认为是同一个对象,怎么修改?
        System.out.println(hashSet.toString());
        //2.删除元素
        hashSet.remove(p2);
        //3.遍历
        //3.1 增强for
        for (Person person : hashSet) {
            System.out.println(person);
        }
        //3.2 迭代器
        Iterator<Person> iterator=hashSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        //4.判断
        System.out.println(hashSet.isEmpty());
        //直接new一个相同属性的对象结果输出是false,因为这是俩个对象。只不过他们俩属性相同。
        System.out.println(hashSet.contains(new Person("张", 21)));
        //注:假如相同属性便认为是同一个对象,该怎么做?
    }
}

:要实现“注”里的问题,可以重写hashCode和equals代码:

 	@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 Person {
    private String name;
    private int age;
    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

2、TreeSet
  • 基于排序顺序实现不重复。
  • 实现了SortedSet接口,对集合元素自动排序。
  • 元素对象的类型必须实现Comparable接口,指定排序规则。
  • 通过CompareTo方法确定是否为重复元素。
/**
 * 使用TreeSet保存数据
 * 存储结构:红黑树
 * 要求:元素类必须实现Comparable接口,compareTo方法返回0,认为是重复元素 
 */
public class TestTreeSet {
    public static void main(String[] args) {
        TreeSet<Person> persons= new TreeSet<>();
        Person p1=new Person("张",21);
        Person p2=new Person("李", 22);
        Person p3=new Person("王", 21);
        //1.添加元素
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        //注:直接添加会报类型转换错误,需要实现Comparable接口
        System.out.println(persons.toString());
        //2.删除元素
        persons.remove(p1);
        persons.remove(new Person("李", 22));
        System.out.println(persons.toString());
        //3.遍历  foreach循环   迭代器
        //4.判断
        System.out.println(persons.contains(new Person("王", 21)));
    }
}

:人类中实现Comparable接口:

public class Person implements Comparable<Person>{
    @Override
	//1.先按姓名比
	//2.再按年龄比
	public int compareTo(Person o) {
		int n1=this.getName().compareTo(o.getName());
		int n2=this.age-o.getAge();
		return n1==0?n2:n1;
	}
}

比较器:除了实现Comparable接口里的比较方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:

/**
 * TreeSet的使用
 * Comparator:实现定制比较(比较器)
 */
public class TestTreeSet02 {
    
    public static void main(String[] args) {
        TreeSet<Person> persons = new TreeSet<Person>(new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    //先年龄比较
                    //按姓名比较
                    int n1=o1.getAge()-o2.getAge();
                    int n2=o1.getName().compareTo(o2.getName());
                    return n1==0?n2:n1;
                }
            });
        Person p1=new Person("zhang",21);
        Person p2=new Person("li", 22);
        Person p3=new Person("wang", 21);
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        System.out.println(persons.toString());
    }
}
/**
 * 要求:使用TreeSet集合实现字符串按照长度进行排序
 * Comparator接口实现定制比较
 */
public class TestTreeSet03 {
    public static void main(String[] args) {
        //创建集合,指定比较规则
        TreeSet<String> treeSet=new TreeSet<String>(new Comparator<String>() {
            @Override
            //先比较字符串长度
            //再比较字符串
            public int compare(String o1, String o2) {
                int n1=o1.length()-o2.length();
                int n2=o1.compareTo(o2);
                return n1==0?n2:n1;
            }
        });
        treeSet.add("helloworld");
        treeSet.add("zhangsan");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        treeSet.add("zhaoliu");
        System.out.println(treeSet.toString());
        //输出[lisi, wangwu, zhaoliu, zhangsan, helloworld]
    }
}

三、Map集合概述

  • Map接口的特点

    1. 用于存储任意键值对(Key-Value)。

    2. 键:无序、无下标、不允许重复(唯一)。

    3. 值:无序、无下标、允许重复(不唯一)。

  • 方法

    • V put(K key,V value)//将对象存入到集合中,关联键值。key重复则覆盖原值。

    • Object get(Object key)//根据键获取相应的值。

    • Set<K>//返回所有的key

    • Collection<V> values()//返回包含所有值的Collection集合。

    • Set<Map.Entry<K,V>>//键值匹配的set集合

/**
 * Map接口的使用
 * 特点:1.存储键值对 2.键不能重复,值可以重复 3.无序
 */
public class TestMap {
    public static void main(String[] args) {
        Map<String,Integer> map=new HashMap<String, Integer>();
        //1.添加元素
        map.put("张", 21);
        map.put("李", 22);
        map.put("王", 23);
        System.out.println(map.toString());
        //2.删除元素
        map.remove("李");
        System.out.println(map.toString());
        //3.遍历
        //3.1 使用keySet();
        for (String key : map.keySet()) {
            System.out.println(key+" "+map.get(key));
        }
        //3.2 使用entrySet();效率较高
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
    }
}

Map集合的实现类

1、HashMap【重点】
  • 存储结构:哈希表(数组+链表+红黑树)
  • JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。
/**
 * HashMap的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 */
public class TestHashMap02 {
    public static void main(String[] args) {
        HashMap<Student, String> hashMap=new HashMap<Student, String>();
        Student s1=new Student("张三", 36);
        Student s2=new Student("李四", 101);
        Student s3=new Student("王五", 10);
        //1.添加元素
        hashMap.put(s1, "成都");
        hashMap.put(s2, "杭州");
        hashMap.put(s3, "郑州");
        //hashMap.put(s3, "上海");//添加失败,但会更新值
        hashMap.put(new Student("王五", 10),"郑州");//添加成功,不过他们俩的属性一模一样;
        //注:假如相同属性便认为是同一个对象,怎么修改?(学生类重些hashode和equals)
        System.out.println(hashMap.toString());
        System.out.println("=======================================");
        //2.删除元素
        hashMap.remove(s3);
        System.out.println(hashMap.toString());
        //3.遍历
        //3.1 使用keySet()遍历
        for (Student key : hashMap.keySet()) {
            System.out.println(key+" "+hashMap.get(key));
        }
        //3.2 使用entrySet()遍历
        for (Map.Entry<Student, String> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
        //4.判断
        //注:同上
        System.out.println(hashMap.containsKey(new Student("王五", 10)));//false
        System.out.println(hashMap.containsValue("郑州"));
    }
}

:和之前说过的HashSet类似,重复依据是hashCode和equals方法,重写即可:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return Objects.equals(name, student.name) &&
            Objects.equals(age, student.age);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}
HashMap源码分析
  • HashMap刚创建时,table是null,节省空间,当添加第一个元素时,table容量调整为16。
  • HashMap存储的数组:transient Node<K,V>[] table;
  • 默认初始化容量:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
  • 当元素个数大于阈值(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数。
  • 默认加载因子:static final float DEFAULT_LOAD_FACTOR = 0.75f;
  • jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑树,目的是提高效率
  • 链表调整为红黑树的链表长度阈值(JDK1.8):static final int TREEIFY_THRESHOLD = 8;
  • 红黑树调整为链表的链表长度阈值(JDK1.8):static final int UNTREEIFY_THRESHOLD = 6;
  • 链表调整为红黑树的数组最小阈值(JDK1.8):static final int MIN_TREEIFY_CAPACITY = 64;
  • HashMap存储的元素个数:transient int size;

HashSet源码分析

可以看见HashSet的存储结构就是HashMap。

COPYpublic class HashSet<E>
      extends AbstractSet<E>
      implements Set<E>, Cloneable, java.io.Serializable
  {
      private transient HashMap<E,Object> map;
      private static final Object PRESENT = new Object();
      public HashSet() {
          map = new HashMap<>();
      }
  }

那它的存储方式是怎样的呢?可以看一下add方法:

COPYpublic boolean add(E e) {
      return map.put(e, PRESENT)==null;
  }

很明了地发现它的add方法调用的就是map的put方法,把元素作为map的key传进去的。


Hashtable
  • JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。
  • 稍微了解即可。
Properties
  • Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
  • 它继承了Hashtable的方法,与流关系密切。
2、TreeMap
  • 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
/**
 * TreeMap的使用
 * 存储结构:红黑树
 */
public class TestTreeMap {
    public static void main(String[] args) {
        TreeMap<Student, Integer> treeMap=new TreeMap<Student, Integer>();
        Student s1=new Student("张三", 36);
        Student s2=new Student("李四", 11);
        Student s3=new Student("王五", 10);
        //1.添加元素
        treeMap.put(s1, 21);
        treeMap.put(s2, 22);
        treeMap.put(s3, 21);
        //不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小
        System.out.println(treeMap.toString());
        //2.删除元素
        treeMap.remove(new Student("王五", 10));
        System.out.println(treeMap.toString());
        //3.遍历
        //3.1 使用keySet()
        for (Student key : treeMap.keySet()) {
            System.out.println(key+" "+treeMap.get(key));
        }
        //3.2 使用entrySet()
        for (Map.Entry<Student, Integer> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }
        //4.判断
        System.out.println(treeMap.containsKey(s1));
        System.out.println(treeMap.isEmpty());
    }
}

在学生类中实现Comparable接口:

COPYpublic class Student implements Comparable<Student>{
    @Override
    public int compareTo(Student o) {
        int n1=this.age-o.age;
        return n1;
}

除此之外还可以使用比较器来定制比较:

TreeMap<Student, Integer> treeMap2=new TreeMap<Student, Integer>(new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        // 略
        return 0;
    }			
});

TreeSet源码

TreeSet的存储结构实际上就是TreeMa:

COPYpublic class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
}

再来看其存储方式:

COPYpublic boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

它的add方法调用的就是TreeMap的put方法,将元素作为key传入到存储结构中。


四、泛型概述

  • 本质是参数化类型,把类型作为参数传递。
  • 常见形式有泛型类、泛型接口、泛型方法。
  • 语法
    • <T,…> T称为类型占位符,表示一种引用类型,可以写多个逗号隔开。
  • 好处
    • 提高代码的重用性。
    • 防止类型转换异常,提高代码的安全性。

泛型类

/**
 * 泛型类
 * 语法:类名<T>
 * T是类型占位符,表示一种引用类型,编写多个使用逗号隔开
 */
//泛型类
public class MyGeneric<T> {
    //1.创建泛型变量
    //不能使用new来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法。
    T t;
    //2.泛型作为方法的参数
    public void show(T t) {
        System.out.println(t);
    }
    //泛型作为方法的返回值
    public T getT() {
        return t;
    }
}
/**
 *使用泛型类创建对象 
 *注意:
 * 1.泛型只能使用引用类型
 * 2.不同泛型类型的对象不能相互赋值
 */
public class testGeneric {
    public static void main(String[] args) {
        //使用泛型类创建对象
        MyGeneric<String> genericType=new MyGeneric<String>();
        genericType.t="wang";
        genericType.show("w");

        MyGeneric<Integer> genericType1=new MyGeneric<Integer>();
        genericType1.t=10;
        genericType1.show(20);
        Integer integer=genericType1.getT();
        System.out.println(integer);
    }
}

泛型接口

/**
 * 泛型接口
 * 语法:接口名<T>
 * 注意:不能创建泛型静态常量
 */
public interface MyInterface<T> {
    //创建常量(非静态)
	String nameString="zhang";
    
	T server(T t);
}
/**
 * 实现接口时确定泛型类
 */
public class MyInterfaceImpl implements MyInterface<String>{
	@Override
	public String server(String t) {
		System.out.println(t);
		return t; 
	}
}
//测试
MyInterfaceImpl myInterfaceImpl=new MyInterfaceImpl();
myInterfaceImpl.server("xxx");
//xxx
/**
 * 实现接口时不确定泛型类
 */
public class MyInterfaceImpl2<T> implements MyInterface<T>{
	@Override
	public T server(T t) {
		System.out.println(t);
		return t;
	}
}
//测试
MyInterfaceImpl2<Integer> myInterfaceImpl2=new MyInterfaceImpl2<Integer>();
myInterfaceImpl2.server(2000);
//2000

泛型方法

/**
 * 泛型方法
 * 语法:<T> 返回类型
 */
public class MyGenericMethod {
	public <T> void show(T t) {
		System.out.println("泛型方法"+t);
	}
}
//测试
MyGenericMethod myGenericMethod=new MyGenericMethod();
myGenericMethod.show("zhang");
myGenericMethod.show(200);
myGenericMethod.show(3.14);

泛型集合

  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。

  • 特点

  • 编译时即可检查,而非运行时抛出异常。

    • 访问时,不必类型转换(拆箱)。
  • 不同泛型指尖引用不能相互赋值,泛型不存在多态。


五、Collections工具类

  • 概念:集合工具类,定义了除了存取以外的集合常用方法。

  • 方法

//Collections工具类的使用
public class TestCollections {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<Integer>();
        list.add(40);
        list.add(10);
        list.add(30);
        list.add(20);
        list.add(50);

        //sort排序
        System.out.println(list.toString());
        Collections.sort(list);
        System.out.println(list.toString());
        System.out.println("=====================");

        //binarySearch二分查找
        int i=Collections.binarySearch(list, 10);
        System.out.println(i);//0

        //copy复制
        List<Integer> list2=new ArrayList<Integer>();
        for(int ii=0;ii<5;ii++) { //ii<4就会报错 下标越界
            list2.add(0);
        }
        //该方法要求目标元素容量大于等于源目标
        Collections.copy(list2,list);
        System.out.println(list2.toString());
        System.out.println("=====================");

        //reserve反转
        Collections.reverse(list2);
        System.out.println(list2.toString());

        //shuffle 打乱
        Collections.shuffle(list2);
        System.out.println(list2.toString());
        System.out.println("=====================");

        //补充:集合转成数组
        Integer[] array = list.toArray(new Integer[0]);
        System.out.println(array.length);
        //补充:数组转成集合,受限集合,不能添加和删除
        String[] array1= {"zhang","li","wang"};
        List<String> list3 = Arrays.asList(array1);
        System.out.println(list3);
        //把基本类型数组转成集合时,需要修改为包装类
        Integer[] nums = {1, 2, 3};
        List<Integer> list4 = Arrays.asList(nums);
        System.out.println(list4);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值