集合

千锋JAVA集合框架视频笔记


  • 概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。
  • 和数组的区别
  1. 数组长度固定,集合长度不固定。
  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。
  • 位置: java.util.*;

Collection体系集合

JAVA集合_删除元素

Coolection父接口

  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。
  • 方法
    • boolean add(Object obj) //添加一个对象。
    • boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
    • void clear() //清空此集合中的所有对象。
    • boolean contains(Object o) //检查此集合中是否包含o对象。
    • boolean equals(Object o) //比较此集合是否与指定对象相等。
    • boolean isEmpty() //判断此集合是否为空。
    • boolean remove(Object o) //在此集合中移除o对象。
    • int size() //返回此集合中的元素个数。
    • Object[] toArray() //姜此集合转换成数组。

public class Demo01 {
    public static void main(String[] args) {
        Collection collection =new ArrayList();
        //1.添加元素
        collection.add("语文");
        collection.add("数学");
        collection.add("英语");
        collection.add("物理");
        System.out.println("元素个数:"+collection.size());
        System.out.println(collection);
        //2.删除元素
        collection.remove("数学");
        System.out.println("元素个数:"+collection.size());
        System.out.println(collection);
        //3.遍历元素
        System.out.println("------使用增强for------");
        for (Object o:collection) {
            System.out.println(o);
        }
        System.out.println("-----使用迭代器------");
        Iterator iterator=collection.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
            //迭代过程中不能使用collection的删除方法
            //collection.remove();引发错误:并发修改异常
            //iterator.remove();//应使用迭代器的方法
        }
        //4.判断
        System.out.println(collection.isEmpty());//false
        System.out.println(collection.contains("英语"));//true
    }
}

List接口

  • 特点:有序、有下标、元素可以重复。
  • 方法
    • void add(int index,Object o) //在index位置插入对象o。
    • boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
    • Object get(int index) //返回集合中指定位置的元素。
    • List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。

public class Demo02 {
    public static void main(String[] args) {
        List list=new ArrayList();
        //1.添加
        list.add("苹果");
        list.add("香蕉");
        list.add(0,"梨");
        List list1=new ArrayList();
        list1.add("芒果");
        list.addAll(2,list1);
        System.out.println("元素个数为"+" "+list.size());//元素个数为 4
        System.out.println(list);//[梨, 苹果, 芒果, 香蕉]
        //2.删除
//        list.remove(1);
//        System.out.println(list);
        //3.遍历元素
        System.out.println("----for循环遍历---");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));//返回指定位置的元素
        }
        System.out.println("----增强for遍历---");
        for (Object o:list
             ) {
            System.out.println(o);
        }
        System.out.println("----迭代器遍历---");
        Iterator it=list.iterator();
        while (it.hasNext())
            System.out.println(it.next());
        System.out.println("------使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。----");
        ListIterator listIterator=list.listIterator();
        while (listIterator.hasNext())
        {
            System.out.println(listIterator.next());
        }
        System.out.println("------从后往前遍历-----");
        while (listIterator.hasPrevious()) {
            System.out.println(listIterator.previous());
        }
        //4.判断
        System.out.println(list.isEmpty());//false
        System.out.println(list.contains("香蕉"));//true
        //5.获取位置
        System.out.println(list.indexOf("苹果"));
        //6.补充方法subList,返回子集合,含头不含尾,左闭右开
        List list2=list.subList(1, 3);
        System.out.println(list2);//[苹果, 芒果]
        //注意:当类型为数字类型时,add(5)会自动封箱
        //使用删除方法remove(5)只能删除下标为5的元素
        //要删除5这个元素要使用强转换remove(Object(5))或remove(new Integer(20))
    }
}

Lsit实现类

  • ArrayList 【重点】
    • 数组结构实现,必须要连续空间,查询快、增删慢
    • jdk1.2版本,运行效率块、线程不安全
  • Vector
    • 数组结构实现,查询快、增删慢
    • jdk1.0版本,运行效率慢,线程安全
  • LinkedList
    • 双向链表结构实现,无需连续空间,增删快,查询慢

ArrayList


public class Demo3 {
	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)));
		//注:与上文相同的问题。
		//5.查找
		System.out.println(arrayList.indexOf(s1));
	}
}

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

COPY@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;
}

原码分析

DEFAULT_CAPACITY = 10; //默认容量
//注意:如果没有向集合中添加任何元素时,容量0,添加一个后,容量为10
//每次扩容是原来的1.5倍
elementData存放元素的数组
size 实际元素个数

具体分析过程参考

Vector


public class Demo4 {
	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("苹果");
		 */
		//3.遍历
		//使用枚举器
		Enumeration enumeration=vector.elements();
		while (enumeration.hasMoreElements()) {
			String s = (String) enumeration.nextElement();
			System.out.println(s);
		}
		//4.判断
		System.out.println(vector.isEmpty());
		System.out.println(vector.contains("苹果"));
		//5. Vector其他方法
		//firstElement()  lastElement()  ElementAt();
	}
}

LinkedList


/**
 * LinkedList的用法
 * 存储结构:双向链表
 * 1.添加元素
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class Demo5 {
	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 使用列表迭代器(略)
		//4. 判断
		System.out.println(linkedList.contains(s1));
		System.out.println(linkedList.isEmpty());
		System.out.println(linkedList.indexOf(s3));
	}
}

LinkedList源码分析参考


ArrayList和LinkedList区别

  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。

JAVA集合_迭代器_02

泛型

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

泛型类

/**
 * 泛型类
 * @param <T>
 */
public class MyGeneric<T> {
    T t;
    public void show(T t){
        System.out.println(t);
    }

    public T getT(){
        return t;
    }
}
/** * 注意: * 1.泛型只能使用引用类型 * 2.不同泛型类型的对象不能相互赋值 */public class Application {    public static void main(String[] args) {        MyGeneric<String> myGeneric=new MyGeneric<String>();        myGeneric.t="hello";        myGeneric.show("myGeneric");//myGeneric        String string = myGeneric.getT();        System.out.println(string);//hello        MyGeneric<Integer> myGeneric1=new MyGeneric<Integer>();        myGeneric1.t=100;        myGeneric1.show(200);//200        System.out.println(myGeneric1.getT());//100    }}

泛型接口

/** * 语法:接口名<T> * 注意:不能创建泛型静态常量 * 泛型接口 */public interface MyInterface<T> {    String s="hello";    T server(T t);}
/** * 实现接口时确定泛型类 */public class MyInterfaceImpl implements MyInterface<String>{    @Override    public String server(String s) {        System.out.println(s);        return s;    }}
/** * 实现接口时不确定泛型类 */public class MyInterfaceImpl2<T> implements MyInterface<T> {    @Override    public T server(T t) {        System.out.println(t);        return t;    }}
public class TestInterface {    public static void main(String[] args) {        MyInterfaceImpl myInterface=new MyInterfaceImpl();        myInterface.server("haha");        MyInterfaceImpl2<String> myInterfaceImpl2=new MyInterfaceImpl2<String>();        myInterfaceImpl2.server("xxxx");    }}

泛型方法

/** * 泛型方法 * 语法:<T> 返回类型 */public class MyGenericMethod {    public <T> String get(T t){        System.out.println(t);        return (String) t;    }}
public class TestMethod {    public static void main(String[] args) {        MyGenericMethod myGenericMethod=new MyGenericMethod();        System.out.println(myGenericMethod.get("hello"));    }}

泛型集合

  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
  • 特点:
    • 编译时即可检查,而非运行时抛出异常。
    • 访问时,不必类型转换(拆箱)。
    • 不同泛型指尖引用不能相互赋值,泛型不存在多态。

之前我们在创建LinkedList类型对象的时候并没有使用泛型,但是进到它的源码中会发现:

public class LinkedList<E>    extends AbstractSequentialList<E>    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{//略}

它是一个泛型类,而之前使用的时候并没有传递,说明java语法是允许的,这个时候传递的类型是Object类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。

Set接口

  • 特点:无序、无下标、元素不可重复
  • 方法:全部继承自Collection中的方法
  • 增、删、遍历、判断与collection一致

Set实现类

HashSet(重点)

  • 基于HashCode计算元素存放位置。
  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为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 + "]";    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Person person = (Person) o;        return age == person.age &&                name.equals(person.name);    }    @Override    public int hashCode() {        return Objects.hash(name, age);    }}
/** * HashSet集合的使用 * 存储结构:哈希表(数组+链表+红黑树) * 1.添加元素 * 2.删除元素 * 3.遍历 * 4.判断 */public class Demo01 {    public static void main(String[] args) {        HashSet<Person> hashSet = new HashSet();        Person p1 = new Person("张三", 20);        Person p2 = new Person("李四", 22);        Person p3 = new Person("王五", 25);        //1.添加元素        hashSet.add(p1);        hashSet.add(p2);        hashSet.add(p3);        //重复,添加失败        hashSet.add(p3);        //直接new一个相同属性的对象,依然会被添加,不难理解。        //假如相同属性便认为是同一个对象,怎么修改?(重写hashCode和equals方法)        System.out.println("元素个数为:"+hashSet.size());        System.out.println(hashSet.toString());        //2.删除元素//        hashSet.remove(p1);//        System.out.println("元素个数为:"+hashSet.size());//        System.out.println(hashSet.toString());        //3.遍历元素        //增强for        for (Person p:hashSet) {            System.out.println(p);        }        //迭代器        System.out.println("-----------");        Iterator iterator=hashSet.iterator();        while (iterator.hasNext())            System.out.println(iterator.next());        //4.判断        System.out.println(hashSet.isEmpty());        System.out.println(hashSet.contains(p1));        System.out.println(hashSet.contains(new Person("王五", 25)));//重写后返回true    }}

:hashSet存储过程:

  1. 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
  2. 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。

存储过程实际上就是重复依据,要实现“注”里的问题,可以重写hashCode和equals代码:

可以选择自动重写方法如上,还可选择手动重写方法实现“注”里面的问题

@Overridepublic int hashCode() {    final int prime = 31;    int result = 1;    result = prime * result + age;    result = prime * result + ((name == null) ? 0 : name.hashCode());    return result;}@Overridepublic 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;}

hashCode方法里为什么要使用31这个数字大概有两个原因:

  1. 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
  2. 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。

TreeSet

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

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

public class Demo03 {    public static void main(String[] args) {        TreeSet<Person> persons=new TreeSet(new Comparator<Person>() {            @Override            public int compare(Person p1, Person p2) {                int n1 = p1.getName().compareTo(p2.getName());                int n2 = p1.getAge() - p2.getAge();                return n1 == 0 ? n2 : n1;            }        });        Person p1 = new Person("张三", 20);        Person p2 = new Person("李四", 22);        Person p3 = new Person("王五", 25);        //1.添加元素        persons.add(p1);        persons.add(p2);        persons.add(p3);        persons.add(p1);        System.out.println(persons.toString());    }}

Map集合概述

  • 特点:存储一对数据(Key-Value),无序、无下标,键不可重复。

  • 方法

    • 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接口的使用 */public class Demo01 {    public static void main(String[] args) {        Map<String,Integer> map=new HashMap();        //添加元素        map.put("张三",25);        map.put("李四",23);        map.put("王五",18);        System.out.println(map.size());        System.out.println(map.toString());        //删除元素//        map.remove("张三");//        map.remove("李四",23);//        System.out.println(map.toString());        //遍历        //3.1 使用keySet();//        Set<String> strings = map.keySet();//        for (String string :strings) {//            System.out.println(string+"-----"+map.get(string));//        }        for (String string :map.keySet()) {            System.out.println(string+"-----"+map.get(string));        }        System.out.println("-------");        //3.2 使用entrySet();效率较高        for(Map.Entry<String, Integer> entry:map.entrySet())//            System.out.println(entry);//        //李四=23//        //张三=25//        //王五=18            System.out.println(entry.getKey()+"----"+entry.getValue());    }}

Map集合的实现类

HashMap【重点】

  • JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。
/** * hashMap的使用 * 存储结构:哈希表(数组+链表+红黑树) */public class Demo02 {    public static void main(String[] args) {        HashMap<Person, String> hashMap = new HashMap<>();        Person p1 = new Person("张三", 20);        Person p2 = new Person("李四", 22);        Person p3 = new Person("王五", 25);        hashMap.put(p1,"深圳");        hashMap.put(p2,"上海");        hashMap.put(p3,"湖北");        System.out.println(hashMap.toString());        //添加失败,但会更新值        hashMap.put(p3,"湖北");        //添加成功,不过两个属性一模一样;        //注:假如相同属性便认为是同一个对象,怎么修改?(重写hashCode和equals方法)        //hashMap.put(new Person("王五", 25),"湖北");        //System.out.println(hashMap.toString());        //2.删除元素//        hashMap.remove(p1);//        System.out.println(hashMap.toString());        //3.遍历        System.out.println("---使用keySet遍历----");        for (Person p:hashMap.keySet())            System.out.println(p+"-------"+hashMap.get(p));        System.out.println("----使用EntrySet遍历-----");        for (Map.Entry<Person,String> entry:hashMap.entrySet())            System.out.println(entry.getKey()+"---------"+entry.getValue());    }}

重写hashCode和equals方法可以判断元素是否重复

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

Hashtable

  • JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。

  • 初始容量11,加载因子0.75。

    这个集合在开发过程中已经不用了,稍微了解即可。

Properties

  • Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。

它继承了Hashtable的方法,与流关系密切,此处不详解。

TreeMap

  • 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
/** * TreeMap的使用 * 存储结构:红黑树 */public class Demo03 {    public static void main(String[] args) {        TreeMap<Person, String> treeMap=new TreeMap<Person, String>();        Person p1 = new Person("张三", 20);        Person p2 = new Person("李四", 22);        Person p3 = new Person("王五", 25);        //1.添加元素        treeMap.put(p1,"深圳");        treeMap.put(p2,"上海");        treeMap.put(p3,"湖北");        //不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小        System.out.println(treeMap.toString());        //2.删除元素//        treeMap.remove(new Person("王五", 25));//        System.out.println(treeMap.toString());        //3.遍历        //3.1 使用keySet()        for (Person key : treeMap.keySet()) {            System.out.println(key+" "+treeMap.get(key));        }        //3.2 使用entrySet()        for (Map.Entry<Person, String> entry : treeMap.entrySet()) {            System.out.println(entry.getKey()+" "+entry.getValue());        }        //4.判断        System.out.println(treeMap.containsKey(p1));        System.out.println(treeMap.isEmpty());    }}

Collection工具集

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

  • int i = Collections.binarySearch(list, x); 直接二分查找,成功返回索引
  • public static void reverse(List<?> list)反转集合中元素的顺序
  • public static void shuffle(List<?> list)随机重置集合元素的顺序
  • public static void sort(List<T> list)升序排序(元素类型必须实现Comparable接口)
  • Collections.copy(dest,src)复制,dest目标数组,src源数组

补充:

// list转成数组Integer[] arr = list.toArray(new Integer[10]);sout(arr.length);sout(Array.toString(arr));// 数组转成集合// 此时为受限集合,不能 添加和删除!String[] name = {"张三","李四","王五"};List<String> list2 = Arrays.asList(names);// 把基本类型数组转为集合时,需要修改为包装类Integer[] nums = {100, 200, 300, 400, 500};List<Integer> list3 = Arrays.asList(nums);