java字符串与集合转换工具使用
我们知道在Java 中有几种集合类,比如 List,Set,还有 Map,List集合一般是存放的元素是有序可重复的,Set 存放的元素则是无序不可重复的,而 Map 集合存放的是键值对。
java的不固定参数
不固定参数一定要放到最后一个位置
/**
*
* @param name 姓名参数
* @param age 年龄参数
* @param hobbies 爱好 个人不固定
*/
void speak(String name,int age,String ...hobbies){
System.out.println("我是"+name+",我今年"+age+"岁了");
for(String hobby:hobbies){
System.out.print(hobby+" ");
}
}
public static void main(String[] args) {
Person4 zhangsan=new Person4();
zhangsan.speak("张三",23,"游泳","唱歌","跳舞");
}
java.util.Arrays
用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用。
asList
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
作用是返回由指定数组支持的固定大小列表。
/**
* 将字符串转换为集合
* @param string
* @return
*/
private static List<String> getList(String string) {
String[] split = string.split(",");
List<String> strings = Arrays.asList(split);
return strings;
}
我们知道集合中有个上层接口 List,其有个典型实现类 ArrayList
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
所以我们可以用 instanceof 运算符判断 某个对象是否是 List 接口的实现类,如果是返回 true,否则返回 false
ArrayList arrayList = new ArrayList();
System.out.println(arrayList instanceof List);//true
java判断数组中是否包含某个元素
String[] orgString = {"2018-01","2018-02","2018-03","2018-04","2018-05","2018-06","2018-07","2018-08","2018-09","2018-10","2018-11","2018-12"};
for (LeakPointVO leakPointVO : realRushCount) {
String counts = leakPointVO.getNormalValue();
String checkDate = leakPointVO.getCheckDate();
if(!Arrays.asList(orgString).contains(checkDate)||null==checkDate) {
continue;
}
String checkDateName = compareDic(checkDate);
datelist.add(checkDateName);
Numlist.add(counts);
}
返回的 ArrayList 数组是一个定长列表,我们只能对其进行查看或者修改,但是不能进行添加或者删除操作
通过源码我们发现该类是没有add()或者remove() 这样的方法的,如果对其进行增加或者删除操作,都会调用其父类 AbstractList 对应的方法,而追溯父类的方法最终会抛出 UnsupportedOperationException 异常。如下:
1 String[] str = {"a","b","c"};
2 List<String> listStr = Arrays.asList(str);
3 listStr.set(1, "e");//可以进行修改
4 System.out.println(listStr.toString());//[a, e, c]
5 listStr.add("a");//添加元素会报错 java.lang.UnsupportedOperationException
已知数组数据,如何快速获取一个可进行增删改查的列表List
1 String[] str = {"a","b","c"};
2 List<String> listStr = new ArrayList<>(Arrays.asList(str));
3 listStr.add("d");
4 System.out.println(listStr.size());//4
Arrays.asList() 方法使用场景
Arrays工具类提供了一个方法asList, 使用该方法可以将一个变长参数或者数组转换成List 。但是,生成的List的长度是固定的;能够进行修改操作(比如,修改某个位置的元素);不能执行影响长度的操作(如add、remove等操作),否则会抛出UnsupportedOperationException异常。
所以 Arrays.asList 比较适合那些已经有数组数据或者一些元素,而需要快速构建一个List,只用于读取操作,而不进行添加或删除操作的场景。
copyOf
拷贝数组元素。底层采用 System.arraycopy() 实现,这是一个native方法。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
src:源数组
srcPos:源数组要复制的起始位置
dest:目的数组
destPos:目的数组放置的起始位置
length:复制的长度
注意:src 和 dest都必须是同类型或者可以进行转换类型的数组。
int[] num1 = {1,2,3};
int[] num2 = new int[3];
System.arraycopy(num1, 0, num2, 0, num1.length);
System.out.println(Arrays.toString(num2));//[1, 2, 3]
/**
* @param original 源数组
* @param newLength //返回新数组的长度
* @return
*/
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
deepEquals
是用来比较两个数组的元素是否相等,不过 deepEquals 能够进行比较多维数组,而且是任意层次的嵌套数组。
String[][] name1 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
String[][] name2 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
System.out.println(Arrays.equals(name1,name2));// false
System.out.println(Arrays.deepEquals(name1,name2));// true
fill
该系列方法用于给数组赋值,并能指定某个范围赋值。
//给a数组所有元素赋值 val
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
//给从 fromIndex 开始的下标,toIndex-1结尾的下标都赋值 val,左闭右开
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
rangeCheck(a.length, fromIndex, toIndex);//判断范围是否合理
for (int i = fromIndex; i < toIndex; i++)
a[i] = val;
}
toString 用来打印一维数组的元素
1 public static String toString(int[] a) {
2 if (a == null)
3 return "null";
4 int iMax = a.length - 1;
5 if (iMax == -1)
6 return "[]";
7
8 StringBuilder b = new StringBuilder();
9 b.append('[');
10 for (int i = 0; ; i++) {
11 b.append(a[i]);
12 if (i == iMax)
13 return b.append(']').toString();
14 b.append(", ");
15 }
16 }
ArrayList
java.util.ArrayList 是一个用数组实现的集合,支持随机访问,元素有序且可以重复
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
实现 List 接口
这个接口是 List 类集合的上层接口,定义了实现该接口的类都必须要实现的一组方法
我们知道 ArrayList 集合是由数组构成的,那么向 ArrayList 中添加元素,也就是向数组赋值。我们知道一个数组的声明是能确定大小的,而使用 ArrayList 时,好像是能添加任意多个元素,这就涉及到数组的扩容。
扩容的核心方法就是调用Arrays.copyOf 方法,创建一个更大的数组,然后将原数组元素拷贝过去即可。
public boolean add(E e) {
ensureCapacityInternal(size + 1); //添加元素之前,首先要确定集合的大小
elementData[size++] = e;
return true;
}
1 Object[] obj = {null,1};
2
3 ArrayList list = new ArrayList();
4 list.add(null);
5 list.add(1);
6 System.out.println(list.size());//2
对于 ArrayList 集合添加元素,我们总结一下:
①、当通过 ArrayList() 构造一个空集合,初始长度是为0的,第 1 次添加元素,会创建一个长度为10的数组,并将该元素赋值到数组的第一个位置。
②、第 2 次添加元素,集合不为空,而且由于集合的长度size+1是小于数组的长度10,所以直接添加元素到数组的第二个位置,不用扩容。
③、第 11 次添加元素,此时 size+1 = 11,而数组长度是10,这时候创建一个长度为10+10*0.5 = 15 的数组(扩容1.5倍),然后将原数组元素引用拷贝到新数组。并将第 11 次添加的元素赋值到新数组下标为10的位置。
④、第 Integer.MAX_VALUE - 8 = 2147483639,然后 2147483639%1.5=1431655759(这个数是要进行扩容) 次添加元素,为了防止溢出,此时会直接创建一个 1431655759+1 大小的数组,这样一直,每次添加一个元素,都只扩大一个范围。
⑤、第 Integer.MAX_VALUE - 7 次添加元素时,创建一个大小为 Integer.MAX_VALUE 的数组,在进行元素添加。
⑥、第 Integer.MAX_VALUE + 1 次添加元素时,抛出 OutOfMemoryError 异常。
注意:能向集合中添加 null 的,因为数组可以有 null 值存在。
ArrayList这是一个可以放可重复元素的一个集合。其实为什么说ArrayList在添加或者删除上的性能比LinkedList慢,因为在添加或者删除的时候会让数组进行一个拷贝操作,特别是删除的时候,需要对数组进行一个复制或者移动,但还好都是用java的本地方法进行的,在效率上比较高,还要提到的就是另外一个集合Vector,这是一个线程安全的ArrayList,在实现上并没有多大差别,只是在方法上面添加了一个 synchronized
import java.util.ArrayList;
public class test1 {
public static void main(String[] args) {
ArrayList<String> list1 =new ArrayList<String>();
//像动态数组中添加元素
list1.add("a");
list1.add("b");
list1.add("c");
list1.add("d");
System.out.println(list1);
//在指定的位置添加元素
list1.add(4, "e");
System.out.println(list1);
//将一个数组的元素全部添加到另一个数组中
ArrayList<String> list2 =new ArrayList<String>();
list2.add("f");
System.out.println(list2);
//将数组1中的元素全部添加到数组2中
list2.addAll(list1);
System.out.println(list2);
//获取list2中的第2个元素
System.out.println("lit2中的第2个元素"+list2.get(1));
//遍历list2中的所有元素
for(int i=0;i<=list2.size()-1;i++){
System.out.println(list2.get(i));
}
//将数组1中的全部元素添加到数组2中的指定位置
list2.addAll(0, list1);
System.out.println(list2);
//删除数组中的指定位置的元素
list2.remove(1);
System.out.println(list2);
//删除指定内容
list2.remove("b");
//删除多个元素
list2.removeAll(list1);//所有在list21中存在的元素都会被删除
System.out.println(list2);
//清空list集合
list1.clear();
System.out.println("@@"+list1);
//修改list集合
list2.set(0, "B");
System.out.println(list2);
}
}
①、根据索引删除元素
1 public E remove(int index) {
2 rangeCheck(index);//判断给定索引的范围,超过集合大小则抛出异常
3
4 modCount++;
5 E oldValue = elementData(index);//得到索引处的删除元素
6
7 int numMoved = size - index - 1;
8 if (numMoved > 0)//size-index-1 > 0 表示 0<= index < (size-1),即索引不是最后一个元素
9 //通过 System.arraycopy()将数组elementData 的下标index+1之后长度为 numMoved的元素拷贝到从index开始的位置
10 System.arraycopy(elementData, index+1, elementData, index,
11 numMoved);
12 elementData[--size] = null; //将数组最后一个元素置为 null,便于垃圾回收
13
14 return oldValue;
15 }
remove(int index) 方法表示删除索引index处的元素,首先通过 rangeCheck(index) 方法判断给定索引的范围,超过集合大小则抛出异常;接着通过 System.arraycopy 方法对数组进行自身拷贝。关于这个方法的用法可以参考这篇博客。
②、直接删除指定元素
1 public boolean remove(Object o) {
2 if (o == null) {//如果删除的元素为null
3 for (int index = 0; index < size; index++)
4 if (elementData[index] == null) {
5 fastRemove(index);
6 return true;
7 }
8 } else {//不为null,通过equals方法判断对象是否相等
9 for (int index = 0; index < size; index++)
10 if (o.equals(elementData[index])) {
11 fastRemove(index);
12 return true;
13 }
14 }
15 return false;
16 }
17
18
19 private void fastRemove(int index) {
20 modCount++;
21 int numMoved = size - index - 1;
22 if (numMoved > 0)
23 System.arraycopy(elementData, index+1, elementData, index,
24 numMoved);
25 elementData[--size] = null; //
26 }
remove(Object o)方法是删除第一次出现的该元素。然后通过System.arraycopy进行数组自身拷贝。
通过调用 set(int index, E element) 方法在指定索引 index 处的元素替换为 element。并返回原数组的元素。
1 public E set(int index, E element) {
2 rangeCheck(index);//判断索引合法性
3
4 E oldValue = elementData(index);//获得原数组指定索引的元素
5 elementData[index] = element;//将指定所引处的元素替换为 element
6 return oldValue;//返回原数组索引元素
7 }
通过调用 rangeCheck(index) 来检查索引合法性。
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
当索引为负数时,会抛出 java.lang.ArrayIndexOutOfBoundsException 异常。当索引大于集合长度时,会抛出 IndexOutOfBoundsException 异常。
①、根据索引查找元素
1 public E get(int index) {
2 rangeCheck(index);
3
4 return elementData(index);
5 }
同理,首先还是判断给定索引的合理性,然后直接返回处于该下标位置的数组元素。
②、根据元素查找索引
1 public int indexOf(Object o) {
2 if (o == null) {
3 for (int i = 0; i < size; i++)
4 if (elementData[i]==null)
5 return i;
6 } else {
7 for (int i = 0; i < size; i++)
8 if (o.equals(elementData[i]))
9 return i;
10 }
11 return -1;
12 }
注意:indexOf(Object o) 方法是返回第一次出现该元素的下标,如果没有则返回 -1。
还有 lastIndexOf(Object o) 方法是返回最后一次出现该元素的下标。
public static void main(String[] args) {
ArrayList<String> arrayList=new ArrayList<String>();
arrayList.add("张三");
arrayList.add("李四");
pringArrayList(arrayList);
// 将指定的元素插入此列表中的指定位置。
arrayList.add(1,"小张三");
pringArrayList(arrayList);
// 用指定的元素替代此列表中指定位置上的元素。
arrayList.set(2, "小李四");
pringArrayList(arrayList);
// 移除此列表中指定位置上的元素。
arrayList.remove(0);
pringArrayList(arrayList);
}
SubList
1 public List<E> subList(int fromIndex, int toIndex) {
2 subListRangeCheck(fromIndex, toIndex, size);
3 return new SubList(this, 0, fromIndex, toIndex);
4 }
作用是返回从 fromIndex(包括) 开始的下标,到 toIndex(不包括) 结束的下标之间的元素视图。如下:
1 ArrayList<String> list = new ArrayList<>();
2 list.add("a");
3 list.add("b");
4 list.add("c");
5
6 List<String> subList = list.subList(0, 1);
7 for(String str : subList){
8 System.out.print(str + " ");//a
9 }
这里出现了 SubList 类,这也是 ArrayList 中的一个内部类。
注意:返回的是原集合的视图,也就是说,如果对 subList 出来的集合进行修改或新增操作,那么原始集合也会发生同样的操作。
1 ArrayList<String> list = new ArrayList<>();
2 list.add("a");
3 list.add("b");
4 list.add("c");
5
6 List<String> subList = list.subList(0, 1);
7 for(String str : subList){
8 System.out.print(str + " ");//a
9 }
10 subList.add("d");
11 System.out.println(subList.size());//2
12 System.out.println(list.size());//4,原始集合长度也增加了
想要独立出来一个集合,解决办法如下:
List<String> subList = new ArrayList<>(list.subList(0, 1));
isEmpty()
1 public boolean isEmpty() {
2 return size == 0;
3 }
private String getJoinActivityMembers(ActivityMetting activityMetting)
{
List<String> orgUserIds = new ArrayList<String>(Arrays.asList(activityMetting.getOrgUserId().split(",")));
List<String> joinMembers = new ArrayList<String>(Arrays.asList(activityMetting.getJoinMembers().split(",")));
joinMembers.addAll(orgUserIds);
joinMembers = new ArrayList<>(new HashSet<>(joinMembers));
List<String> listTemp = new ArrayList<>();
for (int i = 0;i < joinMembers.size(); i++) {
if (joinMembers.get(i) != null&&joinMembers.get(i).length()!=0) {
listTemp.add(joinMembers.get(i));
}
}
if(CollectionUtils.isEmpty(listTemp))
{
return "";
}
String str = String.join(",", listTemp);
return str;
}
LinkedList
这是一个由链表构成的数组,LinkedList 是一个用链表实现的集合,元素有序且可以重复。
1 public class LinkedList<E>
2 extends AbstractSequentialList<E>
3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable
相对于 ArrayList 集合,LinkedList 集合多实现了一个 Deque 接口,这是一个双向队列接口,双向队列就是两端都可以进行增加和删除操作。
LinkedList 有两个构造函数,第一个是默认的空的构造函数,第二个是将已有元素的集合Collection 的实例添加到 LinkedList 中,调用的是 addAll() 方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
注意:LinkedList 是没有初始化链表大小的构造函数,因为链表不像数组,一个定义好的数组是必须要有确定的大小,然后去分配内存空间,而链表不一样,它没有确定的大小,通过指针的移动来指向下一个内存地址的分配。
private static void printLinkedList(LinkedList<String> linkedList){
System.out.print("当前元素的集合:");
for(int i=0;i<linkedList.size();i++){
System.out.print(linkedList.get(i)+" ");
}
System.out.println();
}
public static void main(String[] args) {
LinkedList<String> linkedList=new LinkedList<String>();
linkedList.add("张三");
linkedList.add("李四");
linkedList.add("王五");
linkedList.add("李四");
linkedList.add("赵六");
printLinkedList(linkedList);
// 返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
System.out.println(linkedList.indexOf("李四"));
printLinkedList(linkedList);
// 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println(linkedList.peekFirst());
printLinkedList(linkedList);
// 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
System.out.println(linkedList.peekLast());
printLinkedList(linkedList);
// 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
System.out.println(linkedList.pollFirst());
printLinkedList(linkedList);
// 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
System.out.println(linkedList.pollLast());
printLinkedList(linkedList);
}
HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,而且 key 和 value 都可以为 null。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
添加元素
1 //hash(key)就是上面讲的hash方法,对其进行了第一步和第二步处理
2 public V put(K key, V value) {
3 return putVal(hash(key), key, value, false, true);
4 }
5 /**
6 *
7 * @param hash 索引的位置
8 * @param key 键
9 * @param value 值
10 * @param onlyIfAbsent true 表示不要更改现有值
11 * @param evict false表示table处于创建模式
12 * @return
13 */
14 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
15 boolean evict) {
16 Node<K,V>[] tab; Node<K,V> p; int n, i;
17 //如果table为null或者长度为0,则进行初始化
18 //resize()方法本来是用于扩容,由于初始化没有实际分配空间,这里用该方法进行空间分配,后面会详细讲解该方法
19 if ((tab = table) == null || (n = tab.length) == 0)
20 n = (tab = resize()).length;
21 //注意:这里用到了前面讲解获得key的hash码的第三步,取模运算,下面的if-else分别是 tab[i] 为null和不为null
22 if ((p = tab[i = (n - 1) & hash]) == null)
23 tab[i] = newNode(hash, key, value, null);//tab[i] 为null,直接将新的key-value插入到计算的索引i位置
24 else {//tab[i] 不为null,表示该位置已经有值了
25 Node<K,V> e; K k;
26 if (p.hash == hash &&
27 ((k = p.key) == key || (key != null && key.equals(k))))
28 e = p;//节点key已经有值了,直接用新值覆盖
29 //该链是红黑树
30 else if (p instanceof TreeNode)
31 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
32 //该链是链表
33 else {
34 for (int binCount = 0; ; ++binCount) {
35 if ((e = p.next) == null) {
36 p.next = newNode(hash, key, value, null);
37 //链表长度大于8,转换成红黑树
38 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
39 treeifyBin(tab, hash);
40 break;
41 }
42 //key已经存在直接覆盖value
43 if (e.hash == hash &&
44 ((k = e.key) == key || (key != null && key.equals(k))))
45 break;
46 p = e;
47 }
48 }
49 if (e != null) { // existing mapping for key
50 V oldValue = e.value;
51 if (!onlyIfAbsent || oldValue == null)
52 e.value = value;
53 afterNodeAccess(e);
54 return oldValue;
55 }
56 }
57 ++modCount;//用作修改和新增快速失败
58 if (++size > threshold)//超过最大容量,进行扩容
59 resize();
60 afterNodeInsertion(evict);
61 return null;
62 }
①、判断键值对数组 table 是否为空或为null,否则执行resize()进行扩容;
②、根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③;
③、判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals;
④、判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤;
⑤、遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;
⑥、插入成功后,判断实际存在的键值对数量size是否超过了最大容量threshold,如果超过,进行扩容。
⑦、如果新插入的key不存在,则返回null,如果新插入的key存在,则返回原key对应的value值(注意新插入的value会覆盖原value值)
HashMap 删除元素首先是要找到 桶的位置,然后如果是链表,则进行链表遍历,找到需要删除的元素后,进行删除;如果是红黑树,也是进行树的遍历,找到元素删除后,进行平衡调节,注意,当红黑树的节点数小于 6 时,会转化成链表。
查找元素
①、通过 key 查找 value
首先通过 key 找到计算索引,找到桶位置,先检查第一个节点,如果是则返回,如果不是,则遍历其后面的链表或者红黑树。其余情况全部返回 null。
1 public V get(Object key) {
2 Node<K,V> e;
3 return (e = getNode(hash(key), key)) == null ? null : e.value;
4 }
5
6 final Node<K,V> getNode(int hash, Object key) {
7 Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
8 if ((tab = table) != null && (n = tab.length) > 0 &&
9 (first = tab[(n - 1) & hash]) != null) {
10 //根据key计算的索引检查第一个索引
11 if (first.hash == hash && // always check first node
12 ((k = first.key) == key || (key != null && key.equals(k))))
13 return first;
14 //不是第一个节点
15 if ((e = first.next) != null) {
16 if (first instanceof TreeNode)//遍历树查找元素
17 return ((TreeNode<K,V>)first).getTreeNode(hash, key);
18 do {
19 //遍历链表查找元素
20 if (e.hash == hash &&
21 ((k = e.key) == key || (key != null && key.equals(k))))
22 return e;
23 } while ((e = e.next) != null);
24 }
25 }
26 return null;
27 }
②、判断是否存在给定的 key 或者 value
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
//遍历桶
for (int i = 0; i < tab.length; ++i) {
//遍历桶中的每个节点元素
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
HashSet
HashSet 是一个由 HashMap 实现的集合。元素无序且不能重复。
1 public class HashSet<E>
2 extends AbstractSet<E>
3 implements Set<E>, Cloneable, java.io.Serializable
添加元素
1 public boolean add(E e) {
2 return map.put(e, PRESENT)==null;
3 }
通过 map.put() 方法来添加元素,说明了该方法如果新插入的key不存在,则返回null,如果新插入的key存在,则返回原key对应的value值(注意新插入的value会覆盖原value值)。
也就是说 HashSet 的 add(E e) 方法,会将 e 作为 key,PRESENT 作为 value 插入到 map 集合中,如果 e 不存在,则插入成功返回 true;如果存在,则返回false。
删除元素
1 public boolean remove(Object o) {
2 return map.remove(o)==PRESENT;
3 }
调用 HashMap 的remove(Object o) 方法,该方法会首先查找 map 集合中是否存在 o ,如果存在则删除,并返回该值,如果不存在则返回 null。
也就是说 HashSet 的 remove(Object o) 方法,删除成功返回 true,删除的元素不存在会返回 false。
查找元素
1 public boolean contains(Object o) {
2 return map.containsKey(o);
3 }
调用 HashMap 的 containsKey(Object o) 方法,找到了返回 true,找不到返回 false。
附录HashSet源码
对于HashSet来说,源码相对简单,没有太多东西是自己实现的,基本上都是基于HashMap实现的,这就是他为什么不提供随机访问的原因,因为HashMap要遍历元素的话就只有迭代的方式
package java.util;
import java.io.InvalidObjectException;
//hashSet是一个实现起来非常简单的集合,代码也不多,就二百行左右,去掉了注释估计也没有多少了,
//因为他们身只是一个是借助HashMap来实现的。可见作者是有多么懒啊,只不过这个集合也是一个常用的集合,其实我觉得这类完全可以优化一下的
//因为它并不使用HashMap的所有功能。
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
//构建一个HashMap集合
public HashSet() {
map = new HashMap<>();
}
//构造的时候动态添加集合元素
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
//
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* the specified initial capacity and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hash table
* @throws IllegalArgumentException if the initial capacity is less
* than zero
*/
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
/**
* Constructs a new, empty linked hash set. (This package private
* constructor is only used by LinkedHashSet.) The backing
* HashMap instance is a LinkedHashMap with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hash map
* @param loadFactor the load factor of the hash map
* @param dummy ignored (distinguishes this
* constructor from other int, float constructor.)
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive
*/
//采用LinkedHashMap作为底层实现
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
/**
* Returns an iterator over the elements in this set. The elements
* are returned in no particular order.
*
* @return an Iterator over the elements in this set
* @see ConcurrentModificationException
*/
public Iterator<E> iterator() {
return map.keySet().iterator();
}
/**
* Returns the number of elements in this set (its cardinality).
*
* @return the number of elements in this set (its cardinality)
*/
public int size() {
return map.size();
}
/**
* Returns <tt>true</tt> if this set contains no elements.
*
* @return <tt>true</tt> if this set contains no elements
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* Returns <tt>true</tt> if this set contains the specified element.
* More formally, returns <tt>true</tt> if and only if this set
* contains an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this set is to be tested
* @return <tt>true</tt> if this set contains the specified element
*/
public boolean contains(Object o) {
return map.containsKey(o);
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
* if this set contains such an element. Returns <tt>true</tt> if
* this set contained the element (or equivalently, if this set
* changed as a result of the call). (This set will not contain the
* element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
/**
* Removes all of the elements from this set.
* The set will be empty after this call returns.
*/
public void clear() {
map.clear();
}
/**
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements
* themselves are not cloned.
*
* @return a shallow copy of this set
*/
@SuppressWarnings("unchecked")
public Object clone() {
try {
HashSet<E> newSet = (HashSet<E>) super.clone();
newSet.map = (HashMap<E, Object>) map.clone();
return newSet;
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
/**
* Save the state of this <tt>HashSet</tt> instance to a stream (that is,
* serialize it).
*
* @serialData The capacity of the backing <tt>HashMap</tt> instance
* (int), and its load factor (float) are emitted, followed by
* the size of the set (the number of elements it contains)
* (int), followed by all of its elements (each an Object) in
* no particular order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// Write out HashMap capacity and load factor
s.writeInt(map.capacity());
s.writeFloat(map.loadFactor());
// Write out size
s.writeInt(map.size());
// Write out all elements in the proper order.
for (E e : map.keySet())
s.writeObject(e);
}
/**
* Reconstitute the <tt>HashSet</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read capacity and verify non-negative.
int capacity = s.readInt();
if (capacity < 0) {
throw new InvalidObjectException("Illegal capacity: " +
capacity);
}
// Read load factor and verify positive and non NaN.
float loadFactor = s.readFloat();
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new InvalidObjectException("Illegal load factor: " +
loadFactor);
}
// Read size and verify non-negative.
int size = s.readInt();
if (size < 0) {
throw new InvalidObjectException("Illegal size: " +
size);
}
// Set the capacity according to the size and load factor ensuring that
// the HashMap is at least 25% full but clamping to maximum capacity.
capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
HashMap.MAXIMUM_CAPACITY);
// Create backing HashMap
map = (((HashSet<?>)this) instanceof LinkedHashSet ?
new LinkedHashMap<E,Object>(capacity, loadFactor) :
new HashMap<E,Object>(capacity, loadFactor));
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
@SuppressWarnings("unchecked")
E e = (E) s.readObject();
map.put(e, PRESENT);
}
}
/**
* Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
* and <em>fail-fast</em> {@link Spliterator} over the elements in this
* set.
*
* <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and
* {@link Spliterator#DISTINCT}. Overriding implementations should document
* the reporting of additional characteristic values.
*
* @return a {@code Spliterator} over the elements in this set
* @since 1.8
*/
public Spliterator<E> spliterator() {
return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
}
}