Set
存储的数据是不重复的,无序的。
HashSet
为什么是无序的呢?这是因为HashSet中运用了Hash算法来计算插入Set中的数据的位置(幂等性算法)。
为什么是不重复的呢?因为Hash算法是幂等性算法,也就是说同一个值经过Hash算法计算出的位置是一定的。而存储相同数据相当于会进行覆盖,这样是没有意义的。所以HashSet也不会这样做,不会进行任何处理。我们可以插入相同的数据,但是存储的数据不会有重复的。
添加数据
HashSet set = new HashSet();
set.add("kevin");
set.add("kevin");
set.add("qian");
set.add("kun");
set.add("yt");
System.out.println(set);//[qian, kevin, kun, yt]
修改数据
由于Hash算法的存在,无法确定修改后的数据经过Hash算法得出的位置与原位置一致,所以也无法修改数据。只能删除了数据后再进行添加。
删除数据
HashSet set = new HashSet();
set.add("kevin");
set.add("qian");
set.add("kun");
set.remove("kun");
System.out.println(set);// [qian, kevin]
查询数据
只能使用for循环查询数据
HashSet set = new HashSet();
set.add("kevin");
set.add("qian");
set.add("kun");
for (Object o : set) {
System.out.println(o);
}
// qian
// kevin
// kun
其他常用方法
Set的其他方法与List的差不多:
HashSet set = new HashSet();
ArrayList list = new ArrayList();
list.add("kevin");
list.add("kevin");
list.add("qian");
set.addAll(list);
Object[] objects = set.toArray();
Object clone = set.clone();
// set.clear();
System.out.println(set);// [qian, kevin]
System.out.println(set.size());// 2
System.out.println(set.isEmpty());// false
HashSet重复数据
对于new一个对象,它们的地址肯定是不同的,所以HashSet认为这是两个不同的数据
HashSet<Student> set = new HashSet();
set.add(new Student(1001, "kevin"));
set.add(new Student(1001, "kevin"));
set.add(new Student(1002, "qian"));
for (Student student : set) {
System.out.println("id:" + student.id + ",name:" + student.name);
}
// id:1002,name:qian
// id:1001,name:kevin
// id:1001,name:kevin
在进行hash运算时,就是通过hashcode的值来进行运算的。
那么有没有可能不同的hashcode被hash运算计算为同一个位置呢?这是可能的,新的值会像链表结构一样被放在已经在当前位置的数据的后面
所以说HashSet的底层数据结构为数组+链表
如果我们想让传入的引用类型数据的构造时传入的参数一致,就认为是同一数据,我们需要重写hashCode和equals
public class JavaCollection_08 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet();
set.add(new Student(1001, "kevin"));
set.add(new Student(1001, "kevin"));
set.add(new Student(1002, "qian"));
for (Student student : set) {
System.out.println("id:" + student.id + ",name:" + student.name);
}
// id:1001,name:kevin
// id:1002,name:qian
}
}
class Student {
public int id;
public String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
return this.id;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Student) {
Student theStudent = (Student) obj;
if (theStudent.id == this.id || theStudent.name.equals(this.name)) {
return true;
}
}
return false;
}
}
Queue
ArrayBlockingQueue
Array+blocking(阻塞)+Queue
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.add("kevin");
queue.add("qian");
queue.add("kun");
System.out.println(queue);// [kevin, qian, kun]
ArrayBlockingQueue有个数限制。
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.add("kevin");
queue.add("qian");
queue.add("kun");
queue.add("yes");// 报错:Queue full
System.out.println(queue);// [kevin, qian, kun]
如何表现出Blocking?使用put添加数据
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.put("kevin");
System.out.println("第一个");// 第一个
queue.put("qian");
System.out.println("第二个");// 第二个
queue.put("kun");
System.out.println("第三个");// 第三个
queue.put("yes");// 由于blocking,不继续执行后面的代码。整个程序不会结束,会一直blocking
System.out.println("第四个");
System.out.println(queue);
我们可以使用offer存值,它会返回一个布尔值。成功为true,失败为false
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
System.out.println(queue.offer("kevin"));// true
System.out.println(queue.offer("qian"));// true
System.out.println(queue.offer("kun"));// true
System.out.println(queue.offer("yt"));// false
System.out.println(queue);// [kevin, qian, kun]
可以使用poll方法取出来值,返回值为取出的值。
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer("kevin");
queue.offer("qian");
queue.offer("kun");
queue.offer("yt");
System.out.println(queue.poll());// kevin
System.out.println(queue.poll());// qian
System.out.println(queue.poll());// kun
System.out.println(queue.poll());// null
System.out.println(queue);// []
也可以使用take取值,当取完set中的值后再取值也会blocking:
ArrayBlockingQueue queue = new ArrayBlockingQueue(3);
queue.offer("kevin");
queue.offer("qian");
queue.offer("kun");
queue.offer("yt");
System.out.println(queue.take());// kevin
System.out.println(queue.take());// qian
System.out.println(queue.take());// kun
System.out.println(queue.take());// 会blocking,后面的代码不会执行,并且程序不会退出。
System.out.println(queue);
Map
键值对集合
HashMap
类似HashSet,但是有覆盖的概念。根据key定位,value不同会进行覆盖。
当key不同,但通过hash算法得到了同一个位置时。会通过链表结构存储,并且是单向链表。如果单向链表中数据存储的很多。查询起来效率很低,所以jdk中提供了一种特殊的结构——红黑二叉树。
添加数据/修改数据
覆盖数据时,返回值为原数据。如果无原数据返回为空(null)
HashMap map = new HashMap();
map.put("kevin", 1);
map.put("qian", 2);
System.out.println(map.put("kun", 3));// null
System.out.println(map.put("kevin", 4));// 1
System.out.println(map);// {qian=2, kevin=4, kun=3}
也可以使用putIfAbsent
进行添加数据,如果有数据就不做处理,并返回那个数据的value
HashMap map = new HashMap();
map.putIfAbsent("kevin", 1);
System.out.println(map.putIfAbsent("qian", 2));// null
System.out.println(map.putIfAbsent("kevin", 4));// 1
System.out.println(map);// {qian=2, kevin=1}
而修改数据可以使用replace
方法,返回原数据的value
HashMap map = new HashMap();
map.putIfAbsent("kevin", 1);
System.out.println(map.replace("kevin", 3));// 1
System.out.println(map.replace("yt", 3));// null
System.out.println(map);// {kevin=3}
查询数据
返回值为value
HashMap map = new HashMap();
map.put("kevin", 1);
map.put("qian", 2);
map.put("kun", 3);
map.put("kevin", 4);
System.out.println(map.get("kevin"));// 4
删除数据
HashMap map = new HashMap();
map.put("kevin", 1);
map.put("qian", 2);
map.put("kun", 3);
map.put("kevin", 4);
System.out.println(map.remove("kevin"));// 4
System.out.println(map);// {qian=2, kun=3}
当传两个值时是value为传入值时才删除,这时候返回值为是否删除(或者是否有这个数据):
HashMap map = new HashMap();
map.put("kevin", 1);
map.put("qian", 2);
map.put("kun", 3);
map.put("kevin", 4);
System.out.println(map.remove("kevin", 1));// false
System.out.println(map.remove("kevin", 4));// true
System.out.println(map);// {qian=2, kun=3}
其它常用方法
将key取为一个set,并进行遍历来取key
HashMap map = new HashMap();
map.put("kevin", "1");
map.put("qian", "2");
map.put("kun", "3");
Set set = map.keySet();
for (Object o : set) {
System.out.println(o);
}
// qian
// kevin
// kun
将value取为一个集合,并遍历来取value
HashMap map = new HashMap();
map.put("kevin", "1");
map.put("qian", "2");
map.put("kun", "3");
Collection values = map.values();
for (Object value : values) {
System.out.println(value);
}
// 2
// 1
// 3
查询是否有key或者value:
HashMap map = new HashMap();
map.put("kevin", "1");
map.put("qian", "2");
map.put("kun", "3");
System.out.println(map.containsKey("kevin"));// true
System.out.println(map.containsKey("kkk"));// false
System.out.println(map.containsValue("2"));// true
System.out.println(map.containsValue("5"));// false
使用entrySet将其转换为Set,并且使用for遍历,entry还有getKey方法和getValue方法来取key和value。
HashMap<String, String> map = new HashMap();
map.put("kevin", "1");
map.put("qian", "2");
map.put("kun", "3");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.print(entry + " ");
System.out.println(entry.getKey() + "," + entry.getValue());
}
// qian=2 qian,2
// kevin=1 kevin,1
// kun=3 kun,3
Hashtable
Hashtable与HashMap区别:
- 1、实现方式不同(继承的父类不同)
-
- Hashtable继承的Dictionary类
- HashMap继承的AbstractMap类
- 2、底层结构的默认容量不同:HashMap默认为16,Hashtable默认为11
- 3、HashMap的K和V都可以为null,而Hashtable的K和V不能为null
- 4、HashMap的数据定位采用的Hash算法,而Hashtable采用的hashcode
- 5、HashMap的性能高,Hashtable的性能低。在多线程的并发操作下HashMap会出问题,而Hashtable不会出问题。
HashMap map = new HashMap();
map.put(null, null);
System.out.println(map);//{null=null}
Hashtable hashtable = new Hashtable();
hashtable.put(null, null);// NullPointerException 空指针异常
迭代器
在使用for循环来删除map中的数据,并且使用set遍历的key来取value。
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
Set<String> keys = map.keySet();
for (String key : keys) {
if ("b".equals(key)) {
map.remove(key);
}
System.out.println(map.get(key));
}
// 1
// null
// ConcurrentModificationException
但是,map中删除了,set并不能同步,set中还存有三个key的位置。所以会继续进行遍历,而遍历到最后一个数据时,由于删除了前面的数据。导致现在set中只有两个数据,而没有第三个数据,所以会出现错误。那有什么办法能够使其同步呢?
我们可以使用迭代器来进行操作:
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
Set<String> keys = map.keySet();
Iterator<String> iterator = keys.iterator();
// hashNext用于判断是否存在下一条数据
while (iterator.hasNext()) {
// 获取下一条数据
String key = iterator.next();
if ("b".equals(key)) {
iterator.remove();// 只能对当前数据删除
}
System.out.println(map.get(key));
}
// 1
// null
// 3
集合中的工具类
Arrays工具类
数组转为字符串:
int[] ints = {1, 2, 3};
System.out.println(ints);// [I@776ec8df
System.out.println(Arrays.toString(ints));// [1, 2, 3]
在声明List时初始化List:
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(integers);// [1, 2, 3, 4, 5]
排序,默认为升序:
int[] ints = {2, 1, 4, 3, 0};
Arrays.sort(ints);
System.out.println(Arrays.toString(ints));// [0, 1, 2, 3, 4]
二分查找法(查找排序后的数组):
int[] ints = {2, 1, 4, 3, 0};
Arrays.sort(ints);
System.out.println(Arrays.toString(ints));// [0, 1, 2, 3, 4]
System.out.println(Arrays.binarySearch(ints, 4));// 4
数组的比较,顺序和数值都相同,也可以比较某一段数据,参数为:数组1,第一个索引(包含),第二个索引(不包含),数组2,第一个索引(包含),第二个索引(不包含)
int[] ints = {1, 2, 3, 4, 5};
int[] ints1 = {1, 2, 3, 4, 5};
int[] ints2 = {0, 2, 6, 4, 5};
int[] ints3 = {0, 2, 6, 4, 5, 7, 8};
System.out.println(Arrays.equals(ints, ints1));// true
System.out.println(Arrays.equals(ints, ints2));// false
System.out.println(Arrays.equals(ints2, 0, 5, ints3, 0, 5));// true
集合中的常见异常
ArrayList
IndexOutOfBoundsException,虽然容量为10。但是它的索引范围为:0——数据长度-1
ArrayList list = new ArrayList(10);
list.add("kevin");
list.add("qian");
System.out.println(list.get(2));// IndexOutOfBoundsException: Index 2 out of bounds for length 2
LinkedList
NoSuchElementException,不存在数据。
LinkedList list = new LinkedList();
System.out.println(list.getFirst());// NoSuchElementException
HashMap
HashMap一旦在遍历时进行数据的增加和修改,就会发生异常:
HashMap map = new HashMap();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
for (Object o : map.keySet()) {
if ("b".equals(o)) {
map.put("d", 4);
}
}
// ConcurrentModificationException
所以这里尽量使用迭代器来进行操作。