第十七章:容器深入研究

第十七章:容器深入研究



前言

提示:这里可以添加本文要记录的大概内容:


一、collection的功能方法

collection不包含get随机方法,是因为其子接口包含list和set,而list和set内部维持着不同的顺序,所以必须使用迭代器Iterator。


二、collection的可选操作

1、面向对象设计中的契约,无论你选择如何实现该接口,都可以向接口发送消息,但是可选操作违反这基本原则。
2、collection中的增加或移除都是可选操作,意味着实现类并不需要这些方法提供功能定义。
3、未获支持的操作,可以延迟到需要时在实现。
4、关于集合转数组如下没有演示,是因为数组没有增删改的方法,所以不演示了。

1、未获支持的操作

① 最常见的未获支持的操作,都是源于背后固定尺寸的数据结构支持的容器,当发生了扩容或者缩减数据量将会抛出该异常(即UnsupportedOperationException),但是修改替换则不会。
②Collections.unmodifiableList 任何的修改都会抛出该异常。

public class Unsupported {
  static void test(String msg, List<String> list) {
    System.out.println("--- " + msg + " ---");
    Collection<String> c = list;
    Collection<String> subList = list.subList(1,8);
    // Copy of the sublist:
    Collection<String> c2 = new ArrayList<String>(subList);
    try { c.retainAll(c2); } catch(Exception e) {
      System.out.println("retainAll(): " + e);
    }
    try { c.removeAll(c2); } catch(Exception e) {
      System.out.println("removeAll(): " + e);
    }
    try { c.clear(); } catch(Exception e) {
      System.out.println("clear(): " + e);
    }
    try { c.add("X"); } catch(Exception e) {
      System.out.println("add(): " + e);
    }
    try { c.addAll(c2); } catch(Exception e) {
      System.out.println("addAll(): " + e);
    }
    try { c.remove("C"); } catch(Exception e) {
      System.out.println("remove(): " + e);
    }
    // The List.set() method modifies the value but
    // doesn't change the size of the data structure:
    try { list.set(0, "X");} catch(Exception e) {
      System.out.println("List.set(): " + e);
    }
  }
  public static void main(String[] args) {
    List<String> list =
         Arrays.asList("A B C D E F G H I J K L".split(" "));
    test("Modifiable Copy", new ArrayList<String>(list));
    test("Arrays.asList()", list);
    test("unmodifiableList()",
         Collections.unmodifiableList(new ArrayList<String>(list)));
  }
}

运行结果:

--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException

总结:由于collection的子类没有实现add、clear等方法,这些方法都抛出UnsupportedOperationExpection异常,是因为 ArrayList extends AbstractList也没有实现add、remove、retain等方法,所以如此。

    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

三、List的功能方法

1、list的迭代器

//普通的iterator只有查询功能
private static void iteratorListTest() {
    List<String> list = new ArrayList<String>(Arrays.asList("hello wold 你".split(" ")));
    ListIterator<String> iterator = list.listIterator();
    while (iterator.hasNext()) {
        System.out.println("hasPrevious()之前是否有值:" + iterator.hasPrevious());
        System.out.println("next():" + iterator.next());
        System.out.println("previous()前一个元素的角标:" + iterator.previousIndex());
        System.out.println("nextIndex():" + iterator.nextIndex());
        //相当月next的反方向取值,进入死循环
//            System.out.println("previous()之前的值:" + iterator.previous());
        System.out.println("=======");
    }
}

运行结果:

hasPrevious()之前是否有值:false
next():hello
previous()前一个元素的角标:0
nextIndex()1
=======
hasPrevious()之前是否有值:true
next():wold
previous()前一个元素的角标:1
nextIndex()2
=======
hasPrevious()之前是否有值:true
next():你
previous()前一个元素的角标:2
nextIndex()3
=======

2、list迭代器数据修改

需要进行设置或者移除,则必须配合循环使用。

private static void iteratorModifyTest() {
    List<String> list = new ArrayList<String>(Arrays.asList("hello wold 你".split(" ")));
    ListIterator<String> iter = list.listIterator();
    iter.add("好");
    iter.next();
    iter.set("47");
    iter.remove();
    System.out.println(list);
}

运行结果:

[, wold,]

四、set和储存顺序

1、各种set的特点

1、set:不含重复元素,通过元素的equals方法判断唯一性,set与collection有完全一样的接口,不保证完全存取顺序。
2、HashSet:数组+链表+红黑树结构,元素必须实现hashCode和equals方法,查询速度快(HashMap的键实现的);通过哈希吗查找,然后equals方法判断是否一致。
3、TreeSet:树结构,内部元素有序;元素必须实现comparable接口和覆盖equals方法。
4、必须为散列和树形存储创建一个equals方法。
5、LinkedHashSet:链表结构,存取有序,具有hashMap一样的性能,元素必须实现hashCode方法。
6、良好的编程风格,覆盖equals方法时,也会覆盖hashCode。

class SetType {
  int i;
  public SetType(int n) { i = n; }
  public boolean equals(Object o) {
    return o instanceof SetType && (i == ((SetType)o).i);
  }
  public String toString() { return Integer.toString(i); }
}

class HashType extends SetType {
  public HashType(int n) { super(n); }
  public int hashCode() { return i; }
}

class TreeType extends SetType
implements Comparable<TreeType> {
  public TreeType(int n) { super(n); }
  public int compareTo(TreeType arg) {
    return (arg.i < i ? -1 : (arg.i == i ? 0 : 1));
  }
}

public class TypesForSets {
  static <T> Set<T> fill(Set<T> set, Class<T> type) {
    try {
      for(int i = 0; i < 10; i++)
          set.add(
            type.getConstructor(int.class).newInstance(i));
    } catch(Exception e) {
      throw new RuntimeException(e);
    }
    return set;
  }
  static <T> void test(Set<T> set, Class<T> type) {
    fill(set, type);
    fill(set, type); // Try to add duplicates
    fill(set, type);
    System.out.println(set);
  }
  public static void main(String[] args) {
    test(new HashSet<HashType>(), HashType.class);
    test(new LinkedHashSet<HashType>(), HashType.class);
    test(new TreeSet<TreeType>(), TreeType.class);
    System.out.println("如下Set不支持去重操作======>");
    test(new HashSet<SetType>(), SetType.class);
    test(new HashSet<TreeType>(), TreeType.class);
    test(new LinkedHashSet<SetType>(), SetType.class);
    test(new LinkedHashSet<TreeType>(), TreeType.class);
    System.out.println("如下Set会抛出异常===========");
    try {
      test(new TreeSet<SetType>(), SetType.class);
    } catch(Exception e) {
      System.out.println(e.getMessage());
    }
    try {
      test(new TreeSet<HashType>(), HashType.class);
    } catch(Exception e) {
      System.out.println(e.getMessage());
    }
  }
} 

结果:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
如下Set不支持去重操作======>
[3, 4, 9, 6, 5, 0, 8, 4, 0, 3, 1, 6, 5, 7, 4, 9, 1, 1, 7, 6, 2, 9, 0, 8, 2, 2, 7, 3, 8, 5]
[5, 8, 9, 2, 3, 9, 4, 0, 8, 6, 7, 9, 1, 1, 7, 5, 2, 0, 0, 2, 4, 8, 1, 4, 3, 5, 3, 7, 6, 6]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
如下Set会抛出异常===========
java.lang.ClassCastException: test.SetType cannot be cast to java.lang.Comparable
java.lang.ClassCastException: test.HashType cannot be cast to java.lang.Comparable

五、queue队列

1、队列的常用方法

值得注意的是LinkedList类实现了Queue接口,因此我们可以把LinkedList当成Queue来用。
add      增加一个元索               如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove   移除并返回队列头部的元素   如果队列为空,则抛出一个NoSuchElementException异常
element  返回队列头部的元素         如果队列为空,则抛出一个NoSuchElementException异常
offer    添加一个元素并返回true     如果队列已满,则返回false
poll     移除并返问队列头部的元素   如果队列为空,则返回null
peek     返回队列头部的元素         如果队列为空,则返回null
put      添加一个元素               如果队列满,则阻塞
take     移除并返回队列头部的元素   如果队列为空,则阻塞

2、队列区别于内部排序,不是性能

public class QueueBehavior {
  private static int count = 10;
  static <T> void test(Queue<T> queue, Generator<T> gen) {
    for(int i = 0; i < count; i++)
      queue.offer(gen.next());
    while(queue.peek() != null)
      System.out.print(queue.remove() + " ");
    System.out.println();
  }
  static class Gen implements Generator<String> {
    String[] s = ("one two three four five six seven " +
      "eight nine ten").split(" ");
    int i;
    public String next() { return s[i++]; }
  }
  public static void main(String[] args) {
    test(new LinkedList<String>(), new Gen());
    test(new PriorityQueue<String>(), new Gen());
    test(new ArrayBlockingQueue<String>(count), new Gen());
    test(new ConcurrentLinkedQueue<String>(), new Gen());
    test(new LinkedBlockingQueue<String>(), new Gen());
    test(new PriorityBlockingQueue<String>(), new Gen());
  }
}

结果:

one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two

3、优先队列

PriorityQueue队列实现Comparable接口,覆盖compareTo方法,支持内部排序

class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
  static class ToDoItem implements Comparable<ToDoItem> {
    private char primary;
    private int secondary;
    private String item;
    public ToDoItem(String td, char pri, int sec) {
      primary = pri;
      secondary = sec;
      item = td;
    }
    public int compareTo(ToDoItem arg) {
      if(primary > arg.primary)
        return +1;
      if(primary == arg.primary)
        if(secondary > arg.secondary)
          return +1;
        else if(secondary == arg.secondary)
          return 0;
      return -1;
    }
    public String toString() {
      return Character.toString(primary) +
        secondary + ": " + item;
    }
  }
  public void add(String td, char pri, int sec) {
    super.add(new ToDoItem(td, pri, sec));
  }
  public static void main(String[] args) {
    ToDoList toDoList = new ToDoList();
    toDoList.add("Empty trash", 'C', 4);
    toDoList.add("Feed dog", 'A', 2);
    toDoList.add("Feed bird", 'B', 7);
    toDoList.add("Mow lawn", 'C', 3);
    toDoList.add("Water lawn", 'A', 1);
    toDoList.add("Feed cat", 'B', 1);
    while(!toDoList.isEmpty())
      System.out.println(toDoList.remove());
  }
} 

结果:

A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash

五、Map

map中表现为键值对保存呈现次序、效率、对象周期、多线程工作、判断“键”的等价策略方面。
在这里插入图片描述

1、linkedHashMap

散列化所有元素,以插入时顺序返回键值对。

public class LinkedHashMapDemo {
  public static void main(String[] args) {
    LinkedHashMap<Integer,String> linkedMap = new LinkedHashMap<Integer,String>(new CountingMapData(9));
    print(linkedMap);
    // 最少使用算法构造,没有使用的放在队列前面
    linkedMap =
      new LinkedHashMap<Integer,String>(16, 0.75f, true);
    linkedMap.putAll(new CountingMapData(9));
    print(linkedMap);
    for(int i = 0; i < 6; i++) // Cause accesses:
      linkedMap.get(i);
    print(linkedMap);
    linkedMap.get(0);
    print(linkedMap);
  }
} 

结果:

{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0}
{6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0}
{6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0}

2、HashMap

散列码:对象调用自身的hashCode方法(也称散列函数)生成的数字即是,默认使用对象地址计算散列码。
散列:

1、存储元数最快的数据结构是数组,所以用数组存储链表或红黑树。
2、由于线性查询速度慢;而散列快,在于散列算法,所以散列表HashMap、HashSet、LinkedHashSet快。
3、散列数据结构覆盖hashCode和equals方法
4、由于瓶颈是键的查询速度。
5、在HashMap中,哈希桶数组table的长度length大小必须为2的n次方,不然会取最近的2的次方。
在这里插入图片描述

HashMap设计与原理:

结构:数组 + 链表 + 红黑树,链表保存的是Entry 对象存储四个属性(hash,key,value,next)。

容量:也称槽位、桶位,默认初始容量16,。

负载因子:默认0.75,决定空间和效率问题,空间利用率越高这个值越接近1,这样就会导致容易发生hash碰撞查询效率慢。

查询元素:键通过hash算法计算出散列码(这里的Hash算法本质上就是三步:取key的hashCode值、高位运算、取模运算。),也就是桶的下标,然后通过对链表或红黑树进行equals线性查询。

扩容:负载因子 * 容量,当达元素个数到阈值时将进行扩容,数组变成原来的2倍。

3、hashCode

1、同一个对象无论何时调用hashCode都应该生成一样的值。
2、默认hashCode是对象地址计算的散列码,应该使用对象内有意义的成员信息。
3、设计hashCode()方法指导:
给result变量先赋值一个非零int值,然后对成员变量计算出散列码,然后合并计算

如下案例是由于键没有覆盖hashCode方法,导致无法找到该key:

public class Groundhog {
  protected int number;
/*  @Override
  public boolean equals(Object obj) {
    return obj instanceof Groundhog && ( number == ((Groundhog) obj).number);
  }

  @Override
  public int hashCode() {
    return number;
  }*/

  public Groundhog(int n) { number = n; }
  public String toString() {
    return "Groundhog #" + number;
  }
}
public class SpringDetector {
  // Uses a Groundhog or class derived from Groundhog:
  public static <T extends Groundhog>
  void detectSpring(Class<T> type) throws Exception {
    Constructor<T> ghog = type.getConstructor(int.class);
    Map<Groundhog,Prediction> map = new HashMap<Groundhog,Prediction>();
    for(int i = 0; i < 10; i++)
      map.put(ghog.newInstance(i), new Prediction());
    print("map = " + map);
    Groundhog gh = ghog.newInstance(3);
    print("Looking up prediction for " + gh);
    if(map.containsKey(gh))
      print(map.get(gh));
    else
      print("Key not found: " + gh);
  }
  public static void main(String[] args) throws Exception {
    detectSpring(Groundhog.class);
  }
} /* Output:
map = {Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!}
Looking up prediction for Groundhog #3
Key not found: Groundhog #3
*///:~

六、集合总结对比

1、ArrayList就是数组结构,有角标,查询速度很快。
2、linkedList就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();
3、hashSet最长用,查询速度快。TreeSet基于TreeMap保持元素顺序。LinkedHashSet基于散列表,用链表维护内部顺序。

七、持有引用

1、抽象类Reference的三个继承:SoftReference、WeakReference、PhantomReference,有强到弱,
2、垃圾回收器回收没有引用的对象。
3、想继续持有某个对象的引用,希望以后还能访问该对象,但是也希望内存不足时垃圾回收器释放它,就使用Reference对象。

 //: containers/References.java
// Demonstrates Reference objects
import java.lang.ref.*;
import java.util.*;

class VeryBig {
  private static final int SIZE = 10000;
  private long[] la = new long[SIZE];
  private String ident;
  public VeryBig(String id) { ident = id; }
  public String toString() { return ident; }
  protected void finalize() {
    System.out.println("Finalizing " + ident);
  }
}

public class References {
  private static ReferenceQueue<VeryBig> rq =  new ReferenceQueue<VeryBig>();
  public static void checkQueue() {
    Reference<? extends VeryBig> inq = rq.poll();
    if(inq != null)
      System.out.println("In queue: " + inq.get());
  }
  public static void main(String[] args) {
    int size = 10;
    // Or, choose size via the command line:
    if(args.length > 0)
      size = new Integer(args[0]);
    LinkedList<SoftReference<VeryBig>> sa = new LinkedList<SoftReference<VeryBig>>();
    for(int i = 0; i < size; i++) {
      sa.add(new SoftReference<VeryBig>( new VeryBig("Soft " + i), rq));
      System.out.println("Just created: " + sa.getLast());
      checkQueue();
    }
    LinkedList<WeakReference<VeryBig>> wa = new LinkedList<WeakReference<VeryBig>>();
    for(int i = 0; i < size; i++) {
      wa.add(new WeakReference<VeryBig>(
        new VeryBig("Weak " + i), rq));
      System.out.println("Just created: " + wa.getLast());
      checkQueue();
    }
    SoftReference<VeryBig> s = new SoftReference<VeryBig>(new VeryBig("Soft"));
    WeakReference<VeryBig> w = new WeakReference<VeryBig>(new VeryBig("Weak"));
    System.gc();
    LinkedList<PhantomReference<VeryBig>> pa = new LinkedList<PhantomReference<VeryBig>>();
    for(int i = 0; i < size; i++) {
      pa.add(new PhantomReference<VeryBig>( new VeryBig("Phantom " + i), rq));
      System.out.println("Just created: " + pa.getLast());
      checkQueue();
    }
  }
} /* (Execute to see output) *///:~

Just created: java.lang.ref.SoftReference@1e80bfe8
Just created: java.lang.ref.SoftReference@66a29884
Just created: java.lang.ref.SoftReference@4769b07b
Just created: java.lang.ref.SoftReference@cc34f4d
Just created: java.lang.ref.SoftReference@17a7cec2
Just created: java.lang.ref.SoftReference@65b3120a
Just created: java.lang.ref.SoftReference@6f539caf
Just created: java.lang.ref.SoftReference@79fc0f2f
Just created: java.lang.ref.SoftReference@50040f0c
Just created: java.lang.ref.SoftReference@2dda6444
Just created: java.lang.ref.WeakReference@5e9f23b4
Just created: java.lang.ref.WeakReference@4783da3f
Just created: java.lang.ref.WeakReference@378fd1ac
Just created: java.lang.ref.WeakReference@49097b5d
Just created: java.lang.ref.WeakReference@6e2c634b
Just created: java.lang.ref.WeakReference@37a71e93
Just created: java.lang.ref.WeakReference@7e6cbb7a
Just created: java.lang.ref.WeakReference@7c3df479
Just created: java.lang.ref.WeakReference@7106e68e
Just created: java.lang.ref.WeakReference@7eda2dbb
Just created: java.lang.ref.PhantomReference@6576fe71
Finalizing Weak 0
Finalizing Weak
In queue: null
Finalizing Weak 9
Just created: java.lang.ref.PhantomReference@76fb509a
Finalizing Weak 8
In queue: null
Finalizing Weak 7
Just created: java.lang.ref.PhantomReference@300ffa5d
Finalizing Weak 6
In queue: null
Finalizing Weak 5
Just created: java.lang.ref.PhantomReference@1f17ae12
Finalizing Weak 4
In queue: null
Finalizing Weak 3
Just created: java.lang.ref.PhantomReference@4d405ef7
Finalizing Weak 2
In queue: null
Finalizing Weak 1
Just created: java.lang.ref.PhantomReference@6193b845
In queue: null
Just created: java.lang.ref.PhantomReference@2e817b38
In queue: null
Just created: java.lang.ref.PhantomReference@c4437c4
In queue: null
Just created: java.lang.ref.PhantomReference@433c675d
In queue: null
Just created: java.lang.ref.PhantomReference@3f91beef
In queue: null

2、WeakHashMap

每个值都保存一份实例,当没有被引用的键,键和值对会被清理(实际只清理键)。

class Element {
  private String ident;
  public Element(String id) { ident = id; }
  public String toString() { return ident; }
  public int hashCode() { return ident.hashCode(); }
  public boolean equals(Object r) {
    return r instanceof Element &&
      ident.equals(((Element)r).ident);
  }
  protected void finalize() {
    System.out.println("Finalizing " +
      getClass().getSimpleName() + " " + ident);
  }
}

class Key extends Element {
  public Key(String id) { super(id); }
}

class Value extends Element {
  public Value(String id) { super(id); }
}

public class CanonicalMapping {
  public static void main(String[] args) {
    int size = 1000;
    // Or, choose size via the command line:
    if(args.length > 0)
      size = new Integer(args[0]);
    Key[] keys = new Key[size];
    WeakHashMap<Key,Value> map = new WeakHashMap<Key,Value>();
    for(int i = 0; i < size; i++) {
      Key k = new Key(Integer.toString(i));
      Value v = new Value(Integer.toString(i));
      if(i % 3 == 0)
        keys[i] = k; // 保存在数组中的没有被gc
      map.put(k, v);
    }
    System.gc();
  }
} /* (Execute to see output) *///:~

美团技术

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值