目录

                                    java基础--集合

1集合与数组

2集合中接口和类的关系

3 Collection 

         1 list,set,map对比

2 list(有序、可重复)

(1)ArrayList

示例

(2)List与ArrayList区别

(3)LinkedList

(4)List的三个子类的特点

3 Set(无序、不能重复)

1 HashSet

2 TreeSet

3 set 的遍历

4 Map(键值对、键唯一、值不唯一)

1 HashMap

2Hashtable

3TreeMap

4 Map遍历

5 引用都是地址值

6集合也可以做参数

7hashCode()与 equal()对比

8 数组和集合互转

9 迭代器

1 迭代器的原理

2 遍历的同时添加元素

3 next()方法

10集合中删除元素对比总结 ?????


                                    java基础--集合

1集合与数组

数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。

集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。
注:数组我在前面的博客讲了大家可以看下

2集合中接口和类的关系

Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是SetListSet中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。

MapJava.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value

Iterator所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
1.hasNext()是否还有下一个元素。
2.next()返回下一个元素。
3.remove()删除当前元素。

3 Collection 

1 list,set,map对比

接口

子接口

是否有序

是否允许元素重复

Collection 

         

 

List   

ArrayList

      

LinkedList

      

Vector

Set

AbstractSet

  

HashSet

  

TreeSet

是(用二叉排序树)

Map

AbstractMap

使用key-value来映射和存储数据,key必须唯一,value可以重复

  

HashMap

 

  

TreeMap

是(用二叉排序树)

使用key-value来映射和存储数据,key必须唯一,value可以重复

2 list(有序、可重复)

List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢。

(1)ArrayList

ArrayList是基于数组的,在初始化ArrayList时,会构建空数组(Object[] elementData={})。ArrayList是一个无序的,它是按照添加的先后顺序排列,当然,他也提供了sort方法,如果需要对ArrayList进行排序,只需要调用这个方法,提供Comparator比较器即可

1 add操作:

  1)如果是第一次添加元素,数组的长度被扩容到默认的capacity,也就是10.

  2) 当发觉同时添加一个或者是多个元素,数组长度不够时,就扩容,这里有两种情况:

  只添加一个元素,例如:原来数组的capacity10size已经为10,不能再添加了。需要扩容,新的capacity=old capacity+old capacity>>1=10+10/2=15.即新的容量为15

  当同时添加多个元素时,原来数组的capacity10size10,当同时添加6个元素时。它需要的min capacity16,而按照capacity=old capacity+old capacity>>1=10+10/2=15new capacity小于min capacity,则取min capacity

  对于添加,如果不指定下标,就直接添加到数组后面,不涉及元素的移动,如果要添加到某个特定的位置,那需要将这个位置开始的元素往后挪一个位置,然后再对这个位置设置。

2 Remove操作:

 Remove提供两种,按照下标和value

  1remove(int index):首先需要检查Index是否在合理的范围内。其次再调用System.arraycopyindex之后的元素向前移动。

  2remove(Object o):首先遍历数组,获取第一个相同的元素,获取该元素的下标。其次再调用System.arraycopyindex之后的元素向前移动。

3 Get操作:

  这个比较简单,直接对数组进行操作即可。

示例

去除ArrayList中重复字符串的元素方式

(2)List与ArrayList区别

   核心思想。就是性能

装箱:在值类型向引用类型转换时发生;

拆箱:在引用类型向值类型转换时发生;

值类型:直接将内存存储在栈内,由系统自动释放资源的数据类型;

引用类型:由类型的实际值引用(类似于指针)表示的数据类型,通俗点说就是在编程时需要new出来的变量类型都是引用型,引用类型是存放在内存的堆中;

栈:由大至小的分配,先进后出,直接存放值类型的地方;我们一般出现的内存溢出就是由于栈位都分配完了;

堆:由小至大的分配,随意存储,存放引用类型的地方;

 1 public void ArrayListDemo()
 2 {
 3   ArrayList list = new ArrayList();
 4   list.add(1);
 5   list.add(2);
 6   
 7   foreach(int i in list)
 8   {
 9     Console.WriteLine("value is {0}", value);
10   }
11 }

由于ArrayList的每个item默认是Object的类型,所以当我们执行语句list.add(1);的时候,就是做了一次装箱的操作。同理,在for循环里list的每一项都要做一个拆箱的操作才能得到变量i,最后到打印变量i时,由于字符串也是引用类型,所以也要做一次的装箱的操作。这里前后一共做了6次的装箱拆箱(4次装箱,2次拆箱),每一次的装箱拆箱都涉及CPU以及内存的分配,都是性能的损耗。

(3)LinkedList

LinkedList是基于链表的,它是一个双向链表,每个节点维护了一个prevnext指针。同时对于这个链表,维护了firstlast指针,first指向第一个元素,last指向最后一个元素。LinkedList是一个无序的链表,按照插入的先后顺序排序,不提供sort方法对内部元素排序。

Add元素:

  LinkedList提供了几个添加元素的方法:addFirstaddLastaddAlladd等,时间复杂度为O(1)

Remove元素:

  LinkedList提供了几个移除元素的方法:removeFirstremoveLastremoveFirstOccurrenceremove等,时间复杂度为O(1)

Get元素:

  根据给定的下标index,判断它first节点、last直接距离,如果index<size(数组元素个数)/2,就从first开始。如果大于,就从last开始。这个和我们平常思维不太一样,也许按照我们的习惯,从first开始。这也算是一点小心的优化吧。

(4)List的三个子类的特点

3 Set(无序、不能重复)

Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。

1 HashSet

  HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。

 HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次封装,而且只使用了HashMapkey来实现各种特性,而HashMapvalue始终都是PRESENT

 HashSet不允许重复(HashMapkey不允许重复,如果出现重复就覆盖),允许null值,非线程安全。

构造方法

HashSet() 
  构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75
HashSet(Collection<? extends E> c) 
  构造一个包含指定 collection 中的元素的新 set
HashSet(int initialCapacity) 
  构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。
HashSet(int initialCapacity, float loadFactor)
  构造一个新的空 set,其底层 HashMap 实例具有指定的初始容量和指定的加载因子。

 

方法

boolean add(E e) 
  如果此 set 中尚未包含指定元素,则添加指定元素。

public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。
void clear()
  从此 set 中移除所有元素。
** Object clone() 
  返回此 HashSet 实例的浅表副本:并没有复制这些元素本身。
boolean contains(Object o) 
  如果此 set 包含指定元素,则返回 true
boolean isEmpty()**
  如果此 set 不包含任何元素,则返回 true
** Iterator iterator() 
  返回对此 set 中元素进行迭代的迭代器。
boolean remove(Object o) 
  如果指定元素存在于此 set 中,则将其移除。
int size()**
  返回此 set 中的元素的数量(set 的容量)。

LinkedHashSet

将集合中的重复元素去掉

2 TreeSet

基于 TreeMap NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator进行排序,具体取决于使用的构造方法。

执行结果:会抛出一个 异常:java.lang.ClassCastException
显然是出现了类型转换异常。原因在于我们需要告诉TreeSet如何来进行比较元素,如果不指定,就会抛出这个异常

如何解决:
如何指定比较的规则,需要在自定义类(Person)中实现Comparable接口,并重写接口中的compareTo方法

  • 如果将compareTo()返回值写死为0,元素值每次比较,都认为是相同的元素,这时就不再向TreeSet中插入除第一个外的新元素。所以TreeSet中就只存在插入的第一个元素。
  • 如果将compareTo()返回值写死为1,元素值每次比较,都认为新插入的元素比上一个元素大,于是二叉树存储时,会存在根的右侧,读取时就是正序排列的。
  • 如果将compareTo()返回值写死为-1,元素值每次比较,都认为新插入的元素比上一个元素小,于是二叉树存储时,会存在根的左侧,读取时就是倒序序排列的。

此时变为1 的话

小技巧

这种情况下就不存了

1按照年龄排序

 

2按照姓名排序

3按照姓名长度排序

示例

源码

 

3 set 的遍历

1.迭代遍历:

Set<String> set = new HashSet<String>();  
Iterator<String> it = set.iterator();  
while (it.hasNext()) {  
  String str = it.next();  
  System.out.println(str);  
}  

2.for(foreach)循环遍历:

for (String str : set) {  
      System.out.println(str);  
}

 

4 Map(键值对、键唯一、值不唯一)

Map集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。

1 HashMap

HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,能保证只有一个线程更改Map。

public Object put(Object Key,Object value)方法用来将元素添加到map中。

 

数组方式存储key/value线程非安全允许null作为keyvaluekey不可以重复,value允许重复,不保证元素迭代顺序是按照插入时的顺序,keyhash值是先计算keyhashcode值,然后再进行计算,每次容量扩容会重新计算所以keyhash值,会消耗资源,要求key必须重写equalshashcode方法

  默认初始容量16,加载因子0.75,扩容为旧容量乘2,查找元素快,如果key一样则比较value,如果value不一样,则按照链表结构存储value,就是一个key后面有多个value

方法

1、添加:

  V put(K key, V value) (可以相同的key值,但是添加的value值会覆盖前面的,返回值是前一个,如果没有就返回null

  putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。

2、删除

  remove() 删除关联对象,指定key对象

  clear() 清空集合对象

3、获取

  value get(key) 可以用于判断键是否存在的情况。当指定的键不存在的时候,返回的是null

4、判断:

  boolean isEmpty() 长度为0返回true否则false

  boolean containsKey(Object key) 判断集合中是否包含指定的key

5、长度:

  boolean containsValue(Object value) 判断集合中是否包含指定的value

 Int size()

  map的主要的方法就这几个

LinkedHashMap(继承了HashMap

LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

2Hashtable

HashtableHashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

3TreeMap

基于红黑二叉树的NavigableMap的实现,线程非安全,不允许nullkey不可以重复,value允许重复,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出

4 Map遍历

第一种:KeySet()

获取所有的键

将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 。
  取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。

Map map = new HashMap();
    map.put("key1","lisi1");
    map.put("key2","lisi2");
    map.put("key3","lisi3");
    map.put("key4","lisi4");  
    //先获取map集合的所有键的set集合,keyset()
    Iterator it = map.keySet().iterator();
    //获取迭代器
    while(it.hasNext()){
        Object key = it.next();
        System.out.println(map.get(key));
}

 

第二种: values() 获取所有的值

Collection values()不能获取到key对象
        Collection<String> vs = map.values();
        Iterator<String> it = vs.iterator();
        while (it.hasNext()) {
            String value = it.next();
            System.out.println(" value=" + value);
        }

第三种:entrySet()

Set<Map.Entry<K,V>> entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。Map.Entry表示映射关系。entrySet():迭代后可以e.getKey()e.getValue()两种方法来取keyvalue。返回的是Entry接口。
典型用法如下:

// 返回的Map.Entry对象的Set集合 Map.Entry包含了key和value对象
        Set<Map.Entry<Integer, String>> es = map.entrySet();
        Iterator<Map.Entry<Integer, String>> it = es.iterator();
        while (it.hasNext()) {
            // 返回的是封装了key和value对象的Map.Entry对象
            Map.Entry<Integer, String> en = it.next();
            // 获取Map.Entry对象中封装的key和value对象
            Integer key = en.getKey();
            String value = en.getValue();
            System.out.println("key=" + key + " value=" + value);
        }

 推荐使用第三种方式,即entrySet()方法,效率较高。
  对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把keyvalue都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。

 

5 引用都是地址值

引用都是地址值,基本都是值

 

6集合也可以做参数

集合也可以作为参数传进函数的,传进来的就是地址值,

7hashCode()与 equal()对比

 

 hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致,那么equal()既然已经能实现对比的功能了,为什么还要hashCode()呢?

 因为重写的equal()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equal()呢?

 因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:

1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。

 2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的

所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

8 数组和集合互转

 asList()

toArray()

9 迭代器

1 迭代器的原理

 迭代器是对集合进行遍历,而每一个集合的内部的存储结构都是不同的,所以每一个集合存和取都是不一样的,那么就需要在每一个类中定义hasnext()和next()方法,这样做是可以的,但是会让整个集合体过于臃肿,迭代器是将这样的方法向上抽取出接口,然后在每个类的内部,定义自己的迭代方式,这样做的好处有二,第一规定了整个集合体系的遍历方式都是hasnext()和next()方法,第二,代码有底层内部实现,使用者不用管怎么实现,会用即可。

2 遍历的同时添加元素

并发修改  ListIterator  可以解决那个问题。

3 next()方法

10集合中删除元素对比总结 ?????

List 方法1普通的for循环遍历并删除

public void forRemove(List<T> list, T obj){
		
		for(int i = 0;i < list.size(); i++){
			if (obj == list.get(i))
			{
				list.remove(obj);
			}
		}
	}

      List<String> list = new ArrayList<>();
 
		list.add("1");
		list.add("2");
		list.add("2");
		list.add("3");
 
		re.forRemove(list,"2");
		System.out.println(list.toString());

程序输出[1,2,3]

这是因为,删除时改变了list的长度。删除第一个2后,长度变为了3,这时list.get(2)为3,不再是2了,不能删除第2个2

List 方法2  forEach循环删除

 

 

List 方法3. Iterator 迭代器删除(推荐)

public void iteratorRemove(List<T> list, T obj){
	
		Iterator<T> it = list.iterator();
		while(it.hasNext()){
 
			T item = it.next();
			if (item.equals(obj))
			{
				it.remove();//remove the current item
			}
		
		}
	}
List<String> list = new ArrayList<>();
 
		list.add("1");
		list.add("2");
		list.add("2");
		list.add("3");
 
		//re.forRemove(list,"2");
		//re.forEachRemove(list,"2");
		re.iteratorRemove(list,"2");
		System.out.println(list.toString());

但是需要注意以下两点 对Iterator的remove()方法调用必须在Iterator的next()方法之后。 调用next()方法后只能执行一次remove()方法

综上,对于集合的删除操作,特别是List,需要使用迭代器来操作。