//+------------------------------------------------------------------+ //| LinkedList.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include #include //+------------------------------------------------------------------+ //| Class CLinkedListNode. | //| Usage: Represents a node of linked list. | //+------------------------------------------------------------------+ template class CLinkedListNode { protected: CLinkedList*m_list; CLinkedListNode*m_next; CLinkedListNode*m_prev; T m_item; public: CLinkedListNode(T value): m_item(value) { } CLinkedListNode(CLinkedList*list,T value): m_list(list),m_item(value) { } ~CLinkedListNode(void) { } //--- methods of access to protected data CLinkedList* List(void) { return(m_list); } void List(CLinkedList*value) { m_list=value; } CLinkedListNode*Next(void) { return(m_next); } void Next(CLinkedListNode*value) { m_next=value; } CLinkedListNode*Previous(void) { return(m_prev); } void Previous(CLinkedListNode*value) { m_prev=value; } T Value(void) { return(m_item); } void Value(T value) { m_item=value; } }; //+------------------------------------------------------------------+ //| Class CLinkedList. | //| Usage: Represents a doubly linked list. | //+------------------------------------------------------------------+ template class CLinkedList: public ICollection { protected: CLinkedListNode*m_head; int m_count; public: CLinkedList(void); CLinkedList(ICollection*collection); CLinkedList(T &array[]); ~CLinkedList(void); //--- methods of filling data bool Add(T value); CLinkedListNode*AddAfter(CLinkedListNode*node,T value); bool AddAfter(CLinkedListNode*node,CLinkedListNode*new_node); CLinkedListNode*AddBefore(CLinkedListNode*node,T value); bool AddBefore(CLinkedListNode*node,CLinkedListNode*new_node); CLinkedListNode*AddFirst(T value); bool AddFirst(CLinkedListNode*node); CLinkedListNode*AddLast(T value); bool AddLast(CLinkedListNode*node); //--- methods of access to protected data int Count(void); CLinkedListNode*Head(void) {return(m_head);} CLinkedListNode*First(void); CLinkedListNode*Last(void); bool Contains(T item); //--- methods of copy data from collection int CopyTo(T &dst_array[],const int dst_start=0); //--- methods of cleaning and deleting void Clear(void); bool Remove(T item); bool Remove(CLinkedListNode*node); bool RemoveFirst(void); bool RemoveLast(void); //--- method for searching CLinkedListNode*Find(T value); CLinkedListNode*FindLast(T value); private: bool ValidateNode(CLinkedListNode*node); bool ValidateNewNode(CLinkedListNode*node); void InternalInsertNodeBefore(CLinkedListNode*node,CLinkedListNode*new_node); void InternalInsertNodeToEmptyList(CLinkedListNode*new_node); void InternalRemoveNode(CLinkedListNode*node); }; //+------------------------------------------------------------------+ //| Initializes a new instance of the CLinkedList class that is | //| empty. | //+------------------------------------------------------------------+ template CLinkedList::CLinkedList(void): m_count(0) { } //+------------------------------------------------------------------+ //| Initializes a new instance of the CLinkedList class that | //| contains elements copied from the specified array and has | //| sufficient capacity to accommodate the number of elements copied.| //+------------------------------------------------------------------+ template CLinkedList::CLinkedList(T &array[]): m_count(0) { for(int i=0; i class that | //| contains elements copied from the specified collection and has | //| sufficient capacity to accommodate the number of elements copied.| //+------------------------------------------------------------------+ template CLinkedList::CLinkedList(ICollection*collection): m_count(0) { //--- check collection if(CheckPointer(collection)!=POINTER_INVALID) { T array[]; int size=collection.CopyTo(array,0); for(int i=0; i CLinkedList::~CLinkedList(void) { Clear(); } //+------------------------------------------------------------------+ //| Adds an value to the end of the list. | //+------------------------------------------------------------------+ template bool CLinkedList::Add(T value) { return(CheckPointer(AddLast(value))!=POINTER_INVALID); } //+------------------------------------------------------------------+ //| Adds a new node containing the specified value after the | //| specified existing node in the CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::AddAfter(CLinkedListNode*node,T value) { //--- check node if(!ValidateNode(node)) return(NULL); //--- create new node CLinkedListNode*result=new CLinkedListNode(node.List(),value); //--- insert node to the list InternalInsertNodeBefore(node.Next(),result); return(result); } //+------------------------------------------------------------------+ //| Adds the specified new node after the specified existing node in | //| the LinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::AddAfter(CLinkedListNode*node,CLinkedListNode*new_node) { //--- check node if(!ValidateNode(node)) return(false); //--- check new node if(!ValidateNewNode(new_node)) return(false); //--- insert node to the list InternalInsertNodeBefore(node.Next(),new_node); //--- set the current list as list for new node new_node.List(GetPointer(this)); return(true); } //+------------------------------------------------------------------+ //| Adds a new node containing the specified value before the | //| specified existing node in the CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::AddBefore(CLinkedListNode*node,T value) { //--- check node if(!ValidateNode(node)) return(NULL); //--- create new node CLinkedListNode*result=new CLinkedListNode(node.List(),value); //--- insert node to the list InternalInsertNodeBefore(node,result); if(node==m_head) m_head=result; return(result); } //+------------------------------------------------------------------+ //| Adds the specified new node before the specified existing node in| //| the LinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::AddBefore(CLinkedListNode*node,CLinkedListNode*new_node) { //--- check node if(!ValidateNode(node)) return(false); //--- check new node if(!ValidateNewNode(new_node)) return(false); //--- insert node to the list InternalInsertNodeBefore(node,new_node); //--- set the current list as list for new node new_node.List(GetPointer(this)); if(node==m_head) m_head=new_node; return(true); } //+------------------------------------------------------------------+ //| Adds a new node containing the specified value at the start of | //| the CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::AddFirst(T value) { //--- create new node CLinkedListNode*node=new CLinkedListNode(GetPointer(this),value); //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) { //--- insert node to the empty list InternalInsertNodeToEmptyList(node); } else { //--- insert node to the list InternalInsertNodeBefore(m_head,node); m_head=node; } return(node); } //+------------------------------------------------------------------+ //| Adds the specified new node at the start of the CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::AddFirst(CLinkedListNode*node) { //--- check node if(!ValidateNewNode(node)) return(false); //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) { //--- insert node to the empty list InternalInsertNodeToEmptyList(node); } else { //--- insert node to the list InternalInsertNodeBefore(m_head,node); m_head=node; } //--- set the current list as list for node node.List(GetPointer(this)); return(true); } //+------------------------------------------------------------------+ //| Adds a new node containing the specified value at the end of the | //| CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::AddLast(T value) { //--- create new node CLinkedListNode*node=new CLinkedListNode(GetPointer(this),value); //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) { //--- insert node to the empty list InternalInsertNodeToEmptyList(node); } else { //--- insert node to the list InternalInsertNodeBefore(m_head,node); } return(node); } //+------------------------------------------------------------------+ //| Adds the specified new node at the end of the CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::AddLast(CLinkedListNode*node) { //--- check node if(!ValidateNewNode(node)) return(false); //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) { //--- insert node to the empty list InternalInsertNodeToEmptyList(node); } else { //--- insert node to the list InternalInsertNodeBefore(m_head,node); } //--- set the current list as list for node node.List(GetPointer(this)); return(true); } //+------------------------------------------------------------------+ //| Determines whether an element is in the linked list. | //+------------------------------------------------------------------+ template int CLinkedList::Count(void) { return(m_count); } //+------------------------------------------------------------------+ //| Gets the first node of the CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::First(void) { return(m_head); } //+------------------------------------------------------------------+ //| Gets the last node of the CLinkedList. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::Last(void) { return(CheckPointer(m_head)!=POINTER_INVALID ? m_head.Previous() : NULL); } //+------------------------------------------------------------------+ //| Determines whether a value is in the CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::Contains(T item) { return(CheckPointer(Find(item))!=POINTER_INVALID); } //+------------------------------------------------------------------+ //| Copies a range of elements from the linkedlist to a compatible | //| one-dimensional array. | //+------------------------------------------------------------------+ template int CLinkedList::CopyTo(T &dst_array[],const int dst_start=0) { //--- resize array if(dst_start+m_count>ArraySize(dst_array)) ArrayResize(dst_array,dst_start+m_count); //--- check start index if(dst_start>ArraySize(dst_array)) return(0); //--- start copy CLinkedListNode*node=m_head; if(CheckPointer(node)!=POINTER_INVALID) { int dst_index=dst_start; do { dst_array[dst_index++]=node.Value(); node=node.Next(); } while(dst_index. | //+------------------------------------------------------------------+ template void CLinkedList::Clear(void) { //--- check count if(m_count>0) { //--- check head node if(CheckPointer(m_head)!=POINTER_INVALID) { while(m_head.Next()!=m_head) { CLinkedListNode*node=m_head.Next(); m_head.Next(node.Next()); delete node; } delete m_head; } //--- reset count m_count=0; } } //+------------------------------------------------------------------+ //| Removes the first occurrence of the specified value from the | //| CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::Remove(T item) { //--- find node with specified value CLinkedListNode*node=Find(item); if(CheckPointer(node)!=POINTER_INVALID) { //--- remove node InternalRemoveNode(node); return(true); } return(false); } //+------------------------------------------------------------------+ //| Removes the specified node from the LinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::Remove(CLinkedListNode*node) { //--- check node if(ValidateNode(node)) { //--- remove node InternalRemoveNode(node); return(true); } return(false); } //+------------------------------------------------------------------+ //| Removes the node at the start of the CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::RemoveFirst(void) { //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) return(false); //--- remove head node InternalRemoveNode(m_head); return(true); } //+------------------------------------------------------------------+ //| Removes the node at the end of the CLinkedList. | //+------------------------------------------------------------------+ template bool CLinkedList::RemoveLast(void) { //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) return(false); //--- remove last node InternalRemoveNode(m_head.Previous()); return(true); } //+------------------------------------------------------------------+ //| Finds the first node that contains the specified value. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::Find(T value) { CLinkedListNode*node=m_head; //--- start search specified value in the list if(CheckPointer(node)!=POINTER_INVALID) { do { //--- use default equals function if(::Equals(node.Value(),value)) return(node); node=node.Next(); } while(node!=m_head); } return(NULL); } //+------------------------------------------------------------------+ //| Finds the last node that contains the specified value. | //+------------------------------------------------------------------+ template CLinkedListNode*CLinkedList::FindLast(T value) { //--- check head node if(CheckPointer(m_head)==POINTER_INVALID) return(NULL); //--- get last node CLinkedListNode *last = m_head.Previous(); CLinkedListNode *node = last; //--- start search from the end of the list if(node!=NULL) { do { //--- use default equals function if(::Equals(node.Value(),value)) return(node); node=node.Previous(); } while(node!=last); } return(NULL); } //+------------------------------------------------------------------+ //| Validation of node on not null and belongs in the current list. | //+------------------------------------------------------------------+ template bool CLinkedList::ValidateNode(CLinkedListNode*node) { return(CheckPointer(node)!=POINTER_INVALID && node.List()==GetPointer(this)); } //+------------------------------------------------------------------+ //| Validation of new node on not null. | //+------------------------------------------------------------------+ template bool CLinkedList::ValidateNewNode(CLinkedListNode*node) { return(CheckPointer(node)!=POINTER_INVALID && node.List()==NULL); } //+------------------------------------------------------------------+ //| Insert node before the specified node. | //+------------------------------------------------------------------+ template void CLinkedList::InternalInsertNodeBefore(CLinkedListNode*node,CLinkedListNode*new_node) { //--- set node befor the specified node new_node.Next(node); new_node.Previous(node.Previous()); node.Previous().Next(new_node); node.Previous(new_node); //--- increment count m_count++; } //+------------------------------------------------------------------+ //| Add first node to the list. | //+------------------------------------------------------------------+ template void CLinkedList::InternalInsertNodeToEmptyList(CLinkedListNode*new_node) { //--- set node as head of the list new_node.Next(new_node); new_node.Previous(new_node); m_head=new_node; //--- increment count m_count++; } //+------------------------------------------------------------------+ //| Remove specified node from the list. | //+------------------------------------------------------------------+ template void CLinkedList::InternalRemoveNode(CLinkedListNode*node) { //--- check node if(node.Next()==node) { //--- resets the head of the list m_head=NULL; } else { //--- detach node from the list node.Next().Previous(node.Previous()); node.Previous().Next(node.Next()); if(m_head==node) m_head=node.Next(); } //--- decrement count and delete node m_count--; delete node; } //+------------------------------------------------------------------+