Java集合框架详解
一 、java集合框架常用的构成体系
接口: Collection Map
子接口: List Set
常用实现类: ArrayList LinkedList Vector HashSet LinkedHashSet TreeSet HashMap LinkedHashMap TreeMap Hashtable
工具类: Collections
二、四个接口的区别
1.Collection接口存储无序不唯一数据
2.List接口存储有序不唯一的数据
3.Set接口存储无序唯一的数据
4.Map接口以键值对的形式存储数据,以键取值,键唯一,值可以重复
三、List接口
1、list接口常用方法
add(E):在列表的最后添加元素;E是元素的泛型
add(index,E):在列表的指定位置插入元素
size():返回当前列表的元素个数
get(index):返回下标index的元素,如果没有泛型约数返回Object类型,需要强转;如果有泛型约数,直接返回泛型类型无需强转
clear():清除列表里的所有数据;
isEmpty():检查列表是否为空;
contains():contains底层是调用equals方法,传入一个对象,检查列表中是否包含该对象;
如果传入的是String和基本数据类型,可以直接比较,因为String类自己重写了equals,基本数据类型比较地址就可以;
indexOf():传入对象,返回该对象在列表中第一次出现的索引;如果传入对象和contains一样要在实体类重写equals();
lastIndexOf():传入对象,返回该对象在列表中最后一次出现的索引;如果传入对象和contains一样要在实体类重写equals();
remove():传入一个下标删除列表中对应的元素,返回删除的元素对象,若下标超出集合长度,报下标越界异常。
传入一个对象,需要重写实体类的equals方法,返回boolean值
set(index,o):传入修改的下标,和新传入的对象,将指定位置的下标处的原元素替换成新元素,返回原来元素对象
subList(index,index):返回 List 集合;
toArray():返回对象数组 Object[] o ;
iterator():返回迭代对象
2.ArrayList
实现了一个长度可变的数组,在内存空间开辟一串连续的空间
这种存储结构,在循环遍历和随机访问元素的速度非常快
3.LinkedList
使用链表结构存储数据,在插入和删除元素时速度非常快
特有常用方法:
addFirst():开头添加元素
addLast():末尾添加元素
removeFirst():删除第一个元素,并返回第一个元素
removeLast():删除最后一个元素,并返回返回最后一个元素
getFirst():获取第一个元
getLast():获取最后一个元素
4.遍历集合
①使用for循环
②使用forEach遍历
③使用迭代器(iterator)遍历列表,使用列表调用.iteration()返回迭代器对象,
使用迭代器对象调用.hasNext()方法判断是否又下一条数据,使用迭代器对象调用.next()方法取出下一条数据
ArrayList<String> list = new ArrayList<String>(); list.add("list1"); list.add("list2"); list.add("list3"); list.add("list4"); list.add(2,"123"); list.add(null); int listLength = list.size(); /** * 使用for循环遍历列表 */ for (int i = 0; i < listLength; i++) { String s = list.get(i); System.out.println(s); } System.out.println("-------------------存储实体类---------------------"); ArrayList<News> list2 = new ArrayList<News>(); list2.add(new News(1,"等过","zhang")); list2.add(new News(2,"啦啦","wang")); list2.add(new News(3,"哈哈","li")); list2.add(new News(2,"啦啦","wang"));/** * forEach循环 */ for (News news : list2) { if (news == null) { System.out.println("---"); continue; } System.out.println(news.getId()+" "+news.getTitle()+" "+news.getAuthor()); } /** * 使用迭代器(iterator)遍历列表 * 使用列表调用.iterator()返回迭代器对象 * 使用迭代器对象调用.hasNext()方法判断是否又下一条数据 * 使用迭代器对象调用.next()方法取出下一条数据 */ System.out.println("--------------迭代器输出----------------"); Iterator<News> iter = list2.iterator(); while(iter.hasNext()){ News news = iter.next(); if (news == null) { System.out.println("---"); continue; } System.out.println(news.getId()+news.getAuthor()+news.getTitle()); }
class News{ private int id; private String author; private String title;public News() { super(); } public News(int id, String author, String title) { super(); this.id = id; this.author = author; this.title = title; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } }
四、Set接口
1.常用方法
2.HashSet
b.如果相同则重写equals方法,再判断内容是否相等
所以>>>使用HashSet存储实体对象时,必须重写实体对象的hashCode()和equals()方法
Set<Person> set2 = new HashSet<Person>(); set2.add(new Person(1,"校长",15)); set2.add(new Person(2,"小李",11)); set2.add(new Person(3,"下哈",18)); set2.add(new Person(4,"小妹",13)); //实体对象new一个就是一个对象,set默认比较的是地址所以它不是重复对象可以存储; //若要判断对象内容是否一致,需要重写实体类hashCode()方法和equals()方法 set2.add(new Person(3,"小李",11)); set2.add(null);//不知道为什么会最先输出来 set2.add(null);//set接口的唯一性,所以只能存储一个null //HashSet遍历出来是无序的 Iterator<Person> iterator2 = set2.iterator(); while(iterator2.hasNext()){ Person p = iterator2.next(); if (p == null) { System.out.println("---"); continue; } System.out.println(p.getId()+" "+p.getName()+" "+p.getAge()); }
class Person{ private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Person() { super(); } public Person(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + id; 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 (id != other.id) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
3.LinkedHashSet
4.TreeSet
将存入的数据按照二叉树的规则排序,然后输出(数字好像是从小到大的);如果存入实体类对象:必须给定比较两个实体类的规则(说明按照这个实体的那一属性来区分他们)
方法一:需要实体类实现Comparable接口,重写compareTo()方法
Set<Student> set2 = new TreeSet<Student>(); set2.add(new Student(1,"战三",15)); set2.add(new Student(3,"张三",12)); set2.add(new Student(2,"李四",14)); set2.add(new Student(4,"王伟",11)); set2.add(new Student(3,"赵三",11));//按照只比较id来确定是否重复则这条不能存储;按照名字来比较则不重复 //set2.add(null);//当使用compareTo来定义比较规则时,由于该方法不允许传入null所以TreeSet再这种情况下不能存储null System.out.println(set2.size()); Iterator<Student> iterator2 = set2.iterator(); while (iterator2.hasNext()) { Student p = iterator2.next(); System.out.println(p.getId()+p.getName()+p.getAge()); }
class Student implements Comparable<Student>{ private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Student(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public Student() { super(); } @Override public int compareTo(Student student) { return this.id - student.id; //return this.name.compareTo(student.getName()); }
方法二:
①在实例化TreeSet的同时,通过构造函数传入一个比较器:比较器: 一个实现了Comparator接口,并重写了compare()方法的实现类的对象。
因此需要定义一个类实现比较器接口,重写compare()方法;
Set<Teacher> set1 = new TreeSet<Teacher>(new CompImpl()); set1.add(new Teacher(1,"战三",15)); set1.add(new Teacher(3,"张三",12)); set1.add(new Teacher(2,"李四",14)); set1.add(new Teacher(4,"王伟",11)); set1.add(new Teacher(3,"赵三",11));//按照只比较id来确定是否重复则这条不能存储;按照名字来比较则不重复 set1.add(null);//当使用compare来定义比较规则时,由于该方法允许传入null所以TreeSet再这种情况下能存储null System.out.println(set1.size());//输出5说明null存进去了 Iterator<Teacher> iterator1 = set1.iterator(); while (iterator1.hasNext()) { Teacher p = iterator1.next(); if (p == null) { System.out.println("---"); continue; } System.out.println(p.getId()+p.getName()+p.getAge()); }
class CompImpl implements Comparator<Teacher>{ @Override public int compare(Teacher t1, Teacher t2) { if (t1 == null || t2 == null) { return -1; //return 1; } return t1.getId() - t2.getId(); //return t1.getName().compareTo(t2.getName()); } }
class Teacher{ private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Teacher(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public Teacher() { super(); } }
Set<Doctor> set1 = new TreeSet<Doctor>(new Comparator<Doctor>() { @Override public int compare(Doctor d1, Doctor d2) { // TODO Auto-generated method stub if (d1 == null || d2 == null) { return -1; //return 1; } return d1.getId() - d2.getId(); } }); set1.add(new Doctor(1,"战三",15)); set1.add(new Doctor(3,"张三",12)); set1.add(new Doctor(2,"李四",14)); set1.add(new Doctor(4,"王伟",11)); set1.add(new Doctor(3,"赵三",11));//按照只比较id来确定是否重复则这条不能存储;按照名字来比较则不重复 set1.add(null); Iterator<Doctor> iterator1 = set1.iterator(); while (iterator1.hasNext()) { Doctor d = iterator1.next(); if (d == null) { System.out.println("---"); continue; } System.out.println(d.getId()+d.getName()+d.getAge()); }
class Doctor{ private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Doctor(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public Doctor() { super(); } }
5.遍历
因为Set集合没有顺序所以只能有这两种遍历方法:forEach和 Iterator
6.comparable接口和Comparator接口区别
①前者是java.lang包下的接口,后者是java.util包下的②前者由实体类实现,重写compareTo()方法,TreeSet调用空参构造器使用;
后者重新定义一个实现类来实现该接口,重写compare()方法,TreeSet调用含此接口的有参构造器使用;
③前者的compareTo()方法不允许传入null值(在方法体判空处理也不行),后者的compare()方法允许传入null,只是需要在方法体内进行判空处理即可
https://www.cnblogs.com/szlbm/p/5504634.html
五、Map接口
1.Map接口的特点:以键值对的形式存储数据,以键取值,键唯一值可重复 ;没有继承Iterator接口
2.常用方法:
put(key,value):向map最后追加一个键值对;
get(key):获取key所对应的值
clear():清除所有键值对
containsValue(obj):检测是否包含指定的值
containsKey(obj):检测是否包含指定的键
replace(K, V):替换指定的K的V
keySet():返回Set集合泛型为K
values():返回Collcetion集合泛型为V
entrySet():返回Set集合泛型为Entry<K,V>
3.遍历方式
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(1, 1); map.put(1, 1); map.put(2, 4); map.put(3, 3); map.put(null, null); System.out.println("--------keySet-----------"); /** * 遍历Map的方式一 */ Set<Integer> keys = map.keySet(); Iterator<Integer> iterator = keys.iterator(); while (iterator.hasNext()) { Integer k = iterator.next(); System.out.println(k+"----"+map.get(k)); } /** * 遍历Map的方式二 */ System.out.println("--------------直接取值-------------"); Collection<Integer> values = map.values(); Iterator<Integer> iterator2 = values.iterator(); while (iterator2.hasNext()) { System.out.println(iterator2.next()); } /** * 遍历Map的方式三 */ System.out.println("--------------entry遍历--------------"); Set<Entry<Integer, Integer>> set = map.entrySet(); Iterator<Entry<Integer, Integer>> iterator3 = set.iterator(); while (iterator3.hasNext()) { //Entry是Java给我们提供的一种特殊的数据类型,其实就是一个键值对 //键就是当前这条记录的键,使用getKey()取到; //值就是当前这条记录的值,使用getValue()取到; Entry<Integer, Integer> entry = iterator3.next(); //System.out.println(entry); System.out.println(entry.getKey()+"-------"+entry.getValue()); }
4.HashMap和HashTable的区别
1.HashMap是线程不安全(不同步),HashTable是线程安全的(同步的)
2.HashMap的键可以为null,HashTable的键不可以为null
https://blog.csdn.net/qq_35181209/article/details/74503362
六.关于这些常用的实现类能否存null的总结
ArrayList:可以存多个null,并且按照顺序输出;无论是否在实体类下
LinkedList:在存储非实体类时可以存多个null,并且按照顺序输出;实体类下面无法存储null
HashSet:只能存储一个null并且输出时候在最前面;无论是否在实体类下
LinkedHashSet:只能存储一个null并且输出时候按照输入顺序输出;无论是否在实体类下
TreeSet:底层默认用comparable接口的方法compareTo(),所以无法存入null;只有在存入实体类是自己定义比较规则时候,并且使用Compartor接口的实现类对象做构造函数才能存入
null,可以通过实体类的编码实现存多个null,输出的null在最前面
HashMap:可以存储一个null值,并且输出靠前;
LinkedHashMap:可以存储一个null,输出按照输入的顺序
TreeMap:同TreeSet