设计模式之美:Iterator(迭代器)
索引
意图
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
结构
参与者
Iterator
- 迭代器定义访问和遍历元素的接口。
ConcreteIterator
- 具体迭代器实现迭代器接口。
- 对该聚合遍历时跟踪当前位置。
Aggregate
- 聚合定义创建相应迭代器对象的接口。
ConcreteAggregate
- 具体聚合实现创建相应迭代器的接口,该操作返回 ConreteIterator 的实例。
适用性
在以下情况下可以使用 Iterator 模式:
- 访问一个聚合对象的内容而无需暴露它的内部表示。
- 支持对聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口。
效果
- 它支持以不同的方式遍历一个聚合。
- 迭代器简化了聚合的接口。
- 在同一个聚合上可以有多个遍历。
相关模式
- Iterator 常被应用到 Composite 这样的递归结构上。
- 可以使用 Factory Method 模式来实例化多态迭代器。
- Iterator 可以使用 Memento 来捕获一个迭代的状态,在内部存储 Memento。
实现
实现方式(一):Iterator 模式结构样式代码。
namespace IteratorPattern.Implementation1
{
public abstract class Iterator
{
public abstract object First();
public abstract object MoveNext();
public abstract object Current();
public abstract bool IsDone();
public abstract void Reset();
} public abstract class Aggregate
{
public abstract Iterator CreateIterator();
} public class ConcreteAggregate : Aggregate
{
private readonly ArrayList _items = new ArrayList(); public int Count
{
get { return _items.Count; }
} public object this[int index]
{
get { return _items[index]; }
set { _items.Insert(index, value); }
} public override Iterator CreateIterator()
{
return new ConcreteIterator(this);
}
} public class ConcreteIterator : Iterator
{
private readonly ConcreteAggregate _aggregate;
private int _currentIndex = ; public ConcreteIterator(ConcreteAggregate aggregate)
{
_aggregate = aggregate;
} public override object First()
{
if (_aggregate.Count > )
return _aggregate[];
else
return null;
} public override object MoveNext()
{
object item = null;
if (_currentIndex < _aggregate.Count - )
{
item = _aggregate[++_currentIndex];
} return item;
} public override object Current()
{
return _aggregate[_currentIndex];
} public override bool IsDone()
{
return _currentIndex >= _aggregate.Count;
} public override void Reset()
{
_currentIndex = ;
}
} public class Client
{
public void TestCase1()
{
var aggregate = new ConcreteAggregate();
aggregate[] = "Apple";
aggregate[] = "Orange";
aggregate[] = "Strawberry"; var iterator = new ConcreteIterator(aggregate); object item = iterator.First();
while (!iterator.IsDone())
{
Console.WriteLine(item);
item = iterator.MoveNext();
}
}
}
}
实现方式(二):实现 IEnumerable 中序遍历二叉树。
/// <summary>
/// 二叉树节点
/// </summary>
/// <typeparam name="T">The item type</typeparam>
public class BinaryTreeNode<T>
{
#region Constructors /// <summary>
/// 二叉树节点
/// </summary>
public BinaryTreeNode()
{
} /// <summary>
/// 二叉树节点
/// </summary>
/// <param name="value">节点中的值</param>
public BinaryTreeNode(T value)
{
this.Value = value;
} /// <summary>
/// 二叉树节点
/// </summary>
/// <param name="value">节点中的值</param>
/// <param name="parent">节点的父节点</param>
public BinaryTreeNode(T value, BinaryTreeNode<T> parent)
{
this.Value = value;
this.Parent = parent;
} /// <summary>
/// 二叉树节点
/// </summary>
/// <param name="value">节点中的值</param>
/// <param name="parent">节点的父节点</param>
/// <param name="left">节点的左节点</param>
/// <param name="right">节点的右节点</param>
public BinaryTreeNode(T value,
BinaryTreeNode<T> parent,
BinaryTreeNode<T> left,
BinaryTreeNode<T> right)
{
this.Value = value;
this.Right = right;
this.Left = left;
this.Parent = parent;
} #endregion #region Properties /// <summary>
/// 节点值
/// </summary>
public T Value { get; set; } /// <summary>
/// 父节点
/// </summary>
public BinaryTreeNode<T> Parent { get; set; } /// <summary>
/// 左节点
/// </summary>
public BinaryTreeNode<T> Left { get; set; } /// <summary>
/// 右节点
/// </summary>
public BinaryTreeNode<T> Right { get; set; } /// <summary>
/// 是否为根节点
/// </summary>
public bool IsRoot { get { return Parent == null; } } /// <summary>
/// 是否为叶子节点
/// </summary>
public bool IsLeaf { get { return Left == null && Right == null; } } /// <summary>
/// 是否为可访问的
/// </summary>
internal bool Visited { get; set; } #endregion #region Public Overridden Functions /// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return Value.ToString();
} #endregion
}
/// <summary>
/// 二叉树
/// </summary>
/// <typeparam name="T">二叉树中节点内容类型</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
public class BinaryTree<T> : ICollection<T>, IEnumerable<T> where T : IComparable<T>
{
#region Constructor /// <summary>
/// 二叉树
/// </summary>
public BinaryTree()
{
NumberOfNodes = ;
} /// <summary>
/// 二叉树
/// </summary>
/// <param name="root">二叉树根节点</param>
public BinaryTree(BinaryTreeNode<T> root)
: this()
{
this.Root = root;
} #endregion #region Properties /// <summary>
/// 树的根节点
/// </summary>
public BinaryTreeNode<T> Root { get; set; } /// <summary>
/// 树中节点的数量
/// </summary>
protected int NumberOfNodes { get; set; } /// <summary>
/// 树是否为空
/// </summary>
public bool IsEmpty { get { return Root == null; } } /// <summary>
/// 获取树中节点的最小值
/// </summary>
public T MinValue
{
get
{
if (IsEmpty)
return default(T); BinaryTreeNode<T> minNode = Root;
while (minNode.Left != null)
minNode = minNode.Left; return minNode.Value;
}
} /// <summary>
/// 获取树中节点的最大值
/// </summary>
public T MaxValue
{
get
{
if (IsEmpty)
return default(T); BinaryTreeNode<T> maxNode = Root;
while (maxNode.Right != null)
maxNode = maxNode.Right; return maxNode.Value;
}
} #endregion #region IEnumerable<T> Members /// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
/// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see>
/// that can be used to iterate through the collection.
/// </returns>
public IEnumerator<T> GetEnumerator()
{
foreach (BinaryTreeNode<T> node in Traverse(Root))
{
yield return node.Value;
}
} #endregion #region IEnumerable Members /// <summary>
/// Returns an enumerator that iterates through a collection.
/// </summary>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerator"/>
/// object that can be used to iterate through the collection.
/// </returns>
IEnumerator IEnumerable.GetEnumerator()
{
foreach (BinaryTreeNode<T> node in Traverse(Root))
{
yield return node.Value;
}
} #endregion #region ICollection<T> Members /// <summary>
/// 新增节点
/// </summary>
/// <param name="item">The object to add to the
/// <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
/// <exception cref="T:System.NotSupportedException">The
/// <see cref="T:System.Collections.Generic.ICollection`1"></see>
/// is read-only.</exception>
public void Add(T item)
{
if (Root == null)
{
Root = new BinaryTreeNode<T>(item);
++NumberOfNodes;
}
else
{
Insert(item);
}
} /// <summary>
/// 清除树
/// </summary>
public void Clear()
{
Root = null;
} /// <summary>
/// 树中是否包含此节点
/// </summary>
/// <param name="item">The object to locate in the
/// <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
/// <returns>
/// true if item is found in the
/// <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
/// </returns>
public bool Contains(T item)
{
if (IsEmpty)
return false; BinaryTreeNode<T> currentNode = Root;
while (currentNode != null)
{
int comparedValue = currentNode.Value.CompareTo(item);
if (comparedValue == )
return true;
else if (comparedValue < )
currentNode = currentNode.Left;
else
currentNode = currentNode.Right;
} return false;
} /// <summary>
/// 将树中节点拷贝至目标数组
/// </summary>
/// <param name="array">The array.</param>
/// <param name="arrayIndex">Index of the array.</param>
public void CopyTo(T[] array, int arrayIndex)
{
T[] tempArray = new T[NumberOfNodes];
int counter = ;
foreach (T value in this)
{
tempArray[counter] = value;
++counter;
}
Array.Copy(tempArray, , array, arrayIndex, Count);
} /// <summary>
/// 树中节点的数量
/// </summary>
public int Count
{
get { return NumberOfNodes; }
} /// <summary>
/// 树是否为只读
/// </summary>
public bool IsReadOnly
{
get { return false; }
} /// <summary>
/// 移除节点
/// </summary>
/// <param name="item">节点值</param>
/// <returns>是否移除成功</returns>
public bool Remove(T item)
{
BinaryTreeNode<T> node = Find(item);
if (node == null)
return false; List<T> values = new List<T>();
foreach (BinaryTreeNode<T> l in Traverse(node.Left))
{
values.Add(l.Value);
}
foreach (BinaryTreeNode<T> r in Traverse(node.Right))
{
values.Add(r.Value);
} if (node.Parent.Left == node)
{
node.Parent.Left = null;
}
else
{
node.Parent.Right = null;
} node.Parent = null; foreach (T v in values)
{
this.Add(v);
} return true;
} #endregion #region Private Functions /// <summary>
/// 查找指定值的节点
/// </summary>
/// <param name="value">指定值</param>
/// <returns>
/// 指定值的节点
/// </returns>
protected BinaryTreeNode<T> Find(T value)
{
foreach (BinaryTreeNode<T> node in Traverse(Root))
if (node.Value.Equals(value))
return node;
return null;
} /// <summary>
/// 遍历树
/// </summary>
/// <param name="node">遍历搜索的起始节点</param>
/// <returns>
/// The individual items from the tree
/// </returns>
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
protected IEnumerable<BinaryTreeNode<T>> Traverse(BinaryTreeNode<T> node)
{
// 遍历左子树
if (node.Left != null)
{
foreach (BinaryTreeNode<T> left in Traverse(node.Left))
yield return left;
} // 中序遍历二叉树, 顺序是 左子树,根,右子树
yield return node; // 遍历右子树
if (node.Right != null)
{
foreach (BinaryTreeNode<T> right in Traverse(node.Right))
yield return right;
}
} /// <summary>
/// 插入节点
/// </summary>
/// <param name="value">插入的节点值</param>
protected void Insert(T value)
{
// 从根节点开始比较
BinaryTreeNode<T> currentNode = Root; while (true)
{
if (currentNode == null)
throw new InvalidProgramException("The current tree node cannot be null."); // 比较当前节点与新节点的值
int comparedValue = currentNode.Value.CompareTo(value);
if (comparedValue < )
{
// 当前节点值小于新节点值
if (currentNode.Left == null)
{
currentNode.Left = new BinaryTreeNode<T>(value, currentNode);
++NumberOfNodes;
return;
}
else
{
currentNode = currentNode.Left;
}
}
else if (comparedValue > )
{
// 当前节点值大于新节点值
if (currentNode.Right == null)
{
currentNode.Right = new BinaryTreeNode<T>(value, currentNode);
++NumberOfNodes;
return;
}
else
{
currentNode = currentNode.Right;
}
}
else
{
// 当前节点值等于新节点值
currentNode = currentNode.Right;
}
}
} #endregion
}
实现方式(三):实现 BidirectionalConcurrentDictionary 双向并发字典。
namespace IteratorPattern.Implementation3
{
/// <summary>
/// 双值对
/// </summary>
/// <typeparam name="TFirst">第一个值的类型</typeparam>
/// <typeparam name="TSecond">第二个值的类型</typeparam>
[Serializable]
public struct FirstSecondPair<TFirst, TSecond>
{
private TFirst first;
private TSecond second; /// <summary>
/// 第一个值
/// </summary>
public TFirst First
{
get
{
return this.first;
}
} /// <summary>
/// 第二个值
/// </summary>
public TSecond Second
{
get
{
return this.second;
}
} /// <summary>
/// 双值对
/// </summary>
/// <param name="first">第一个值</param>
/// <param name="second">第二个值</param>
public FirstSecondPair(TFirst first, TSecond second)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second"); this.first = first;
this.second = second;
} /// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (obj == null)
return false; FirstSecondPair<TFirst, TSecond> target = (FirstSecondPair<TFirst, TSecond>)obj;
return this.First.Equals(target.First) && this.Second.Equals(target.Second);
} /// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return base.GetHashCode();
} /// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append('['); if (this.First != null)
{
sb.Append(this.First.ToString());
} sb.Append(", "); if (this.Second != null)
{
sb.Append(this.Second.ToString());
} sb.Append(']'); return sb.ToString();
} /// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(FirstSecondPair<TFirst, TSecond> left, FirstSecondPair<TFirst, TSecond> right)
{
if (((object)left == null) || ((object)right == null))
{
return false;
} return left.Equals(right);
} /// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(FirstSecondPair<TFirst, TSecond> left, FirstSecondPair<TFirst, TSecond> right)
{
return !(left == right);
}
} public class BidirectionalConcurrentDictionary<TFirst, TSecond> : IEnumerable<FirstSecondPair<TFirst, TSecond>>
{
#region Fields private ConcurrentDictionary<TFirst, TSecond> firstToSecond = new ConcurrentDictionary<TFirst, TSecond>();
private ConcurrentDictionary<TSecond, TFirst> secondToFirst = new ConcurrentDictionary<TSecond, TFirst>(); #endregion #region Public Methods public void Add(TFirst first, TSecond second)
{
if (firstToSecond.ContainsKey(first) || secondToFirst.ContainsKey(second))
throw new ArgumentException("Duplicate first or second"); firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
} public bool ContainsFirst(TFirst first)
{
return firstToSecond.ContainsKey(first);
} public bool ContainsSecond(TSecond second)
{
return secondToFirst.ContainsKey(second);
} public TSecond GetByFirst(TFirst first)
{
TSecond second;
if (!firstToSecond.TryGetValue(first, out second))
throw new KeyNotFoundException("Cannot find second by first."); return second;
} public TFirst GetBySecond(TSecond second)
{
TFirst first;
if (!secondToFirst.TryGetValue(second, out first))
throw new KeyNotFoundException("Cannot find first by second."); return first;
} public void RemoveByFirst(TFirst first)
{
TSecond second;
if (!firstToSecond.TryGetValue(first, out second))
throw new KeyNotFoundException("Cannot find second by first."); firstToSecond.Remove(first);
secondToFirst.Remove(second);
} public void RemoveBySecond(TSecond second)
{
TFirst first;
if (!secondToFirst.TryGetValue(second, out first))
throw new KeyNotFoundException("Cannot find first by second."); secondToFirst.Remove(second);
firstToSecond.Remove(first);
} public bool TryAdd(TFirst first, TSecond second)
{
if (firstToSecond.ContainsKey(first) || secondToFirst.ContainsKey(second))
return false; firstToSecond.Add(first, second);
secondToFirst.Add(second, first);
return true;
} public bool TryGetByFirst(TFirst first, out TSecond second)
{
return firstToSecond.TryGetValue(first, out second);
} public bool TryGetBySecond(TSecond second, out TFirst first)
{
return secondToFirst.TryGetValue(second, out first);
} public bool TryRemoveByFirst(TFirst first)
{
TSecond second;
if (!firstToSecond.TryGetValue(first, out second))
return false; firstToSecond.Remove(first);
secondToFirst.Remove(second);
return true;
} public bool TryRemoveBySecond(TSecond second)
{
TFirst first;
if (!secondToFirst.TryGetValue(second, out first))
return false; secondToFirst.Remove(second);
firstToSecond.Remove(first);
return true;
} public int Count
{
get { return firstToSecond.Count; }
} public void Clear()
{
firstToSecond.Clear();
secondToFirst.Clear();
} #endregion #region IEnumerable<FirstSecondPair<TFirst,TSecond>> Members IEnumerator<FirstSecondPair<TFirst, TSecond>> IEnumerable<FirstSecondPair<TFirst, TSecond>>.GetEnumerator()
{
foreach (var item in firstToSecond)
{
yield return new FirstSecondPair<TFirst, TSecond>(item.Key, item.Value);
}
} #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator()
{
foreach (var item in firstToSecond)
{
yield return new FirstSecondPair<TFirst, TSecond>(item.Key, item.Value);
}
} #endregion
} public static class ConcurrentDictionaryExtensions
{
public static TValue Add<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> collection, TKey key, TValue @value)
{
TValue result = collection.AddOrUpdate(key, @value, (k, v) => { return @value; });
return result;
} public static TValue Update<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> collection, TKey key, TValue @value)
{
TValue result = collection.AddOrUpdate(key, @value, (k, v) => { return @value; });
return result;
} public static TValue Get<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> collection, TKey key)
{
TValue @value = default(TValue);
collection.TryGetValue(key, out @value);
return @value;
} public static TValue Remove<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> collection, TKey key)
{
TValue @value = default(TValue);
collection.TryRemove(key, out @value);
return @value;
}
}
}
实现方式(四):实现 RoundRobin 循环列表。
namespace IteratorPattern.Implementation4
{
/// <summary>
/// 循环列表
/// </summary>
/// <typeparam name="T"></typeparam>
public class RoundRobinCollection<T> : IEnumerable<T>
{
private T[] _items;
private int _head; /// <summary>
/// 循环列表
/// </summary>
/// <param name="items">供循环的列表项</param>
public RoundRobinCollection(IEnumerable<T> items)
{
if (items == null || items.Count<T>() == )
{
throw new ArgumentException(
"One or more items must be provided", "items");
} // copy the list to ensure it doesn't change on us
// (and so we can lock() on our private copy)
_items = items.ToArray();
} /// <summary>
/// 获取循环器
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
{
int currentHead; lock (_items)
{
currentHead = _head++; if (_head == _items.Length)
{
// wrap back to the start
_head = ;
}
} // return results [current] ... [last]
for (int i = currentHead; i < _items.Length; i++)
{
yield return _items[i];
} // return wrap-around (if any) [0] ... [current-1]
for (int i = ; i < currentHead; i++)
{
yield return _items[i];
}
} /// <summary>
/// 获取循环器
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
《设计模式之美》为 Dennis Gao 发布于博客园的系列文章,任何未经作者本人同意的人为或爬虫转载均为耍流氓。
设计模式之美:Iterator(迭代器)的更多相关文章
- 设计模式17:Iterator 迭代器模式(行为型模式)
Iterator 迭代器模式(行为型模式) 动机(Motivation) 在软件构建过程中,集合对象内部结构常常变化各异.但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码可以透 ...
- [设计模式] Iterator - 迭代器模式:由一份奥利奥早餐联想到的设计模式
Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...
- C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
一.引言 今天我们开始讲"行为型"设计模式的第三个模式,该模式是[迭代器模式],英文名称是:Iterator Pattern.还是老套路,先从名字上来看看."迭代器模 ...
- [C# 设计模式] Iterator - 迭代器模式:我与一份奥利奥早餐的故事
Iterator - 迭代器模式 目录 前言 回顾 UML 类图 代码分析 抽象的 UML 类图 思考 前言 这是一包奥利奥(数组),里面藏了很多块奥利奥饼干(数组中的元素),我将它们放在一个碟子上慢 ...
- C#设计模式之十五迭代器模式(Iterator Pattern)【行为型】
一.引言 今天我们开始讲“行为型”设计模式的第三个模式,该模式是[迭代器模式],英文名称是:Iterator Pattern.还是老套路,先从名字上来看看.“迭代器模式”我第一次看到这个名称,我的理解 ...
- 设计模式(十五):Iterator迭代器模式 -- 行为型模式
1.概述 类中的面向对象编程封装应用逻辑.类,就是实例化的对象,每个单独的对象都有一个特定的身份和状态.单独的对象是一种组织代码的有用方法,但通常你会处理一组对象或者集合. 集合不一定是均一的.图形用 ...
- 《设计模式之美》 <03>面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
面向对象 现在,主流的编程范式或者是编程风格有三种,它们分别是面向过程.面向对象和函数式编程.面向对象这种编程风格又是这其中最主流的.现在比较流行的编程语言大部分都是面向对象编程语言.大部分项目也都是 ...
- 设计模式之美:Memento(备忘录)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Memento 模式结构样式代码. 别名 Token 意图 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这 ...
- 设计模式之美:Interpreter(解释器)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Interpreter 模式结构样式代码. 实现方式(二):解释波兰表达式(Polish Notation). 意图 给定一个语 ...
随机推荐
- [转]CentOS-6.3安装配置cmake
CentOS-6.3安装配置cmake zhoulf 2013-02-03 原创 安装说明 安装环境:CentOS-6.3安装方式:源码编译安装 软件:cmake-2.8.10.2.tar.gz下 ...
- js中typeof与instanceof用法区别
今天写JS代码,遇到动态生成多个名称相同的input复选按钮 需要判断其是否是数组,用到了if (typeof(document.MapCheckMgr.checkid)!="undefin ...
- Html命名锚
这个有什么用 案例 参考资料 这个有什么用 嘛, 不管写博客,还是公司的wiki,一般开头都会有个目录什么的, 这时候命名描就有用了, 所谓的描就是书签. 案例 比如这篇博客的目录,首先建立一个无序列 ...
- ORACLE RAC 11G 更改 /etc/hosts文件
来自官方文档:()Can I change the public hostname in my Oracle Database 10g Cluster using Oracle Clusterware ...
- 【Java】:压缩成多个压缩卷
Java自带的库不支持压缩成多个压缩卷,找到了一个开源库 zip4j ,发现更好用 so easy package com.jws.common.mail; import java.io.File; ...
- 一 java线程的等待/通知模型
java 中线程之间的通信问题,有这么一个模型:一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程.前者是生产者,后者就是消费者 ...
- iOS emoji表情转码 或者判断
如果项目中有评论或者信息恢复的地方,往往会用到emoji,有时候如后台不支持emoji,就会显示乱码错误,我们可以把emoji转成unicode编码或者utf8编码格式传给服务器.当然如果后台服务器接 ...
- Rational.Rose.Enterprise.v7.0 (2007)安装分享
很多人都在找rational软件,很多都是2003的,有的宣称是2007,但结果还是2003.也许真的不存在Rational.Rose 2007,不过有IBM.Rational.Rose.Enterp ...
- Dojo框架学习笔记<二>
一.dojo/dom 该模块定义了Dojo Dom API,主要有以下几种用法: 1.dom.byId();(相当于document.getElementById()) ①最直接的用 ...
- 中国UTM分区
高斯-克吕格投影是“等角横切圆柱投影”,投影后中央经线保持长度不变,即比例系数为1: UTM投影是“等角横轴割圆柱投影”,圆柱割地球于南纬80度.北纬84度两条等高圈,投影后两条割线上没有变形,中央经 ...