第11章 持有对象

第11章 持有对象

集合类: List/Set/Queue/Map

11.1 泛型和类型安全的容器

public class Apple {

    public void name(){
        System.out.println("Apple");
    }
}
public class Orange {
}

// 不使用泛型
public class Test {
    @SuppressWarnings("unchecked") // 抑制警告信息
    public static void main(String[] args) {
        // 希望创建Apple容器
        ArrayList arrayList =  new ArrayList();
        arrayList.add(new Apple());
        arrayList.add(new Apple());
        // 可以添加任意类型
        arrayList.add(new Orange());
        for (Object o : arrayList) {
            // 获得对象时,必须转型
            ((Apple) o).name();
        }
    }
}

// 使用泛型
public class Test {
    @SuppressWarnings("unchecked") // 抑制警告信息
    public static void main(String[] args) {
        ArrayList<Apple> arrayList = new ArrayList<>();
        arrayList.add(new Apple());
        arrayList.add(new Apple());
        // 不可以添加除指定类型
        // arrayList.add(new Orange());
        // 无需转型
        for (Apple apple : arrayList) {
            apple.name();
        }
    }
}

11.2 基本概念

Collection 一个独立元素的序列。List按照插入顺序保存,Set不能有重复元素,Queue按照排队规则确认元素的顺序。

Map 一组成对的“键值对”对象,允许使用键来查找值,被称为映射表或者关联数组。

public class Test {
    public static void main(String[] args) {
        Collection<Integer> c = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            // 注意:List总会添加,Set不存在时添加,存在时忽略
            c.add(i);
        }
        c.forEach(i -> System.out.println(i));
    }
}

11.3 添加一组元素

基本使用

public class Test {
    public static void main(String[] args) {
        // Arrays.asList()将数组或者可变参数转为List
        Integer[] integers = new Integer[]{1,2,3};
        List<Integer> asList1 = Arrays.asList(integers);
        List<Integer> asList2 = Arrays.asList(1, 2, 3);
        // 根据已有集合创建新集合
        Collection<Integer> c = new ArrayList<>(asList1);
        // Collection.addAll()方法,添加集合
        c.addAll(asList2);
        // Collections.addAll()向集合中添加数组或者可变参数
        Collections.addAll(c,integers);
        Collections.addAll(c,1,2,3);
    }
}

asList()问题

class Snow{}
class A extends Snow{}
class B extends Snow{}
class Power extends Snow{}
class e extends Power{}
class f extends Power{}
public class Test {
    public static void main(String[] args) {
        List<Snow> snows1 = Arrays.asList(new A(), new e(), new f());
        // 因为目标中最近的公约数是Power,该方法不会创建List<Snow>
        List<Power> snows2 = Arrays.asList(new e(), new f());
        // 使用显示类型参数说明解决这一问题
        List<Snow> snows = Arrays.<Snow>asList(new e(), new f());
    }
}

11.4 容器的打印

public class Test {

    public void fill(Collection<String> c){
       Collections.addAll(c,"a","b","c","a","b");
        System.out.println(c);
    }

    public void fill(Map<String,String> map){
        map.put("name","ding");
        map.put("age","12");
        map.put("high","175");
        System.out.println(map);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.fill(new ArrayList<>());
        test.fill(new LinkedList<>());
        test.fill(new HashSet<>());
        test.fill(new TreeSet<>());
        test.fill(new LinkedHashSet<>());
        test.fill(new HashMap<>());
        test.fill(new TreeMap<>());
        test.fill(new LinkedHashMap<>());
    }
}
/*
[a, b, c, a, b]
[a, b, c, a, b]
[a, b, c]
[a, b, c]
[a, b, c]
{high=175, name=ding, age=12}
{age=12, high=175, name=ding}
{name=ding, age=12, high=175}
*/

11.5 List

ArrayList 随机访问快,中间插入删除慢

LinkedList 中间插入删除快,随机访问慢

public class Test {
    public static void print(Object o) {
        System.out.println(o);
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "a", "b", "h"));
        print("1 " + list);
        // 末尾添加元素
        list.add("e");
        // 指定位置插入元素,其他顺序后移
        list.add(1, "f");
        print("2 " + list);
        // 是否包含该元素
        boolean e = list.contains("e");
        print("3 " + e);
        // 删除指定的元素
        boolean f = list.remove("f");
        // 删除指定位置的元素
        list.remove(4);
        print("4 " + f);
        // 获取指定位置的元素
        String s = list.get(0);
        print("5 " + s);
        // 获取指定元素的第一个索引
        int a = list.indexOf("a");
        print("6 " + a);
        print(list);
        // 截取子集合,左闭右开,注意,子集合是老集合的映像,两者的改变会相互影响
        List<String> subList = list.subList(0, 3);
        print("7 " + subList);
        print(subList);
        // 建立和原有集合不相关的新集合
        ArrayList<String> strings = new ArrayList<>(subList);
        strings.remove("a");
        // 是否包含指定集合的所有元素
        boolean all = list.containsAll(subList);
        print("8 " + all);
        // 升序排序集合
        Collections.sort(list);
        print("9 " + list);
        // 打乱集合
        Collections.shuffle(list);
        print("10 " + list);
        // 取得两个集合的交集
        list.retainAll(strings);
        print("11 " + list);
        // 移除指定集合中的所有元素
        list.removeAll(strings);
        print("12 " + list);
        list.add(0, "p");
        // 替换指定位置的元素
        list.set(0, "o");
        print("13 " + list);
        // 判断集合是否为空
        boolean empty = list.isEmpty();
        // 获得集合的元素数
        int num = list.size();
        print("14 " + empty);
        // 集合转数组
        Object[] objects = list.toArray();
        String[] strings1 = list.toArray(new String[0]);
        print("15 " + strings1[0]);
    }
}
/*
1 [a, b, c, a, b, h]
2 [a, f, b, c, a, b, h, e]
3 true
4 true
5 a
6 0
[a, b, c, a, h, e]
7 [a, b, c]
[a, b, c]
8 true
9 [a, a, b, c, e, h]
10 [e, c, h, a, b, a]
11 [c, b]
12 []
13 [o]
14 false
15 o
*/

11.6 迭代器

.基本使用

public class Test {
    public static void print(Object o) {
        System.out.println(o);
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "a", "b"));
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            print(iterator.next());
        }
        iterator = list.iterator();
        for (int i = 0; i < 3; i++) {
            iterator.next();
            // 调用remove方法前必须调用next()方法
            iterator.remove();
        }
        print(list);
    }
}

解耦操作

获得序列中的对象与容器分离

   public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c", "a", "b");
        ArrayList<String> arrayList = new ArrayList<>(list);
        LinkedList<String> linkedList = new LinkedList<>(list);
        HashSet<String> hashSet = new HashSet<>(list);
        TreeSet<String> treeSet = new TreeSet<>(list);
        display(arrayList.iterator());
        display(linkedList.iterator());
        display(hashSet.iterator());
        display(treeSet.iterator());
    }

    public static void display(Iterator<String> it){
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }

ListIterator

    public static void main(String[] args) {
        List<String> aslist = Arrays.asList("a", "b", "c");
        List<String> list = new ArrayList<>(aslist);
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            // 当前元素 下一个元素索引 前一个元素索引
            System.out.println(it.next()+" "+it.nextIndex()+" "+ it.previousIndex());
            /*
            a 1 0
            b 2 1
            c 3 2
             */
            // 前一个元素
            //it.previous(); // 需要注销,否则死循环
        }
        while (it.hasPrevious()){
            System.out.println(it.previous());
            /*
            c
            b
            a
             */
        }
        // 指定迭代器开始的位置
        it = list.listIterator(1);
        while (it.hasNext()){
            System.out.println(it.next());
            /*
            b
			c
             */
        }

    }

11.7 LinkedList

   public static void main(String[] args) {
        List<String> aslist = Arrays.asList("a", "b", "c");
        //List<String> aslist = Arrays.asList();
        LinkedList<String> linkedList = new LinkedList<>(aslist);
        // 获取第一个元素
        String first = linkedList.getFirst(); // 集合为空抛异常NoSuchElementException
        String element = linkedList.element(); // 集合为空抛异常NoSuchElementException
        String peek = linkedList.peek(); // 获得null
        // 删除第一个元素
        String remove = linkedList.remove(); // 集合为空抛异常NoSuchElementException
        String removeFirst = linkedList.removeFirst(); // 集合为空抛异常NoSuchElementException
        String poll = linkedList.poll(); // 获得null
        // 在头部添加元素
        linkedList.addFirst("f"); // 无返回
        // 在尾部添加元素
        boolean e = linkedList.add("e"); // 返回是否成功
        linkedList.addLast("g");
        // 移除最后一个元素
        String removeLast = linkedList.removeLast();
    }

11.8 Stack(栈)

class Stack<T>{
    private LinkedList<T> linkedList = new LinkedList<>();

    /**
     * 添加元素
     * @param t
     * @return
     */
    public void push(T t){
        linkedList.addFirst(t);
    }

    /**
     * 获得元素
     * @return
     */
    public T peek(){
        return linkedList.getFirst();
    }

    /**
     * 移除元素
     * @return
     */
    public T pop(){
        return linkedList.removeFirst();
    }

    /**
     * 判断是否为空
     * @return
     */
    public boolean isEmpty(){
        return linkedList.isEmpty();
    }

    public String toString(){
        return linkedList.toString();
    }
}

  public static void main(String[] args) {
        List<String> aslist = Arrays.asList("a", "b", "c");
        Stack<String> stack = new Stack<>();
        for (String s : aslist) {
            stack.push(s);
        }
        System.out.println(stack);
        while (!stack.isEmpty()){
            System.out.println(stack.peek());
            stack.pop();
        }

    }

注意: 如果只需要栈的行为,java.util包中的Stack是不合适的,它不该继承集合类,继承导致它有了其他方法。

11.9 Set

Set最长用的场景是判断元素是否在该集合下。

   public static void main(String[] args) {
        Random random = new Random();
        Set<Integer> set1 = new HashSet<>();
        Set<Integer> set2 = new TreeSet<>();
        Set<Integer> set3 = new LinkedHashSet<>();
        for (int i = 0; i < 10000; i++) {
            // 随机排序,会出现按顺序排序的假象,因为hashcode相同
            set1.add(random.nextInt(20));
            // 按顺序排序
            set2.add(random.nextInt(20));
            // 按插入顺序排序
            set3.add(random.nextInt(20));
        }
        System.out.println(set1);
        System.out.println(set2);
        System.out.println(set3);
        /*
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
		[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
		[0, 2, 12, 3, 7, 8, 13, 5, 9, 16, 6, 17, 15, 14, 19, 18, 1, 10, 11, 4]
         */
    }

基本用法

 public static void main(String[] args) {
        List<String> asList = Arrays.asList("a", "b", "c");
        // 创建
        Set<String> set1 = new HashSet<>(asList);
        // 是否包含
        System.out.println("1 "+set1.contains("a"));
        Set<String> set2 = new HashSet<>();
        // 批量添加
        Collections.addAll(set2,"a","b");
        // 是否批量包含
        boolean containsAll = set1.containsAll(set2);
        System.out.println("2 "+containsAll);
        // 批量删除
        boolean removeAll = set1.removeAll(set2);
        System.out.println("3 "+set2);
    }

11.10 Map

验证Random的随机性

    public static void main(String[] args) {
        Map<Integer,Integer> map = new HashMap<>();
        Random random = new Random();
        for (int i = 0; i < 1000; i++) {
            int key = random.nextInt(20);
            Integer v = map.get(key);
            map.put(key,v == null?1:++v);
        }
        System.out.println(map);
    }
/*
{0=52, 1=50, 2=48, 3=54, 4=61, 5=44, 6=55, 7=56, 8=57, 9=40, 10=56, 11=52, 12=51, 13=35, 14=50, 15=52, 16=47, 17=38, 18=46, 19=56}
*/

是否包含key和value

 public static void main(String[] args) {
        Map<String,String> map = new HashMap<>();
        map.put("name","ding");
        map.put("age","13");
        System.out.println(map.containsKey("name")); // true
        System.out.println(map.containsValue("ding")); // true
    }

组成更复杂的结构

  public static void main(String[] args) {
        Map<String,List<Integer>> map = new HashMap<>();
        map.put("骰子",Arrays.asList(1,2,3,4,5,6));
        map.put("扑克牌",Arrays.asList(1,2,3,4));
        System.out.println(map.keySet()); // key的Set
        System.out.println(map.values()); // values的Collection
        // 遍历map的实体
        for (Map.Entry<String, List<Integer>> stringListEntry : map.entrySet()) {
            System.out.println(stringListEntry.getKey());
            // 遍历实体中的List
            for (Integer i : stringListEntry.getValue()) {
                System.out.println(i);
            }
        }
    }

11.11 Queue队列

基本使用

public static void main(String[] args) {
       Queue<Character> queue = new LinkedList<>();
        for (char c : "Hello World".toCharArray()) {
            // 将一个元素插入队尾
            queue.offer(c);        
        }
    	re(queue);
    }

    public static void re(Queue queue){
        // 返回队头,队列为空null
        // queue.element() != null NoSuchElementException
        while (queue.peek() != null){
            // 移除返回队头
            System.out.println(queue.remove()); // NoSuchElementException
            //queue.poll() 为空为null
        }
    }

PriorityQueue(优先级队列)

插入时会根据优先级顺序排列,优先级高的排在前面,默认从小到大。

 public static void main(String[] args) {
        PriorityQueue priorityQueue = new PriorityQueue();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int an = random.nextInt(20);
            priorityQueue.offer(an);
        }
        re(priorityQueue); // 1 2 2 3 7 10 15 17 19 19
        String s = "abcegdfjsdsa";
        List<String> stringList = Arrays.asList(s.split(""));
        priorityQueue = new PriorityQueue(stringList);
        // 可以接收相同的元素
        re(priorityQueue); // a a b c d d e f g j s s
        // 反转优先级
        priorityQueue = new PriorityQueue(stringList.size(),Collections.reverseOrder());
        priorityQueue.addAll(stringList);
        re(priorityQueue); // s s j g f e d d c b a a
        // 去重
        Set<Character> set = new HashSet<>();
        for (char c : s.toCharArray()) {
            set.add(c);
        }
        priorityQueue = new PriorityQueue(set);
        re(priorityQueue); // a b c d e f g j s
    }

11.12 Collection和Iterator

Collection是所有序列容器的根接口,使用Collection可以创建更通用的代码;Iterator(迭代器)也可以实现相同的功能;在实现Collection接口时,必须实现iterator()方法,因此,在某些情况下,使用迭代器效果会更好一些。

另:Collection接口继承了Iterable接口,实现了Iterable接口的类都可以使用Foreach.

public class InterfaceVSIterator {

    /**
     * 展示容器中的所有内容
     * @param c 集合
     */
    public static void disapply(Collection<String> c){
        for (String s : c) {
            System.out.print(s+" ");
        }
        System.out.println();
    }

    /**
     * 展示容器中的所有内容
     * @param it 迭代器
     */
    public static void diapply(Iterator<String> it){
        while (it.hasNext()){
            System.out.print(it.next() +" ");
        }
        System.out.println();
    }
    
     public static void main(String[] args) {
        List<String> list = Arrays.asList("a","b","c");
        InterfaceVSIterator.disapply(list);
        InterfaceVSIterator.diapply(list.iterator());
    }
    /*
    a b c 
	a b c 
	*/
}

// 继承Collection接口
public class CollectionSequence extends AbstractCollection<String> {
    private String[] strings = new String[]{"a","b","c"};
    @Override
    public Iterator<String> iterator() {
        Iterator<String> it = new Iterator<String>() {
            // 定义计数器
            private int index = 0;
            @Override
            public boolean hasNext() {
                return index < strings.length;
            }

            @Override
            public String next() {
                // 返回当前计数的元素,并将计数器+1
                return strings[index++];
            }
        };
        return it;
    }

    @Override
    public int size() {
        return strings.length;
    }
    
     public static void main(String[] args) {
       CollectionSequence c = new CollectionSequence();
       InterfaceVSIterator.disapply(c);
       InterfaceVSIterator.diapply(c.iterator());
       /*
        a b c
        a b c
        */
    }
}

// 实现迭代器方法
public class StringSequence {
    protected String[] strings = new String[]{"a","b","c"};
}

class NoCollectionSequence extends StringSequence{

    public Iterator<String> iterator(){
        return new Iterator<String>() {
            private int index =0;
            @Override
            public boolean hasNext() {
                return index < strings.length;
            }

            @Override
            public String next() {
                return strings[index++];
            }
        };
    }

    public static void main(String[] args) {
        NoCollectionSequence n = new NoCollectionSequence();
        InterfaceVSIterator.diapply(n.iterator());
        /*
        a b c 
         */
    }
}
/*
可以看到继承Collection接口必须实现iterator()方法,还有其他一些方法,而迭代器方法,只需添加一个迭代器方法就可以。
*/

11.13 Foreach与迭代器

foreach语法可以用于数组和所有实现了Iterable接口的类(包含所有的collection)。

// 所有的collection
 public static void main(String[] args) {
       Collection c = Arrays.asList("hello world".split(""));
        for (Object o : c) {
            System.out.print(o+" ");
        }
        System.out.println();
    }
// 实现了Iterable接口
public class IterableClass implements Iterable {
    private  String[] strings ;
    public IterableClass(String ...s){
        strings = s;
    }
    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index =0;
            @Override
            public boolean hasNext() {
                return index < strings.length;
            }

            @Override
            public String next() {
                return strings[index++];
            }
        };
    }
    public static void main(String[] args) {
       IterableClass iterableClass = new IterableClass("a","b","c");
        for (Object aClass : iterableClass) {
            System.out.println(aClass);
        }
    }
}

 public static void main(String[] args) {
        // 获得当前系统的所有环境变量
        Map<String, String> getenv = System.getenv();
        for (Map.Entry entry:getenv.entrySet()){
            System.out.println(entry);
        }
    }

// 虽然数组和实现了Iterable接口的类都可以使用foreach,但数组并不能被当成Iterable
 public static void main(String[] args) {
        String[] strings = {"a","b","c"};
        // 下面写法会报错
        //test(strings);
    }
    
    public static void test(Iterable it){
        for (Object o : it) {
            System.out.println(o);
        }
    }
11.13.1 适配器惯用法

实现向前遍历

public class RevArrayList extends ArrayList {

    public Iterable revIterable(){
        return new Iterable() {
            @Override
            public Iterator iterator() {
                return new Iterator() {
                    // 从最后一位开始
                    private int index =size()-1;
                    @Override
                    public boolean hasNext() {
                        //return index > -1;
                        return index >= 0;
                    }

                    @Override
                    public Object next() {
                        return get(index--);
                    }
                };
            }
        };
    }
}

  public static void main(String[] args) {
        RevArrayList revArrayList = new RevArrayList();
        revArrayList.addAll(Arrays.asList("a","b","c"));
        // 默认使用正循环
        for (Object o : revArrayList) {
            System.out.print(o +" ");
        }
        System.out.println();
        for (Object o:revArrayList.revIterable()){
            System.out.print(o+" ");
        }
        System.out.println();
    }
    /*
    a b c
    c b a
     */

向前遍历/向后遍历/随机遍历

public class MultiIterableClass implements Iterable {
    String[] strings;

    public MultiIterableClass(String... s) {
        strings = s;
    }
    // 向后
    @Override
    public Iterator iterator() {
        return new Iterator() {
            private int index = 0;

            @Override
            public boolean hasNext() {
                return index < strings.length;
            }

            @Override
            public Object next() {
                return strings[index++];
            }
        };
    }

    // 向前
    public Iterable rev() {
        return new Iterable() {
            @Override
            public Iterator iterator() {
                return new Iterator() {
                    private int index = strings.length - 1;

                    @Override
                    public boolean hasNext() {
                        return index >= 0;
                    }

                    @Override
                    public Object next() {
                        return strings[index--];
                    }
                };
            }
        };
    }

    // 随机
    public Iterable ran(){
        return new Iterable() {
            @Override
            public Iterator iterator() {
                List<String> list = Arrays.asList(strings);
                Collections.shuffle(list);
                return list.iterator();
            }
        };
    }
}

  public static void main(String[] args) {
        MultiIterableClass m = new MultiIterableClass("a","b","c","d");
        for (Object o : m) {
            System.out.print(o);
        }
        System.out.println();
        for (Object o:m.rev()){
            System.out.print(o);
        }
        System.out.println();
        for (Object o:m.ran()){
            System.out.print(o);
        }
    }
/*
abcd
dcba
bdac
*/

集合的引用问题

  public static void main(String[] args) {
        String[] strings = {"a","b","c","d"};
        List<String> list1 = new ArrayList<>(Arrays.asList(strings));
        System.out.println("随机前:"+list1);
        Collections.shuffle(list1);
        System.out.println("随机后:"+list1);

        System.out.println("数组:"+Arrays.toString(strings));
        List<String> list2 = Arrays.asList(strings);
        System.out.println("随机前:"+list2);
        Collections.shuffle(list2);
        System.out.println("随机后:"+list2);
        System.out.println("数组:"+Arrays.asList(strings));
    }
/*
随机前:[a, b, c, d]
随机后:[c, b, a, d]
数组:[a, b, c, d]
随机前:[a, b, c, d]
随机后:[d, a, b, c]
数组:[d, a, b, c]
*/
// 总结:可以看到,当新建一个集合时,打乱的只是对数组的引用,并不会改变数组的位置,当直接引用数组时,此时集合的改变,会改变数组的位置。

11.14 总结

  • 数组将索引与对象联系起来,保存类型明确的对象,可以是多维的,数组一旦生成,容量就不能改变;
  • Collection保存单一元素,Map保存相关联的键值对。可以自动调整大小,不能持有基本类型,但是可以自动包装;
  • List将索引与对象关联,是排好序的容器;
  • 大量随机访问使用ArrayList,经常从表中间插入或删除,使用LinkedList;
  • 各种Queue和栈的行为,由LinkedList提供支持;
  • Map是将对象与对象相关联的设计。HashMap快速访问,TreeMap保持键处于排序状态,LinkedHashMap保持元素插入时的位置,同时通过散列提供快速访问能力。
  • Set不接受重复元素。HashSet最快的查询速度,TreeSet保持元素处于排序,LinkedHashSet以插入顺序保存元素。
  • 不要使用过时的Vector、Hashtable、Stack。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值