9. 集合

集合

也就是容器。理论上: 集合存储不同==(类类型 引用类型)==类型的元素的。 实际开发: 存储相同类型

数组也是容器。存储相同数据类型的元素 length add/delete 操作比较复杂 效率最快 查询(index)

元素特征元素重复元素有序
Collection一个元素(单列)不定(看实现类)无序(没有索引位置的)
Map一组元素(键值对 key—value)key唯一的 value可重复的无序

泛型

标识: <>  泛型类  泛型接口  泛型方法
  A-z: 参数化数据类型(代表所有的引用类型)     
  泛型: 类型的自动转换
      与集合结合运用的时候  限制集合元素的数据类型的。     

使用集合: 新增(很少做 底层方法) 遍 历(用户)

数据库—> 查询数据库数据 存储集合

1. Collection

常用功能方法:

boolean add(E e)  新增集合元素
    
boolean remove(Object o) 
default boolean removeIf(Predicate<? super E> filter)  删除满足条件的元素
void clear()  清空集合所有元素
    
boolean isEmpty()  判断集合是否是空
boolean contains(Object o) 判断集合里面是否包含指定的元素
    
int size()  

Iterator<E> iterator() 遍历功能  获得集合对象迭代器对象(将集合的所有的元素 都放在迭代器对象中)
     boolean hasNext()  判断光标后面是否有更多的元素需要迭代
     E next()   获得光标之后的元素数据
     default void remove()   删除光标之后元素
    
default void forEach(Consumer<? super T> action)   遍历
    
Object[] toArray()  集合转数组
<T> T[] toArray(T[] a)  推荐使用
参照CollectionDemo.java代码

list vs Set

元素是否有序(索引)元素是否重复
List有索引位置数据可重复
Set无序唯一 不可重复的

1.1 List

void add(int index, E element)  
E get(int index)  
E remove(int index)  
E set(int index, E element)
List<E> subList(int fromIndex, int toIndex)  

常用实现类:

常用的数据结构: 数组 链表 哈希表 栈 红黑树 队列

链表:
  单向: 记忆当前数据  以及下一个元素引用  element  next
  双向:  记忆当前数据  以及下一个还有上一个元素的引用  prev    element  next
底层数据结构增加/删除/查询效率线程安全
ArrayList动态数组查询最快不安全
LinkedList双向链表新增 删除最快 查询慢不安全
Vector动态数组偏慢安全
CopyOnWriteArrayList动态数组比vector效率偏高安全

1. ArrayList

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
 为什么查询比较快?
    1.底层的数据结构
    2.RandomAccess  快速访问    
ArrayList() 构造一个初始容量为十的空列表。
ArrayList(int initialCapacity)   推荐使用 10 
ArrayList(Collection<? extends E> c) 
1.1 add()
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     // {} 初始化为0   10???
     //elementData: 维护集合里面所有元素数据
    }
// private int size; 维护集合的元素个数
public boolean add(E e) {//100
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

 private void ensureCapacityInternal(int minCapacity) {//1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//10
    }
//private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {//{}  1
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);//(10,1)
        }
        return minCapacity;
    }

//protected transient int modCount = 0;
//modCount: 代表操作集合元素次数  add/delete modCount都会累增+1
private void ensureExplicitCapacity(int minCapacity) {//10
        modCount++; //modCount = 1

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

private void grow(int minCapacity) {//10
        // overflow-conscious code
        int oldCapacity = elementData.length;//0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//0   1.5倍的增长
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//10
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
1.2 remove()
 public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
private void fastRemove(int index) {//删除元素索引
        modCount++;//
        int numMoved = size - index - 1;
        if (numMoved > 0)//判断索引后面是否还存在元素  元素移动位置
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

2. LinkedList

手写一个双向链表。 可以充当队列。FIFO first in first out

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList() 
void addFirst(E e)  /boolean offerFirst(E e)  /void push(E e) 新增元素至头部 
    
void addLast(E e)  / boolean offer(E e) /boolean offerLast(E e)新增元素至尾部  
    
E getFirst()  /E peek() / E peekFirst()  /E element() 查询第一个元素 
E getLast()  /E peekLast()  查询最后一个元素

E poll()  /E pop()  /E pollFirst()/E remove()  /E removeFirst() 查询并删除第一个元素  
E pollLast()/E removeLast()    查询并删除最后一个元素 
1. add()
public boolean add(E e) {
        linkLast(e);
        return true;
    }

// transient Node<E> last; //null
//   transient Node<E> first; //null
 void linkLast(E e) {//200
        final Node<E> l = last;//null
        final Node<E> newNode = new Node<>(l, e, null);//(null,200,null)
        last = newNode;//last=200
        if (l == null)
            first = newNode;//200
        else
            l.next = newNode;
        size++;
        modCount++;
  }

//第2次新增
void linkLast(E e) {//300
        final Node<E> l = last;//第1次的元素 200
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;//上一个元素要记住当前元素的引用
        size++;
        modCount++;
    }

//双向链表
private static class Node<E> {
        E item;//当前元素的数据
        Node<E> next;//下一个元素引用
        Node<E> prev;//上一个元素引用

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

 linkedList.remove(1);
 num = 3  index = 1;  size = 5
     
 linkedList.remove(3);
 index =3    size = 5
     
if (index < (size >> 1)) {  //(1<2)
            Node<E> x = first;//第一个元素  1
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
     } else {
            Node<E> x = last;//7
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
   }

3. Vector

Vector(int initialCapacity) 
Vector(int initialCapacity)     
public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

 private void ensureCapacityHelper(int minCapacity) {//1
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)// 1-10
            grow(minCapacity);
    }
 private void grow(int minCapacity) {//11
        // overflow-conscious code
        int oldCapacity = elementData.length;//10
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        //如果不指定capacityIncrement 每次都是2倍增长
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

4. CopyOnWriteArrayList

final transient ReentrantLock lock = new ReentrantLock();
public boolean add(E e) {
        final ReentrantLock lock = this.lock;//可重入锁
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;//0
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

1.2 Set

元素唯一的 元素无序的

数据结构元素是否可以为null线程安全
HashSetHashMap可以
LinkedHashSetHashMap可以
TreeSetTreeMap不可以
CopyOnWriteArraySet数组可以

2. Map<K,V>

存储一组元素: key—value

常用实现类

数据结构k/v是否可以为null线程安全
TreeMap红黑树k不能为null v可以为null
HashMap<K,V>位桶(数组)+单向链表+红黑树/哈希算法都是可以为null
ConcurrentHashMap哈希表都不能为null
LinkedHashMap链表+哈希表都是可以为null
HashTable哈希表都不能为null

常用方法

static interface  Map.Entry<K,V>  将map的k和v封装成一个个的entry对象
    1.K getKey()  
    2.V getValue()  
    
    
 void clear()  
 boolean containsKey(Object key)  
 boolean containsValue(Object value)  
 
 Set<Map.Entry<K,V>> entrySet()  获得map集合的k/v 装成一个个的entry对象存储在set集合里面
 Set<K> keySet()  获得map集合里面所有key  
 default void forEach(BiConsumer<? super K,? super V> action)   
    
 V get(Object key)  
 default V getOrDefault(Object key, V defaultValue)     
 
 boolean isEmpty()  

    
 V put(K key, V value) 新增
    
 V remove(Object key)      
 default boolean remove(Object key, Object value)  
    
 default V replace(K key, V value)  
 default boolean replace(K key, V oldValue, V newValue)
    
 int size()  
 Collection<V> values()  获得map集合里面所有的value    

1. HashMap<K,V>

  • 基于哈希表的实现的Map接口。 k/V都可以为null 集合数据完全无序。 线程不安全
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
常用构造:
HashMap() 创建map集合对象 默认初始容量(16)和默认负载系数(0.75)。 
HashMap(int initialCapacity)  指定初始化容量  推荐
    
注意:    
initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
    
反例: HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,resize()方法总共会调用 8 次,反复重建哈希表和数据迁移。当放置的集合元素个数达千万级时会影响程序性能。    

源码分析:

final float loadFactor;
static final float DEFAULT_LOAD_FACTOR = 0.75f;//负载系数
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

public V put(K key, V value) {//1   100
    return putVal(hash(key), key, value, false, true);//1  1  100
 }

static final int hash(Object key) {//计算key的hash
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  }

transient Node<K,V>[] table; //数组 位桶  null   存储map集合元素

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//第一次新增元素的时候执行resize() 扩容
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);//新增新的元素 key不重复的时候
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;//hash冲突 key值一样  直接新的value覆盖旧的value
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {//hash一致 但是key内容不同 链表存储元素
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //TREEIFY_THRESHOLD=8
                            //binCount=7 代表同一个hash下的元素有7个的时候  该新增第8个
                            //将链表的数据改为树状结构维护数据
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //在链表上,添加了多个重复的key 需要value覆盖
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)//threshold: 数组是否扩容临界值  
            resize();
        afterNodeInsertion(evict);
        return null;
    }
//threshold=12
static final int UNTREEIFY_THRESHOLD = 6;//当树状元素个数<6 将树状转为链表结构
final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            //第一次新增元素的时候  扩容
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

2. LinkedHashMap

新增顺序遍历顺序是一致的

如果自定义类对象充当HashMap或者LinkedHashMap的key的话,一定要重写equals和hashcode 保证元素的唯一性

private static void testHashMap1() {

        //如果自定义类对象作为map的key存储   一定要重写hashcode和equals
        HashMap<User,Integer> map = new HashMap<>(16);

        map.put(new User(1,"jim",123),1);
        map.put(new User(1,"jim",123),2);
        map.put(new User(1,"jim",123),3);
        map.put(new User(1,"jim",123),4);
        map.put(new User(1,"jim",123),5);

        map.forEach((user,value)->{
            System.out.println(user+","+value);
        });
    }

@Setter
@Getter
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class User {

    private Integer id;
    private String name;
    private  Integer age;

}

3. TreeMap

元素有序: key 会按照自然顺序排列。 默认是升序。 要求Key不能为null key的数据类型必须实现Comparable接口。

TreeMap(Comparator<? super K> comparator)  使用自定义Comparator规则排序
TreeMap() 默认使用Comparable的规则
//private transient Entry<K,V> root; null  第一次新增的那个元素

public V put(K key, V value) {//1  100
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

自定义的类对象充当TreeMap的key 有可能会出现类型转换的异常:

Exception in thread “main” java.lang.ClassCastException: com.javasm.map_.User cannot be cast to java.lang.Comparable

private static void testTreeMap1() {
        TreeMap<User,Integer> map = new TreeMap<>();
        //Exception in thread "main" java.lang.ClassCastException: com.javasm.map_.User cannot be cast to java.lang.Comparable
        map.put(new User(1,"jim",123),1);
        map.put(new User(1,"jim",123),2);
        map.put(new User(1,"jim",123),3);
        map.put(new User(1,"jim",123),4);
        map.put(new User(1,"jim",123),5);

        map.forEach((user,value)->{
            System.out.println(user+","+value);
        });
    }

3. 集合元素排序

3.1 List

private static void demo1() {

//        List<Integer> list = new ArrayList<>(10);
//        Collections.addAll(list, 100, 10, 90, 1, 200, 100);

        //Arrays.asList()
        List<Integer> list = Arrays.asList(100, 10, 90, 1, 200, 100);

        System.out.println("排序之前:" + list);

        Collections.sort(list);//Integer.compareTo()

        System.out.println("排序之后:" + list);
        System.out.println(list.get(0));
        System.out.println(list.get(list.size() - 1));

    }
private static void demo2() {
        List<User> list = Arrays.asList(
                new User(1, "jim", 20),
                new User(10, "tim", 50),
                new User(11, "jim", 41),
                new User(6, "jim", 41));


        //默认按照年龄升序排列   如果年龄相同  按照id降序排列
        System.out.println("排序之前:" + list);

        Collections.sort(list);

        System.out.println("排序之后:" + list);

    }
private static void demo2() {
        List<User> list = Arrays.asList(
                new User(1, "jim", 20),
                new User(10, "tim", 50),
                new User(11, "jim", 41),
                new User(6, "jim", 41));


        //排序规则:  默认按照年龄升序排列   如果年龄相同  按照id降序排列
        //一般会使用外部比较器: java.util.Comparator<T>

        System.out.println("排序之前:" + list);

//        Collections.sort(list, new Comparator<User>() {
//            @Override
//            public int compare(User user1, User user2) {
//                int result = user1.getAge().compareTo(user2.getAge());
//                if (result == 0) {
//                    result = user1.getId().compareTo(user2.getId());
//                }
//                return result;
//            }
//        });

      /*  Collections.sort(list, (user1, user2) -> {
            int result = user1.getAge().compareTo(user2.getAge());
            if (result == 0) {
                result = user1.getId().compareTo(user2.getId());
            }
            return result;
        });*/

        Collections.sort(list, Comparator.comparing(User::getAge).thenComparing(User::getId));
        System.out.println("排序之后:" + list);
    }

3.2 Set

HashSet / LinkedHashSet 不能直接排

TreeSet 元素按照自然顺序排列

  private static void demo2() {
        TreeSet<User> set = new TreeSet<>(Comparator.comparing(User::getAge));
        Collections.addAll(set,
                new User(1, "jim", 20),
                new User(10, "tim", 50),
                new User(11, "jim", 41),
                new User(7, "jim2", 41));
        System.out.println(set);
    }

3.3 Map

  private static void demo3() {

        TreeMap<User, Integer> map = new TreeMap<>(Comparator.comparing(User::getAge));
        map.put(new User(1, "jim", 20), 1);
        map.put(new User(10, "tim", 50), 2);
        map.put(new User(11, "jim", 41), 3);
        map.put(new User(7, "jim2", 41), 4);

        System.out.println(map);

    }

4. Collections

提供了很多静态的方法  操作集合元素
static <T> boolean addAll(Collection<? super T> c, T... elements)    将数据新增到指定集合对象里面
void sort(List<T> list)   排序
static <T> void sort(List<T> list, Comparator<? super T> c)  
 
static <T extends Object & Comparable<? super T>>
T max(Collection<? extends T> coll)  
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)      

在集合里面,很多集合类都是现成不安全的类,可以使用Collections里面的方法将现成不安全的集合类转换成现成安全的集合对象。
 static <T> List<T> synchronizedList(List<T> list)  
 static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)  
 static <T> Set<T> synchronizedSet(Set<T> s)  
    
static void shuffle(List<?> list)  使用指定的随机源打乱集合元素

5. Stream机制

https://developer.ibm.com/zh/articles/j-lo-java8streamapi/

参照: StreamDemo.java

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值