一 Set集合
1 特点
无序,唯一
2 增强for循环来遍历
(所有的Collection都可以用这个增强for循环遍历)
for (String s : set) {
System.out.println(s);
}
二 HashSet集合
1)底层数据结构是哈希表(是一个元素为链表的数组)
2)保证元素唯一性的方式依赖于结合中存储元素的hashCode()和equals()方法。
执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合
3)开发的时候,非常简单,代码自动生成即可。
4)通过写代码发现,如果只写equals()也可以正确执行的,来区分不同的对象。那为什么还一定要写hashCode()函数呢?(不可以直接在hashCode()中写return 0;)
因为,如果只写equals(),虽然解决了重复问题,但是效率较低。
所以要让哈希值尽量不同。最终方案就是:
把对象的成员变量值进行相加。如果是基本类型,就直接加值。如果是引用类型,就加哈希值
(但有时候为了区分开很多元素,所以尽可能的把返回值的表达式变得复杂一些)
5)案例:
一 HashSet存储字符串并遍历
用for循环即可
二 HashSet存储自定义对象并遍历(对象的成员变量值相同即为同一个元素)
如果只是用刚才的方法,会发现有问题。
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<Student>();
hs.add(new Student("林青霞", 27));
hs.add(new Student("柳岩", 22));
hs.add(new Student("王祖贤", 30));
hs.add(new Student("林青霞", 27));
hs.add(new Student("林青霞", 20));
hs.add(new Student("范冰冰", 22));
for(Student s: hs){
System.out.println(s.toString());
}
}
具体表现是:
输出了两个Student("林青霞", 27)。因为没有改写hashCode()、equals()函数时,默认的是比较地址值的。所以add()函数会发生逻辑上的错误。
三 TreeSet集合
1)底层数据结构是红黑树(是一个自平衡的二叉树)
2)保证元素的排序方式
a:自然排序(元素具备比较性)
让元素所属的类实现Comparable接口,重写compareTo()方法
b:比较器排序(集合具备比较性)
让集合构造方法接收Comparator的实现类对象,Comparator()的compare()方法重写
方法二更方便一些,案例如下:
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>(){
public int compare(Student s1, Student s2){ // (是传2个参数, 实现Comparable接口是传1个参数)
// 先按年龄比较,如果相同,再调用名字(String)的compareTo()方法
int num1 = s1.getAge() - s2.getAge();
int num2 = num1 == 0 ? (s1.getName().compareTo(s2.getName())) : num1;
return num2;
}
});
hs.add(new Student("林青霞", 27));
hs.add(new Student("柳岩", 22));
hs.add(new Student("王祖贤", 30));
hs.add(new Student("林青霞", 27));
hs.add(new Student("林青霞", 20));
hs.add(new Student("范冰冰", 22));
for(Student s: hs){
System.out.println(s.toString());
}
}
注意: s1 - s2 的是升序;反之是降序
四Collection集合总结
1
Collection
|--List 有序,可重复
|--ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高
|--Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低
|--LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高
|--Set 无序,唯一
|--HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()
开发中自动生成这两个方法即可
|--LinkedHashSet
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一
|--TreeSet
底层数据结构是红黑树。
如何保证元素排序的呢?
自然排序
比较器排序
如何保证元素唯一性的呢?
根据比较的返回值是否是0来决定
2 针对Collection集合我们到底使用谁呢?
唯一吗?
是:Set
排序吗?
是:TreeSet
否:HashSet
如果你知道是Set,但是不知道是哪个Set,就用HashSet。
否:List
要安全吗?
是:Vector
否:ArrayList或者LinkedList
查询多:ArrayList
增删多:LinkedList
如果你知道是List,但是不知道是哪个List,就用ArrayList。
如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
如果你知道用集合,就用ArrayList。
3 在集合中常见的数据结构
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
五 Map集合
1将键映射到值的对象(或者说键值对)。一个映射不能包含重复的键(键唯一)。
2 Map和Collection的区别?
A:Map 存储的是键值对形式的元素,键唯一,值可以重复。
B:Collection 存储的是单独出现的元素
3 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():获取集合中所有值的集合
代码:
Collection<String> con = map.values();
for(String value: con){
System.out.println(value);
}
E长度功能
int size():返回集合中的键值对的对数
4 Map集合的遍历
方式1:
Set<String> set = map.keySet();
for(String key: set){
System.out.println(map.get(key));
}
方式2:
Set<Map.Entry<String, String>> set = map.entrySet();
for(Map.Entry<String, String> me: set){
String key = me.getKey();
String value = me.getValue();
System.out.println(key + " " + value);
}
5 HashMap 集合
哈希表的作用是用来保证键的唯一性的。所以
new HashMap<K, V> 中的K必须有hashCode()、equals(),否则无法正确的保存数据。
6 TreeMap集合
同TreeSet,也是要保证元素的排序方式:自然排序或者是比较器排序。
7 Hashtable
Hashtable和HashMap的区别?
Hashtable:线程安全,效率低。不允许null键和null值
HashMap:线程不安全,效率高。允许null键和null值
代码1:
public static void main(String[] args) {
Hashtable<String, String> ht = new Hashtable<String, String>();
ht.put("it001", "hello");
//ht.put(null, "world"); NullPointerException
//ht.put("java", null); NullPointerException
System.out.println(ht);
}
7 案例
需求:"aababcabcdabcde",获取字符串中每一个字母出现的次数,要求结果:a(5)b(4)c(3)d(2)e(1)
代码:
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
char[] chs = line.toCharArray();
for(char ch: chs){
Integer i = tm.get(ch);
if(i == null){
tm.put(ch, 1);
}else{
tmp.put(ch, i+1);
}
}
StringBuiler sb = new StringBuiler();
Set<Character> set = tm.keySet();
for(Character key: set){
Integer value = tm.get(key);
sb.append(key).append("(").append(value).append(")");
}
System.out.println(sb.toString());
}
六 Collections
1 是针对集合进行操作的工具类,可以对集合进行排序和查找等
2 常见的几个小方法:
A:public static <T> void sort(List<T> list)
B:public static <T> int binarySearch(List<?> list,T key)
C:public static <T> T max(Collection<?> coll)
D:public static void reverse(List<?> list)
E:public static void shuffle(List<?> list) // 比如可以模拟洗牌
3 案例
代码1:
Collections可以针对ArrayList存储基本包装类的元素排序,存储自定义对象也可以。
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
list.add(new Student("林青霞", 27));
list.add(new Student("风清扬", 30));
list.add(new Student("刘晓曲", 28));
list.add(new Student("武鑫", 29));
list.add(new Student("林青霞", 27));
// 1 自然排序
// Collections.sort(list);
// 2 比较器排序
Collections.sort(list, new Comparator<Student>(){
public int compare(Student s1, Student s2) { // 是传两个参数
int num = s2.getAge() - s1.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
return num2;
}
})
for(Student s: list){
System.out.println(s);
}
}