java数组与集合工具类的使用

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);
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值