目录

一、Collections接口是做什么的?

用官网文档的介绍:

The polymorphic algorithms described here are pieces of reusable functionality provided by the Java platform. All of them come from the Collections class, and all take the form of static methods whose first argument is the collection on which the operation is to be performed. The great majority of the algorithms provided by the Java platform operate on List instances, but a few of them operate on arbitrary Collection instances.(这里描述的多态算法是Java平台提供的可重用功能的一部分。它们都来自Collections类,都采用静态方法的形式,其第一个参数是要执行操作的集合。Java平台提供的绝大多数算法都对列表实例进行操作,但也有少数算法对任意集合实例进行操作。)

从介绍来看,这其实是一个工具类,实现了一些常用的算法,方便我们操作集合,如果没有这个类,也是可以的,就是自己写比较麻烦。但是呢,有了这个类,平时写代码我们可以直接调用(前提是了解里面怎么实现的,实现了什么功能),毕竟不是所有的轮子都要重复造。但是,不是有轮子了,我们就可以不去深究里面到底是啥,重要的不是造轮子,而是,我们必须有能够造轮子的能力

二、Collections源码之大类方法

1.提供不可变集合

一般是通过一个方法直接获取不可变的集合,里面包含了不可变的CollectionSetSortedSetNavigableSetListSortedMapNavigableMap

    // 获取不可变的Collection
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
return new UnmodifiableCollection<>(c);
}
// 获取不可变的Set
public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
return new UnmodifiableSet<>(s);
}
// 获取不可变的SortedSet
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<T> s) {
return new UnmodifiableSortedSet<>(s);
}
// 获取不可变的NavigableSet
public static <T> NavigableSet<T> unmodifiableNavigableSet(NavigableSet<T> s) {
return new UnmodifiableNavigableSet<>(s);
}
// 获取不可变的List
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
// 获取不可变的SortedMap
public static <K,V> SortedMap<K,V> unmodifiableSortedMap(SortedMap<K, ? extends V> m) {
return new UnmodifiableSortedMap<>(m);
}
// 获取不可变的NavigableMap
public static <K,V> NavigableMap<K,V> unmodifiableNavigableMap(NavigableMap<K, ? extends V> m) {
return new UnmodifiableNavigableMap<>(m);
}

但是这些不可变的集合到底是什么呢?怎么使用呢?

我们来看一个最普遍的UnmodifiableCollection类的源码:

    // 实现了Collection和序列化接口
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
// 定义序列化的uid
private static final long serialVersionUID = 1820017752578914078L;
// 定义数据
final Collection<? extends E> c; UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
// 获取大小
public int size() {return c.size();}
// 是否为空
public boolean isEmpty() {return c.isEmpty();}
// 是否包含
public boolean contains(Object o) {return c.contains(o);}
// 转成数组
public Object[] toArray() {return c.toArray();}
// 转成特定类型数组
public <T> T[] toArray(T[] a) {return c.toArray(a);}
// toString方法
public String toString() {return c.toString();}
// 获取迭代器
public Iterator<E> iterator() {
// 用内部类方式实现
return new Iterator<E>() {
// 迭代器其实是c的迭代器
private final Iterator<? extends E> i = c.iterator();
// 是否有下一个元素
public boolean hasNext() {return i.hasNext();}
// 获取下一个
public E next() {return i.next();}
// 移除元素
public void remove() {
throw new UnsupportedOperationException();
}
// 遍历剩下的元素
@Override
public void forEachRemaining(Consumer<? super E> action) {
// Use backing collection version
i.forEachRemaining(action);
}
};
}
// 增加操作抛异常
public boolean add(E e) {
throw new UnsupportedOperationException();
}
//删除操作抛异常
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
// 是否包含所有
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
// 批量添加操作抛异常
public boolean addAll(Collection<? extends E> coll) {
throw new UnsupportedOperationException();
}
// 批量删除抛异常
public boolean removeAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
// 取交集抛异常
public boolean retainAll(Collection<?> coll) {
throw new UnsupportedOperationException();
}
// 清空数据抛异常
public void clear() {
throw new UnsupportedOperationException();
} // Override default methods in Collection
@Override
//遍历元素
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
// 按照条件移除元素抛异常
@Override
public boolean removeIf(Predicate<? super E> filter) {
throw new UnsupportedOperationException();
}
// 获取可分割迭代器
@SuppressWarnings("unchecked")
@Override
public Spliterator<E> spliterator() {
return (Spliterator<E>)c.spliterator();
}
// 获取数据流
@SuppressWarnings("unchecked")
@Override
public Stream<E> stream() {
return (Stream<E>)c.stream();
}
// 获取并行流
@SuppressWarnings("unchecked")
@Override
public Stream<E> parallelStream() {
return (Stream<E>)c.parallelStream();
}
}

从上面的代码可以看出其实所谓不可变的集合,就是用一个包装类,持有对实际的集合的引用,只能执行查询操作,其他操作都会抛出异常UnsupportedOperationException。我们来看其他的,随便挑一个UnmodifiableMap:


// 实现map接口和序列化接口
private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
// 序列号
private static final long serialVersionUID = -1034234728574286014L;
// 持有的引用
private final Map<? extends K, ? extends V> m;
// 初始化函数
UnmodifiableMap(Map<? extends K, ? extends V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}
// 查询大小
public int size() {return m.size();}
// 是否为空
public boolean isEmpty() {return m.isEmpty();}
// 是否包含键
public boolean containsKey(Object key) {return m.containsKey(key);}
// 是否包含值
public boolean containsValue(Object val) {return m.containsValue(val);}
// 通过值获取
public V get(Object key) {return m.get(key);}
// 添加(抛异常)
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
// 删除元素(抛异常)
public V remove(Object key) {
throw new UnsupportedOperationException();
}
// 批量增加(抛异常)
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
// 清空元素(抛异常)
public void clear() {
throw new UnsupportedOperationException();
}
// set的集合
private transient Set<K> keySet;
// ebtry的集合
private transient Set<Map.Entry<K,V>> entrySet;
// value的集合
private transient Collection<V> values;
// 获取key的集合
public Set<K> keySet() {
if (keySet==null)
// 如果不为空,把keyset也变成一个不可变的集合之后再返回
keySet = unmodifiableSet(m.keySet());
return keySet;
} public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)
// 如果不为空,把entryset也变成一个不可变的集合之后再返回
entrySet = new UnmodifiableEntrySet<>(m.entrySet());
return entrySet;
} public Collection<V> values() {
if (values==null)
// // 如果不为空,把value也变成一个不可变的集合之后再返回
values = unmodifiableCollection(m.values());
return values;
}
// 对象的方法判断是否相等,引用相等也是判断为相等
public boolean equals(Object o) {return o == this || m.equals(o);}
// 计算hash
public int hashCode() {return m.hashCode();}
// toString法法
public String toString() {return m.toString();} // 重写方法,通过key获取vaule,没有则返回默认值
@Override
@SuppressWarnings("unchecked")
public V getOrDefault(Object k, V defaultValue) {
// Safe cast as we don't change the value
return ((Map<K, V>)m).getOrDefault(k, defaultValue);
} // 遍历执行action(动作参数化)
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
m.forEach(action);
} // 替换所有,抛异常
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
// 如果不存在则放进去,抛异常
@Override
public V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
} //删除操作,抛异常
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
//替换操作,抛异常
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException();
}
// 替换操作抛异常
@Override
public V replace(K key, V value) {
throw new UnsupportedOperationException();
}
// 抛异常
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
throw new UnsupportedOperationException();
}
// 抛异常
@Override
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
// 抛异常
@Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
// 抛异常
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
} /**
* We need this class in addition to UnmodifiableSet as
* Map.Entries themselves permit modification of the backing Map
* via their setValue operation. This class is subtle: there are
* many possible attacks that must be thwarted.
*
* @serial include
*/
// 将map内部的Entry也用一个不可变的类存起来(实际上也是引用)
static class UnmodifiableEntrySet<K,V>
extends UnmodifiableSet<Map.Entry<K,V>> {
private static final long serialVersionUID = 7854390611657943733L; @SuppressWarnings({"unchecked", "rawtypes"})
UnmodifiableEntrySet(Set<? extends Map.Entry<? extends K, ? extends V>> s) {
// Need to cast to raw in order to work around a limitation in the type system
super((Set)s);
}
// 处理entry的元素
static <K, V> Consumer<Map.Entry<K, V>> entryConsumer(Consumer<? super Entry<K, V>> action) {
return e -> action.accept(new UnmodifiableEntry<>(e));
}
// 遍历
public void forEach(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
c.forEach(entryConsumer(action));
}
// 不可变entryset的可分割迭代器(下面方法和Spliterator的差不多,没有什么好说)
static final class UnmodifiableEntrySetSpliterator<K, V>
implements Spliterator<Entry<K,V>> {
final Spliterator<Map.Entry<K, V>> s; UnmodifiableEntrySetSpliterator(Spliterator<Entry<K, V>> s) {
this.s = s;
} @Override
public boolean tryAdvance(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
return s.tryAdvance(entryConsumer(action));
} @Override
public void forEachRemaining(Consumer<? super Entry<K, V>> action) {
Objects.requireNonNull(action);
s.forEachRemaining(entryConsumer(action));
} @Override
public Spliterator<Entry<K, V>> trySplit() {
Spliterator<Entry<K, V>> split = s.trySplit();
return split == null
? null
: new UnmodifiableEntrySetSpliterator<>(split);
} @Override
public long estimateSize() {
return s.estimateSize();
} @Override
public long getExactSizeIfKnown() {
return s.getExactSizeIfKnown();
} @Override
public int characteristics() {
return s.characteristics();
} @Override
public boolean hasCharacteristics(int characteristics) {
return s.hasCharacteristics(characteristics);
} @Override
public Comparator<? super Entry<K, V>> getComparator() {
return s.getComparator();
}
} @SuppressWarnings("unchecked")
public Spliterator<Entry<K,V>> spliterator() {
return new UnmodifiableEntrySetSpliterator<>(
(Spliterator<Map.Entry<K, V>>) c.spliterator());
} @Override
public Stream<Entry<K,V>> stream() {
return StreamSupport.stream(spliterator(), false);
} @Override
public Stream<Entry<K,V>> parallelStream() {
return StreamSupport.stream(spliterator(), true);
} public Iterator<Map.Entry<K,V>> iterator() {
return new Iterator<Map.Entry<K,V>>() {
private final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = c.iterator(); public boolean hasNext() {
return i.hasNext();
}
public Map.Entry<K,V> next() {
return new UnmodifiableEntry<>(i.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
};
} @SuppressWarnings("unchecked")
public Object[] toArray() {
Object[] a = c.toArray();
for (int i=0; i<a.length; i++)
a[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)a[i]);
return a;
} @SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// We don't pass a to c.toArray, to avoid window of
// vulnerability wherein an unscrupulous multithreaded client
// could get his hands on raw (unwrapped) Entries from c.
Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0)); for (int i=0; i<arr.length; i++)
arr[i] = new UnmodifiableEntry<>((Map.Entry<? extends K, ? extends V>)arr[i]); if (arr.length > a.length)
return (T[])arr; System.arraycopy(arr, 0, a, 0, arr.length);
if (a.length > arr.length)
a[arr.length] = null;
return a;
} /**
* This method is overridden to protect the backing set against
* an object with a nefarious equals function that senses
* that the equality-candidate is Map.Entry and calls its
* setValue method.
*/
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
return c.contains(
new UnmodifiableEntry<>((Map.Entry<?,?>) o));
} /**
* The next two methods are overridden to protect against
* an unscrupulous List whose contains(Object o) method senses
* when o is a Map.Entry, and calls o.setValue.
*/
public boolean containsAll(Collection<?> coll) {
for (Object e : coll) {
if (!contains(e)) // Invokes safe contains() above
return false;
}
return true;
}
public boolean equals(Object o) {
if (o == this)
return true; if (!(o instanceof Set))
return false;
Set<?> s = (Set<?>) o;
if (s.size() != c.size())
return false;
return containsAll(s); // Invokes safe containsAll() above
} /**
* This "wrapper class" serves two purposes: it prevents
* the client from modifying the backing Map, by short-circuiting
* the setValue method, and it protects the backing Map against
* an ill-behaved Map.Entry that attempts to modify another
* Map Entry when asked to perform an equality check.
*/
private static class UnmodifiableEntry<K,V> implements Map.Entry<K,V> {
private Map.Entry<? extends K, ? extends V> e; UnmodifiableEntry(Map.Entry<? extends K, ? extends V> e)
{this.e = Objects.requireNonNull(e);} public K getKey() {return e.getKey();}
public V getValue() {return e.getValue();}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public int hashCode() {return e.hashCode();}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> t = (Map.Entry<?,?>)o;
return eq(e.getKey(), t.getKey()) &&
eq(e.getValue(), t.getValue());
}
public String toString() {return e.toString();}
}
}
}

UnmodifiableMap稍微复杂一点,就是里面分成了entry<key,value>,key,value三个维度,UnmodifiableEntrySet<K,V>继承了UnmodifiableEntry<K,V>UnmodifiableEntry<K,V>实现了Map.Entry<K,V>,事实上也是持有对集合的引用,把修改操作全部禁掉,一旦调用就会抛出异常。

上面的不可变集合提供方法直接获取,如下图:

2、提供同步的集合

看下面的图片,我们可以看到这个工具类其实提供了很多同步的集合类,但是都是基于Synchronize来实现的,开销相对比较大,有点点暴力了。源头就是SynchronizedCollection,实现了Collection接口。

我们来看源码,明显可以发现,里面处理集合的引用外,还有一个对象,mutex,这就是锁的关键了,也就是同步的对象,可以看到下面几乎所有的方法都被加上了Synchronize同步,这样子确实保持了线程安全,但是这样有点影响效率,如果不那么考虑效率的话,也可以使用这个。

    static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L; final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
} SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
} public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized (mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}
public <T> T[] toArray(T[] a) {
synchronized (mutex) {return c.toArray(a);}
} public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
} public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
} public boolean containsAll(Collection<?> coll) {
synchronized (mutex) {return c.containsAll(coll);}
}
public boolean addAll(Collection<? extends E> coll) {
synchronized (mutex) {return c.addAll(coll);}
}
public boolean removeAll(Collection<?> coll) {
synchronized (mutex) {return c.removeAll(coll);}
}
public boolean retainAll(Collection<?> coll) {
synchronized (mutex) {return c.retainAll(coll);}
}
public void clear() {
synchronized (mutex) {c.clear();}
}
public String toString() {
synchronized (mutex) {return c.toString();}
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {c.forEach(consumer);}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) {return c.removeIf(filter);}
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
@Override
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!
}
@Override
public Stream<E> parallelStream() {
return c.parallelStream(); // Must be manually synched by user!
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
}

值得注意的是,还有几个方法没有被加上锁,那就是所有获取流和迭代器的方法都没有,iterator(),spliterator,stream,parallelStream,都有一句话注释:Must be manually synched by user!,就是叫我们长点心,如果迭代或者流计算设计涉及到并发操作,可能会有问题,要使用者自己考虑。

至此,以上的同步内部类都有方法与之对应,也就是传进来集合就行了,会给我们返回一个同步类,可以直接获取,这也太方便了吧没听过鲁迅说便宜的东西往往最贵么(鲁迅说没有说过),也就是效率自己权衡好就行啦~

3、类型检查

这个包装类还提供了很多关于类型检查的方法:



我们来看看是干啥的,挑一个checkedCollection()看看:

    public static <E> Collection<E> checkedCollection(Collection<E> c,
Class<E> type) {
return new CheckedCollection<>(c, type);
}

首先这个方法是返回了一个CheckedCollection,我猜这个类也是封装了Collection,跟进去源码看看:

    static class CheckedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 1578914078182001775L; final Collection<E> c;//持有引用
final Class<E> type;//持有传进来的类型 // 检查类型的函数
@SuppressWarnings("unchecked")
E typeCheck(Object o) {
if (o != null && !type.isInstance(o))
throw new ClassCastException(badElementMsg(o));
return (E) o;
}
//类型不对则打印错误信息
private String badElementMsg(Object o) {
return "Attempt to insert " + o.getClass() +
" element into collection with element type " + type;
} CheckedCollection(Collection<E> c, Class<E> type) {
this.c = Objects.requireNonNull(c, "c");
this.type = Objects.requireNonNull(type, "type");
} public int size() { return c.size(); }
public boolean isEmpty() { return c.isEmpty(); }
public boolean contains(Object o) { return c.contains(o); }
public Object[] toArray() { return c.toArray(); }
public <T> T[] toArray(T[] a) { return c.toArray(a); }
public String toString() { return c.toString(); }
public boolean remove(Object o) { return c.remove(o); }
public void clear() { c.clear(); } public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
public boolean removeAll(Collection<?> coll) {
return c.removeAll(coll);
}
public boolean retainAll(Collection<?> coll) {
return c.retainAll(coll);
} public Iterator<E> iterator() {
// JDK-6363904 - unwrapped iterator could be typecast to
// ListIterator with unsafe set()
final Iterator<E> it = c.iterator();
return new Iterator<E>() {
public boolean hasNext() { return it.hasNext(); }
public E next() { return it.next(); }
public void remove() { it.remove(); }};
}
// 添加的时候执行类型检查
public boolean add(E e) { return c.add(typeCheck(e)); } private E[] zeroLengthElementArray; // Lazily initialized private E[] zeroLengthElementArray() {
return zeroLengthElementArray != null ? zeroLengthElementArray :
(zeroLengthElementArray = zeroLengthArray(type));
}
// copy的时候检查每一个元素
@SuppressWarnings("unchecked")
Collection<E> checkedCopyOf(Collection<? extends E> coll) {
Object[] a;
try {
E[] z = zeroLengthElementArray();
a = coll.toArray(z);
// Defend against coll violating the toArray contract
if (a.getClass() != z.getClass())
a = Arrays.copyOf(a, a.length, z.getClass());
} catch (ArrayStoreException ignore) {
// To get better and consistent diagnostics,
// we call typeCheck explicitly on each element.
// We call clone() to defend against coll retaining a
// reference to the returned array and storing a bad
// element into it after it has been type checked.
a = coll.toArray().clone();
for (Object o : a)
typeCheck(o);
}
// A slight abuse of the type system, but safe here.
return (Collection<E>) Arrays.asList(a);
}
// 批量添加的时候执行拷贝检查机制
public boolean addAll(Collection<? extends E> coll) {
// Doing things this way insulates us from concurrent changes
// in the contents of coll and provides all-or-nothing
// semantics (which we wouldn't get if we type-checked each
// element as we added it)
return c.addAll(checkedCopyOf(coll));
} // Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {c.forEach(action);}
@Override
public boolean removeIf(Predicate<? super E> filter) {
return c.removeIf(filter);
}
@Override
public Spliterator<E> spliterator() {return c.spliterator();}
@Override
public Stream<E> stream() {return c.stream();}
@Override
public Stream<E> parallelStream() {return c.parallelStream();}
}

上面的源码可以看出,这些封装类没有什么特殊的地方,只是包装了一层,执行添加的时候,检查类型,如果不符合则抛出异常。

4.提供空集合或者迭代器

这个类还提供了一些方法可以获取到空集合,会生成指定类型的空 List, Set, Map,而且是不可变的,如进行 add() 操作会报 java.lang.UnsupportedOperationException



我们来看EmptyIterator的源码,里面有可以final的对象EMPTY_ITERATOR,里面的hashNext()方法直接返回false,remove(),next()方法直接不合法。

    private static class EmptyIterator<E> implements Iterator<E> {
static final EmptyIterator<Object> EMPTY_ITERATOR
= new EmptyIterator<>(); public boolean hasNext() { return false; }
public E next() { throw new NoSuchElementException(); }
public void remove() { throw new IllegalStateException(); }
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
}

为啥要做这样的集合呢?看了一下其他的空集合,都是空的。我想了很久

最新文章

  1. jquery文件上传
  2. MongoDB基本管理命令
  3. 两天三场Java实习生面试总结
  4. SQL Server 通配符 Wildcard character
  5. mysql的数据导入导出
  6. Codeforces Round #343 (Div. 2) D - Babaei and Birthday Cake 线段树+DP
  7. udp协议基础(转自疯狂java讲义)
  8. Sublime Text 3配置记录
  9. mysql--help不可用
  10. BZOJ1579 USACO 2009 Feb Gold 3.Revamping Trails Solution
  11. [jQuery]on和bind事件绑定的区别
  12. js正则表达式验证
  13. js 数组里求最大值和最小值
  14. BZOJ_3011_[Usaco2012 Dec]Running Away From the Barn _可并堆
  15. 理解linux下的load
  16. kafka中生产者和消费者API
  17. 【SSH网上商城项目实战17】购物车基本功能的实现
  18. MySQL 存储引擎、锁、调优、失误与事务回滚、与python交互、orm
  19. combotree 满足条件的节点不可选中
  20. Discuz!X2截屏控件手动安装教程-Xproer.ScreenCapture

热门文章

  1. ServletUtils取值(限foton)
  2. this.$nextTick的用法
  3. 《Web接口开发与自动化测试》学习笔记(三)
  4. 【SpringCloud】06.Eureka 总结
  5. c#反转
  6. System.Net邮件发送功能踩过的坑
  7. eclipse关于新建工程找不到二进制文件的解决方法
  8. wcf调用时时间参数问题,返回值中有日期格式得值得问题
  9. Chrome默认启动尺寸的小问题
  10. Docker - 解决容器内获取的时间和主机的时间不一样的问题