Java编程笔记16:深入容器

Java编程笔记16:深入容器

5c9c3b3b392ac581.jpg

图源:PHP中文网

填充容器

填充容器会有Java编程笔记15:数组 - 魔芋红茶’s blog (icexmoon.cn)种提到的填充数组同样的问题。

和数组类似,标准库提供一个方法Collections.fill用于向容器中填充元素:

package ch16.fill;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
   
    public static void main(String[] args) {
   
        List<Integer> nums = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
   
            nums.add(i);
        }
        System.out.println(nums);
        Collections.fill(nums, Integer.valueOf(99));
        System.out.println(nums);
    }
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
// [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]

但这个方法存在局限性:

  1. 只能向List类型的容器填充数据。
  2. 所谓的填充是用指定元素覆盖已有元素,而非添加若干个指定元素。

同样的,这种方式填充的元素同样是同一个对象的引用:

package ch16.fill2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import util.Fmt;

class AddrStr {
   
    private String str;

    public AddrStr(String str) {
   
        this.str = str;
    }

    @Override
    public String toString() {
   
        String addr = super.toString();
        return Fmt.sprintf("AddrStr(%s:%s)", addr, str);
    }
}

public class Main {
   
    public static void main(String[] args) {
   
        List<AddrStr> strings = new ArrayList<>();
        String[] words = "abc".split("");
        for (String w : words) {
   
            strings.add(new AddrStr(w));
        }
        System.out.println(strings);
        Collections.fill(strings, new AddrStr("z"));
        System.out.println(strings);
    }
}
// [AddrStr(ch16.fill2.AddrStr@24d46ca6:a),
// AddrStr(ch16.fill2.AddrStr@6d311334:b),
// AddrStr(ch16.fill2.AddrStr@682a0b20:c)]
// [AddrStr(ch16.fill2.AddrStr@3d075dc0:z),
// AddrStr(ch16.fill2.AddrStr@3d075dc0:z),
// AddrStr(ch16.fill2.AddrStr@3d075dc0:z)]

使用Generator

很容易地,就会想到像在Java编程笔记15:数组 - 魔芋红茶’s blog (icexmoon.cn)中做过的那样,利用Generator接口实现将测试数据填充进容器:

package ch16.generator;

import java.util.ArrayList;
import java.util.List;

import ch15.test2.CommonGenerator;
import ch15.test2.Generator;

public class ListFiller {
   
    public static <T> List<T> fill(List<T> list, Generator<T> gen, int num) {
   
        list.addAll(getList(gen, num));
        return list;
    }

    public static <T> List<T> getList(Generator<T> gen, int num) {
   
        List<T> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
   
            list.add(gen.next());
        }
        return list;
    }

    public static void main(String[] args) {
   
        List<Integer> nums = new ArrayList<>();
        fill(nums, new CommonGenerator.IntGenerator(), 5);
        System.out.println(nums);
        List<Character> chars = new ArrayList<>(getList(new CommonGenerator.CharGenerator(), 5));
        System.out.println(chars);
        List<String> strings = new ArrayList<>();
        strings.addAll(getList(new CommonGenerator.StringGenerator(), 5));
        System.out.println(strings);
    }
}
// [0, 1, 2, 3, 4]
// [a, b, c, d, e]
// [abcde, fghij, klmno, pqrst, uvwxy]

fill方法可以直接用指定的Generator对象填充List,而geList方法可以获取一个用来填充ListList,相对而言后者更灵活一些,可以用于在容器的构造器或者addAll方法中,main中的示例代码说明了这一点。

同样的,可以创建一个用来填充Map的工具类,只不过稍有些不同:

package ch16.generator;

import java.util.LinkedHashMap;
import java.util.Map;

import ch15.test2.CommonGenerator;
import ch15.test2.Generator;

public class MapFiller {
   
    public static class Entry<K, V> {
   
        public final K key;
        public final V value;

        public Entry(K key, V value) {
   
            this.key = key;
            this.value = value;
        }
    }

    public static <K, V> Map<K, V> fill(Map<K, V> map, Generator<Entry<K, V>> gen, int num) {
   
        for (int i = 0; i < num; i++) {
   
            Entry<K, V> entry = gen.next();
            map.put(entry.key, entry.value);
        }
        return map;
    }

    public static <K, V> Map<K, V> fill(Map<K, V> map, Generator<K> keyGen, Generator<V> valueGen, int num) {
   
        for (int i = 0; i < num; i++) {
   
            map.put(keyGen.next(), valueGen.next());
        }
        return map;
    }

    public static <K, V> Map<K, V> getMap(Generator<Entry<K, V>> gen, int num) {
   
        Map<K, V> map = new LinkedHashMap<>();
        fill(map, gen, num);
        return map;
    }

    public static <K, V> Map<K, V> getMap(Generator<K> kGen, Generator<V> vGen, int num) {
   
        Map<K, V> map = new LinkedHashMap<>();
        fill(map, kGen, vGen, num);
        return map;
    }

    public static void main(String[] args) {
   
        Map<Integer, Character> map = getMap(new CommonGenerator.IntGenerator(), new CommonGenerator.CharGenerator(),
                5);
        System.out.println(map);
    }
}
// {0=a, 1=b, 2=c, 3=d, 4=e}

静态内部类Entry是为了方便某些以键值对方式产生元素的Generator使用的,可以用一个单独的例子说明:

package ch16.generator;

import java.util.Map;

import ch15.test2.CommonGenerator;
import ch15.test2.Generator;
import ch16.generator.MapFiller.Entry;

class IntCharGenerator implements Generator<MapFiller.Entry<Integer, Character>> {
   
    Generator<Integer> kGen = new CommonGenerator.IntGenerator();
    Generator<Character> vGen = new CommonGenerator.CharGenerator();

    @Override
    public Entry<Integer, Character> next() {
   
        Integer key = kGen.next();
        Character value = vGen.next();
        Entry<Integer, Character> entry = new Entry<>(key, value);
        return entry;
    }

}

public class Main {
   
    public static void main(String[] args) {
   
        Map<Integer, Character> map = MapFiller.getMap(new IntCharGenerator(), 5);
        System.out.println(map);
    }
}
// {0=a, 1=b, 2=c, 3=d, 4=e}

这里的IntCharGenerator只是简单用于举例,事实上可以通过策略模式来编写一个更通用的KeyValueGenerator<K,V>类作为产生Entry元素的生成器,可以分别将产生键和值的生成器作为策略进行绑定。

这里使用的Generator以及CommonGenerator等组件都是在Java编程笔记15:数组 - 魔芋红茶’s blog (icexmoon.cn)中定义的,没印象的可以翻翻之前的笔记。

Abs类

标准库中的容器组件包含一些抽象类,比如AbstractMap等,可以通过继承这些类来实现自定义容器。

当然一般来说我们不会有类似的需求,标准库已经提供大量丰富可靠的容器,但就产生测试数据这个问题来说,完全可以利用容器的抽象类,来产生一些专门用于测试数据填充和展示的容器:

package ch16.abs;

import java.util.AbstractList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch15.test2.CommonGenerator;
import ch15.test2.Generator;

class SampleList<E> extends AbstractList<E> {
   
    private final int size;
    private Generator<E> gen;
    private Map<Integer, E> items = new HashMap<>();

    public SampleList(Generator<E> gen) {
   
        this(gen, 10);
    }

    public SampleList(Generator<E> gen, int size) {
   
        this.gen = gen;
        if (size < 0) {
   
            size = 0;
        }
        this.size = size;
    }

    @Override
    public E get(int index) {
   
        if (index >= size) {
   
            throw new IndexOutOfBoundsException();
        }
        if (!items.containsKey(index)) {
   
            E item = gen.next();
            items.put(index, item);
        }
        return items.get(index);
    }

    @Override
    public int size() {
   
        return size;
    }

}

public class Main {
   
    public static void main(String[] args) {
   
        List<Integer> sl = new SampleList<>(new CommonGenerator.IntGenerator());
        System.out.println(sl);
    }
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

类似的,可以编写适用于Map的版本:

package ch16.abs2;

import java.util.AbstractMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import ch15.test2.CommonGenerator;
import ch15.test2.Generator;

class SampleMap<K, V> extends AbstractMap<K, V> {
   
    private int size;
    private Generator<K> kGen;
    private Generator<V> vGen;
    private Set<Entry<K, V>> entries = new HashSet<>();

    public SampleMap(Generator<K> kGen, Generator<V> vGen, int size) {
   
        this.kGen = kGen;
        this.vGen = vGen;
        if (size < 0) {
   
            size = 0;
        }
        this.size = size;
        for (int i = 0; i < size; i++) {
   
            entries.add(newEntry());
        }
    }

    public SampleMap(Generator<K> kGen, Generator<V> vGen) {
   
        this(kGen, vGen, 10);
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
   
        return entries;
    }

    private Entry<K, V> newEntry() {
   
        return new Entry<K, V>() {
   

            @Override
            public K getKey() {
   
                return kGen.next();
            }

            @Override
            public V getValue() {
   
                return vGen.next();
            }

            @Override
            public V setValue(V value) {
   
                throw new UnsupportedOperationException();
            }

        };
    }

}

public class Main {
   
    public static void main(String[] args) {
   
        Map<Integer, Character> map = new SampleMap<Integer, Character>(new CommonGenerator.IntGenerator(),
                new CommonGenerator.CharGenerator());
        System.out.println(map);
    }
}
// {0=a, 1=b, 2=c, 3=d, 4=e, 5=f, 6=g, 7=h, 8=i, 9=j}

需要说明的是,上边的两个示例为了更通用,使用了泛型+Generator接口的实现方式,但实际上你可以通过继承容器的抽象类编写任意实现的容器,甚至是内部不包含任何存储单元,只用游标或者别的什么来临时产生元素:

package ch16.abs3;

import java.util.AbstractList;
import java.util.List;

class SampleList2 extends AbstractList<Integer> {
   
    private final int size;

    public SampleList2(int size) {
   
        if (size < 0) {
   
            size = 0;
        }
        this.size = size;
    }

    public SampleList2() {
   
        this(10);
    }

    @Override
    public Integer get(int index) {
   
        if (index >= size) {
   
            throw new IndexOutOfBoundsException();
        }
        return Integer.valueOf(index);
    }

    @Override
    public int size() {
   
        return size;
    }

}

public class Main {
   
    public static void main(String[] args) {
   
        List<Integer> list = new SampleList2();
        System.out.println(list);
    }
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

最后介绍一个稍微复杂的例子:

package ch16.abs4;

import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值