java复习 集合

java复习一

看书总结一下Java的基本知识主要集中在集合,并发

集合

先看一下集合的继承结构关系图
enter image description here

java集合类结构
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └StackSet
Map
├Hashtable
├HashMap
└WeakHashMap

下面依次总结一下

Collection接口

先要注意他们继承的是接口,Collection是最基本的集合接口,一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set,而子接口继承Collection类

public interface List<E> extends Collection<E> {}

所有实现Collection接口的类(继承子接口)必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个 Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后 一个构造函数允许用户复制一个Collection。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    public LinkedList() {
    }

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);//Node<E>类型,添加链表元素
    }
}

  Collection继承了Iterable接口,支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。

    Iterator it = collection.iterator(); // 获得一个迭代对象
  while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
  }

由Collection接口派生的两个接口是List和Set。

List

除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
LinkedList类

LinkedList<String> lList = new LinkedList<String>();  

注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

List list = Collections.synchronizedList(new LinkedList(...));

LinkedeList类中的方法

public boolean add(Object element) 向链表的末尾添加一个新的节点对象element
public void add(int index,Object element)向链表的指定位置尾添加一个新的节点对象element
public void addFirst(Object element)把节点对象element添加到链表的表头
public void addLast(Object element)把节点对象element添加到链表的末尾
public void clear()删除链表的所有节点对象
public Object remove(int index)删除指定位置上的节点对象
public boolean remove(Object element)将首次出现的节点对象element删除
public Obiect removeFirst() 删除第一个节点对象,并返回这个节点对象.
public Object removeLast() 删除最后一个节点对象
public Object get(int index)得到链表中指定位置上的节点对象
public Object getFirst()得到链表中的第一个节点对象
public Object getLast()得到链表中的最后一个节点对象
public int indexOf(Object element) 返回节点对象element,在链表中首次出现的位置,如果链表中无此节点,则返回-1
public int lastIndexOf(Object element) 返回节点对象element,在链表中最后出现的位置,如果链表中无此节点,则返回-1
public Object set(int index ,Object element) 用节点对象element替换指定位置的节点对象,并返回链表中先前位置处的节点对象
public int size()返回链表的长度,即节点的个数
public boolean contains(Object element) 判断链表节点对象中是否含有element

ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。

每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法 并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。

下面是add方法实现的代码

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

private static final int DEFAULT_CAPACITY = 10;//默认为10个

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //增加了原来长度的3/2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //会大幅度占用空间,可能发生GC
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

Java中ArrayList和LinkedList区别
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

Vector
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
Vector和ArrayList
1,vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
2,如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
3,如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist都可以。而如果移动一个指定位置的数据花费的时间为0(n-i)n为总长度,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据所花费的时间为0(1),而查询一个指定位置的数据时花费的时间为0(i)。

ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动 等内存操作,所以索引数据快插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要 差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!

Stack 类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口

Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口

Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。

由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方 法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。

 如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
Hashtable是同步的。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。
API
initialCapacity the initial capacity
loadFactor the load factor
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
Java API介绍
1. 以弱键 实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除
2. WeakHashMap 类的行为部分取决于垃圾回收器的动作。因为垃圾回收器在任何时候都可能丢弃键,WeakHashMap 就像是一个被悄悄移除条目的未知线程。特别地,即使对 WeakHashMap 实例进行同步,并且没有调用任何赋值方法,在一段时间后 size 方法也可能返回较小的值,对于 isEmpty 方法,返回 false,然后返回true,对于给定的键,containsKey 方法返回 true 然后返回 false,对于给定的键,get 方法返回一个值,但接着返回 null,对于以前出现在映射中的键,put 方法返回 null,而 remove 方法返回 false,对于键 set、值 collection 和条目 set 进行的检查,生成的元素数量越来越少。
3. WeakHashMap 中的每个键对象间接地存储为一个弱引用的指示对象。因此,不管是在映射内还是在映射之外,只有在垃圾回收器清除某个键的弱引用之后,该键才会自动移除。

ConcurrentHashMap 类
通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。

ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度以及如何锁,可以简单理解成把一个大的HashTable分解成多个,形成了锁分离

enter image description here

ConcurrentHashMap中主要实体类就是三个:ConcurrentHashMap(整个Hash表),Segment(桶),HashEntry(节点)
ConcurrentHashMap完全允许多个读操作并发进行,读操作并不需要加锁。如果使用传统的技术,如HashMap中的实现,如果允许可以在hash链的中间添加或删除元素,读操作不加锁将得到不一致的数据。ConcurrentHashMap实现技术是保证HashEntry几乎是不可变的。HashEntry代表每个hash链中的一个节点,其结构如下所示:

static final class HashEntry<K,V> {  
     final K key;  
     final int hash;  
     volatile V value;  
     final HashEntry<K,V> next;  
}  

可以看到除了value不是final的,其它值都是final的,这意味着不能从hash链的中间或尾部添加或删除节点,因为这需要修改next 引用值,所有的节点的修改只能从头部开始。对于put操作,可以一律添加到Hash链的头部。但是对于remove操作,可能需要从中间删除一个节点,这就需要将要删除节点的前面所有节点整个复制一遍,最后一个节点指向要删除结点的下一个结点。这在讲解删除操作时还会详述。为了确保读操作能够看到最新的值,将value设置成volatile,这避免了加锁。
一个介绍ConcurrentHashMap很好的博客
volatile类型的介绍
HashMap与TreeMap
1、HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)
2、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该 使用TreeMap(HashMap中元素的排列顺序是不固定的)。集合框架”提供两种常规的Map实现:HashMap和TreeMap (TreeMap实现SortedMap接口)。
3、在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。  这个TreeMap没有调优选项,因为该树总处于平衡状态。
在hashMap中,同样的值的map,顺序不同,equals时,false;
而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
hashtable与hashmap
1.历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
2.同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
3.值:只有HashMap可以让你将空值作为一个表的条目的key或value

Collections集合类总结

排序(Sort)使用sort方法可以根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable接口。

double array[] = {112, 111, 23, 456, 231 };
for (int i = 0; i < array.length; i++) {
list.add(new Double(array[i]));
}
Collections.sort(list);
for (int i = 0; i < array.length; i++) {
  System.out.println(li.get(i));
}
//结果:112,111,23,456,231

shuffle(Collection)方法的使用(含义:对集合进行随机排序)。

public class Practice {
    public static void main(String[] args){
                 List c = new ArrayList();
            c.add("l");
             c.add("o");
             c.add("v");
            c.add("e");
                System.out.println(c);
              Collections.shuffle(c);
            System.out.println(c);
             Collections.shuffle(c);
              System.out.println(c);
       }
    }
    运行结果为:[l, o, v, e]
                  [l, v, e, o]
                  [o, v, e, l]

binarySearch(Collection,Object)方法的使用(含义:查找指定集合中的元素,返回所查找元素的索引)。

public class Practice {
    public static void main(String[] args){
                List c = new ArrayList();
            c.add("l");
            c.add("o");
             c.add("v");
             c.add("e");
            System.out.println(c);
             int m = Collections.binarySearch(c, "o");
               System.out.println(m);
             int n = -m-1;//若查找的元素不存在,示例中的n即表示该元素最有可能存在的位置的索引。
      }
}
运行结果为:[l, o, v, e]
    1

max(Collection),max(Collection,Comparator)方法的使用(前者采用Collection内含自然比较法,后者采用Comparator进行比较)。
min(Collection),min(Collection,Comparator)方法的使用(前者采用Collection内含自然比较法,后者采用Comparator进行比较)。
indexOfSubList(List list,List subList)方法的使用(含义:查找subList在list中首次出现位置的索引)。

public class Practice {
   public static void main(String[] args){
            List list = Arrays.asList("one two three four five six siven".split(" "));
        System.out.println(list);
            List subList = Arrays.asList("three four five six".split(" "));
          System.out.println(Collections.indexOfSubList(list, subList));
  }
}
运行结果为:[one, two, three, four, five, six, siven]
    2

lastIndexOfSubList(List source,List target)方法的使用与上例方法的使用相同
replaceAll(List list,Object old,Object new)方法的使用(含义:替换批定元素为某元素,若要替换的值存在刚返回true,反之返回false)。

public class Practice {
    public static void main(String[] args){
           List list = Arrays.asList("one two three four five six siven".split(" "));
              System.out.println(list);
           List subList = Arrays.asList("three four five six".split(" "));
            System.out.println(Collections.replaceAll(list, "siven", "siven eight"));
            System.out.println(list);
     }
}
运行结果为:
      [one, two, three, four, five, six, siven]
       true
       [one, two, three, four, five, six, siven eight]

reverse()方法的使用(含义:反转集合中元素的顺序)。

public class Practice {
   public static void main(String[] args){
             List list = Arrays.asList("one two three four five six siven".split(" "));
           System.out.println(list);
           Collections.reverse(list);
          System.out.println(list);
   }
}
运行结果为:
   [one, two, three, four, five, six, siven]
   [siven, six, five, four, three, two, one]

rotate(List list,int m)方法的使用(含义:集合中的元素向后移m个位置,在后面被遮盖的元素循环到前面来)。

public class Practice {
   public static void main(String[] args){
               List list = Arrays.asList("one two three four five six siven".split(" "));
            System.out.println(list);
            Collections.rotate(list, 1);
            System.out.println(list);
      }
}
运行结果为:
      [one, two, three, four, five, six, siven]
    [siven, one, two, three, four, five, six]

copy(List m,List n)方法的使用(含义:将集合n中的元素全部复制到m中,并且覆盖相应索引的元素)。

public class Practice {
    public static void main(String[] args){
              List m = Arrays.asList("one two three four five six siven".split(" "));
              System.out.println(m);
               List n = Arrays.asList("我 是 123".split(" "));
               System.out.println(n);
               Collections.copy(m,n);
                  System.out.println(m);
        }
    }
    运行结果为:[one, two, three, four, five, six, siven]
           [我, 是, 123]
          [我, 是, 123, four, five, six, siven]          

swap(List list,int i,int j)方法的使用(含义:交换集合中指定元素索引的位置)。

public class Practice {
        public static void main(String[] args){
               List m = Arrays.asList("one two three four five six siven".split(" "));
                System.out.println(m);
                Collections.swap(m, 2, 3);
                System.out.println(m);
          }
}
运行结果为:
[one, two, three, four, five, six, siven]
[one, two, four, three, five, six, siven]

fill(List list,Object o)方法的使用(含义:用对象o替换集合list中的所有元素)。
nCopies(int n,Object o)方法的使用(含义:返回大小为n的List,List不可改变,其中的所有引用都指向o)。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wwg377655460/article/details/50465469
个人分类: java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭