集合体系图
set集合存储的元素是无序的,不可以重复的
HashSet<String> sh = new HashSet<>(); sh.add("a"); sh.add("b"); sh.add("c"); sh.add("c"); System.out.println(sh);
HashSet存储自定义对象保证元素唯一性
重写hashCode()和equal()方法
自动生成这两种方法即可
HashSet如何保证元素唯一性的原理
/* * 为什么是31? * 1,31是质数 * 2,31既不大也不小 * 3,31是2的五次方-1 */ @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; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) //调用的对象和传入的对象是同一个对象 return true; //直接返回true if (obj == null) //传入的对象为null return false; //返回false if (getClass() != obj.getClass()) //判断两个对象的字节码文件是否是同一个 return false; //如果不是直接返回false Person other = (Person) obj; //向下转型 if (age != other.age) //调用对象的年龄不等于传入对象的年龄 return false; //返回false if (name == null) { //调用对象的姓名为null if (other.name != null) //传入对象的姓名不为null return false; //返回false } else if (!name.equals(other.name)) //调用对象的姓名不等于传入对象的姓名 return false; return true; }
LinkedHashSet的特点
底层是链表实现的,是set集合中唯一一个能保证怎么存怎么取的集合对象
因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样
/* * 获取10个1到20的随机数,要求随机数不能重复,并把最终结果输出到控制台 * 1,用Random生成随机数 * 2,要求不能重复,所以我们使用HashSet * 3,需要10个数,则集合的size小于10就可以继续存放,>=10就不能再存储 * 4,用Random类中的nextInt(n)方法获取1到20的随机数,并把他们保存在集合中 * 5,遍历HashSet */ public class Demo1 { public static void main(String[] args) { Random r = new Random(); HashSet<Integer> hs = new HashSet<>(); while(hs.size()<10) { int num = r.nextInt(20)+1; hs.add(num); } for (Integer integer : hs) { System.out.println(integer); } } }
/* * 将集合中的重复元素去掉 */ public class Test2 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("b"); list.add("a"); list.add("c"); getSingle(list); System.out.println(list); } public static void getSingle(List<String> list) { LinkedHashSet<String> lhs = new LinkedHashSet<>(); // lhs.addAll(list); String[] arr = (String[]) list.toArray(new String[0]); for (String c : arr) { lhs.add(c);//去重 } list.clear();//清空原集合中的内容 list.addAll(lhs);//把去重后的元素加入到list中 } }
TreeSet
TreeSet集合是用来对元素进行排序的,同样也可以保证元素的唯一
- 当compareTo方法返回 0 的时候,集合中只有一个元素;
- 当compareTo方法返回 正数 的时候,集合会怎么存怎么取;
- 当compareTo方法返回 负数 的时候,集合中会倒序存储。
当compareTo方法返回 this.age-o.age时
TreeSet<Person> ts = new TreeSet<>(); ts.add(new Person("张珊",22)); ts.add(new Person("杨振",23)); ts.add(new Person("安琪",23)); ts.add(new Person("盼盼",25)); ts.add(new Person("张珊",22)); System.out.println(ts);
@Override public int compareTo(Person o) { int num=this.age-o.age; //年龄是比较的主要条件 return num==0?this.name.compareTo(o.name):num; //姓名是次要条件 }
@Override public int compareTo(Person o) { int length = this.name.length()-o.name.length(); //比较长度为主要条件 int num = length== 0 ? this.name.compareTo(o.name):length; //比较内容为次要条件 return num==0?this.age-o.age:num; //比较年龄也为次要条件 }
存储Integer类型的元素并遍历
TreeSet<Integer> ts = new TreeSet<>(); ts.add(1); ts.add(3); ts.add(3); ts.add(1); ts.add(2); ts.add(1); System.out.println(ts);
TreeSet保证元素唯一和比较器排序的原理
class CompareByLen implements Comparator<String>{ @Override public int compare(String s1, String s2) { //按照字符串的长度进行比较 int num=s1.length()-s2.length(); //长度为主要条件 return num==0?s1.compareTo(s2):num; //内容为次要条件 } }
TreeSet<String> ts = new TreeSet<>(new CompareByLen());//CompareByLen c = new CompareByLen(); ts.add("aaaaaa"); ts.add("bbbb"); ts.add("cc"); ts.add("csca"); ts.add("vfda"); System.out.println(ts);
TreeSet原理
1.特点
Treeset是用来排序的,可以指定一个顺序,对象存入之后会按照指定的顺序排列。
2使用方式
a.自然顺序(Comparable)
TreeSet类的add()方法中会把存入的对象提升为Conparable类型
调用对象的compareT0()方法和集合中的对象比较
根据compare T0()方法返回的结果进行存储
b.比较器顺序(Comparator)
创建TreeSet的时候可以制定一个Comparator
如果传入了 comparator的子类对象,那么TreeSet就会按照比较a中的顺序排序
add()方法内部会自动a用comparator接口中compare ()方法排序
调用的对象是compare方法的第一个参数集台中的对象是compare方法的第二个参数
c. 两种方式的区别
TreeSet构造函数什么都不传,默认按照类中Comparable的顺序(没有就报classcastException)
TreeSet如果传入comparator.优先按照comparator
eg : 在一个集合中存储了无序并且重复的字符串,定义一个方法,让其有序,而且还不能重复
import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.TreeSet; /* * 在一个集合中存储了无序并且重复的字符串,定义一个方法,让其有序,而且还不能重复 */ public class Test3 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("aaa"); list.add("aaa"); list.add("ccc"); list.add("ddd"); list.add("ffffffff"); list.add("heima"); list.add("itcast"); list.add("aaa"); list.add("aaa"); sort(list); System.out.println(list); } /* * 定义方法进行排序并保留重复 * 1,创建TreeSet集合对象,因为String本身具备比较功能,但是重复不会保留,所以我们要使用比较器 * 2,将list集合所有的元素添加到TreeSet集合中,对其排序,保留重复 * 3,清空list集合 * 4,将TreeSet排好序的元素添加到list中 */ public static void sort(List<String> list) { TreeSet<String> ts = new TreeSet<>(new Comparator<String>() { @Override public int compare(String s1, String s2) { int num=s1.compareTo(s2); return num==0?1:num; } }); ts.addAll(list); list.clear(); list.addAll(ts); } }
Map集合
A:Map接口概述
查看API可以知道:
* 将键映射到值的对象
* 一个映射不能包含重复的键
* 每个键最多只能映射到一个值
B:Map接口和Collection接口的不同
* Map是双列的,Collection是单列的
* Map的键唯一,Collection的子体系Set是唯一的
* Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
HashSet与HashMap,都是双列集合,只不过HashSet将value隐藏不显示。
Map集合的功能概述
* a:添加功能
* V put(K key,V value):添加元素。
* 如果键是第一次存储,就直接存储元素,返回null
* 如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
* b:删除功能
* void clear():移除所有的键值对元素
* V remove(Object key):根据键删除键值对元素,并把值返回
* c:判断功能
* boolean containsKey(Object key):判断集合是否包含指定的键
* boolean containsValue(Object value):判断集合是否包含指定的值
* boolean isEmpty():判断集合是否为空
* d:获取功能
* Set<Map.Entry<K,V>> entrySet():
* V get(Object key):根据键获取值
* Set<K> keySet():获取集合中所有键的集合
* Collection<V> values():获取集合中所有值的集合
* e:长度功能
* int size():返回集合中的键值对的个数
Map集合的遍历之键找值
- 获取所有键的集合
- 遍历键的集合,获取到每一个键
- 根据键找值
Set<String> setKey = map.keySet(); Iterator it = setKey.iterator(); while(it.hasNext()) { String key = (String) it.next(); Integer i = map.get(key); System.out.println(key+"="+i);}
或
for(Entry<String,Integer> en : hm.entrySet()) { System.out.println(en.getKey() + "=" + en.getValue()); }
Map集合的遍历之键值对对象找键和值
- 获取所有键值对对象的集合
- 遍历键值对对象的集合,获取到每一个键值对对象
- 根据键值对对象找键和值
Set<Map.Entry<String, Integer>> en = map.entrySet(); Iterator<Map.Entry<String, Integer>> it = en.iterator(); while(it.hasNext()) { Map.Entry<String, Integer> m = it.next(); String key = m.getKey(); Integer value = m.getValue(); ystem.out.println(key+"="+value);}
或
for(Map.Entry<String, Integer> e : map.entrySet()) { System.out.println(e.getKey()+"="+e.getValue()); }
LinkedHashMap
底层是链表,实现的可以保证怎么存就怎么取
HashMap和Hashtable的区别
1. Hashtable是JDK1.0版本出现的,是线程安全的,效率低
HashMap是JDK1.2版本出现的,是线程不安全的,效率高
2. Hashtable不可以存储null键和null值
HashMap可以存储null键和null值
Collections工具类的概述和常见方法讲解
针对集合操作的工具类
Collections成员方法
- public static <T> void sort(List<T> list) //排序
- public static <T> int binarySearch(List<?> list,T key) //二分查找
- public static <T> T max(Collection<?> coll) //根据默认排序找出最大值
- public static void reverse(List<?> list) //反转
- public static void shuffle(List<?> list) //随机置换,洗牌
泛型固定上边界
class World { } class Person extends World { } class Teacher extends Person { }
泛型固定下边界
类型区别
HashMap
最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。
TreeMap
能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。
Hashtable
与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
LinkedHashMap
保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。
三种遍历总结
- 增强for循环使用方便,但性能较差,不适合处理超大量级的数据。
- 迭代器的遍历速度要比增强for循环快很多,是增强for循环的2倍左右。
- 使用entrySet遍历的速度要比keySet快很多,是keySet的1.5倍左右。
常用API
clear()
|
从 Map 中删除所有映射
|
remove(Object key)
|
从 Map 中删除键和关联的值
|
put(Object key, Object value)
|
将指定值与指定键相关联
|
putAll(Map t)
|
将指定 Map 中的所有映射复制到此 map
|
entrySet()
|
返回 Map 中所包含映射的 Set 视图。Set 中的每个元素都是一个 Map.Entry 对象,可以使用 getKey() 和 getValue() 方法(还有一个 setValue() 方法)访问后者的键元素和值元素
|
keySet()
|
返回 Map 中所包含键的 Set 视图。删除 Set 中的元素还将删除 Map 中相应的映射(键和值)
|
values()
|
返回 map 中所包含值的 Collection 视图。删除 Collection 中的元素还将删除 Map 中相应的映射(键和值)
|
get(Object key)
|
返回与指定键关联的值
|
containsKey(Object key)
|
如果 Map 包含指定键的映射,则返回 true
|
containsValue(Object value)
|
如果此 Map 将一个或多个键映射到指定值,则返回 true
|
isEmpty()
|
如果 Map 不包含键-值映射,则返回 true
|
size()
|
返回 Map 中的键-值映射的数目
|