diff --git a/conferences/2024/17-ilist-and-icollection/code/ProgramArrayList.cs b/conferences/2024/17-ilist-and-icollection/code/ProgramArrayList.cs new file mode 100644 index 0000000..c1270bc --- /dev/null +++ b/conferences/2024/17-ilist-and-icollection/code/ProgramArrayList.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Programacion +{ + #region ARRAYLIST + class ArrayList : IList + { + T[] array; + int increaseLength; + public ArrayList(int increaseLength = 1000) + { + array = new T[increaseLength]; + this.increaseLength = increaseLength; + Count = 0; + } + + #region MÉTODOS POR SER UN ICollection + public int Count { get; private set; } + public void Add(T x) + { + if (Count < array.Length) + array[Count++] = x; + else + { + //Crear un array de mayor longitud para poder poner el nuevo elemento + T[] values = new T[array.Length + increaseLength]; + System.Array.Copy(array, 0, values, 0, array.Length); + values[Count++] = x; + array = values; + } + } + public bool Contains(T x) + { + for (int k = 0; k < Count; k++) + if (array[k].Equals(x)) return true; + return false; + } + #endregion + + #region CP IMPLEMENTAR LOS OTROS MÉTODOS DE ICollection + public bool IsReadOnly + { + get { throw new Exception("Implementar IsReadOnly en CP"); } + } + public void Clear() + { + throw new Exception("Implementar Clear en CP"); + } + public void CopyTo(T[] array, int arrayIndex) + { + throw new Exception("Implementar CopyTo en CP"); + } + public bool Remove(T item) + { + throw new Exception("Implementar Remove en CP"); + } + #endregion + + #region METODOS PROPIOS DE IList + //... + public T this[int i] + { + get + { + if (i < Count) return array[i]; + else throw new Exception("Index out of range"); + } + set + { + if (i < Count) array[i] = value; + else throw new Exception("Index out of range"); + } + } + #endregion + + #region CP IMPLEMENTAR LOS OTROS MÉTODOS DE IList + public int IndexOf(T item) + { + throw new Exception("Implementar IndexOf en CP"); + } + public void Insert(int index, T item) + { + throw new Exception("Implementar Insert en CP"); + } + public void RemoveAt(int index) + { + throw new Exception("Implementar RemoveAt en CP"); + } + #endregion + + #region IMPLEMENTACIÓN DE IEnumerable USANDO UN IEnumerator + //public IEnumerator GetEnumerator() + //{ + // return new ArrayListEnumerator(this); + //} + + //IEnumerator IEnumerable.GetEnumerator() + //{ + // return GetEnumerator(); + //} + + //class ArrayListEnumerator : IEnumerator + //{ + // bool seHizoMoveNext; + // int cursor; + // int total; + // //T current; + // ArrayList originalList; + // public ArrayListEnumerator(ArrayList list) + // { + // seHizoMoveNext = false; + // originalList = list; + // cursor = 0; + // total = originalList.Count; + // } + // public bool MoveNext() + // { + // if (seHizoMoveNext) + // { + // if (++cursor < total) + // { + // return true; + // } + // else return false; + // } + // seHizoMoveNext = true; + // return (cursor < total); + // } + // public T Current + // { + // get + // { + // if (!seHizoMoveNext) throw new Exception("Hay que hacer moveNext"); + // if (cursor < total) return originalList.array[cursor]; + // else throw new Exception("No hay más elementos"); + // } + // } + // object IEnumerator.Current + // { + // get + // { + // return Current; + // } + // } + // public void Reset() + // { + // //CP IMPLEMENTAME !!!! + // } + // public void Dispose() + // { + // //POR AHORA NO ME IMPLEMENTES + // } + //} //ListEnumerator + #endregion + + #region IMPLEMENTACION MÁS SIMPLE USANDO YIELD (no ver por ahora) + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Count; i++) + { + yield return array[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + #endregion + } + #endregion + + #region EJERCICIOS PARA CP IMPLEMENTAR MÉTODOS QUE OPEREN SOBRE IEnumerables y DEVUELVAN IEnumerables + static class Enumerable + { + + public static IEnumerable Union(IEnumerable enum1, IEnumerable enum2) + { + + throw new InvalidOperationException("Union no implementada aún"); + + } + + public static IEnumerable Take(IEnumerable enum1, int n) + { + + throw new InvalidOperationException("Take no implementado aún"); + + } + } + #endregion + + class ProgramArrayList + { + static void Main(string[] args) + { + ArrayList ints = new ArrayList(5); + for (int i = 0; i < 20; i++) ints.Add(i * 2); + ints[10] = 1000; + Console.WriteLine("Hay {0} items", ints.Count); + Console.WriteLine("Iterando con for ..."); + for (int i = 0; i < ints.Count; i++) Console.WriteLine(ints[i]); + Console.WriteLine("Iterando con foreach ..."); + foreach (int k in ints) Console.WriteLine(k); + Console.WriteLine(ints.Contains(1000)); + } + } +} diff --git a/conferences/2024/17-ilist-and-icollection/code/ProgramLinkedList.cs b/conferences/2024/17-ilist-and-icollection/code/ProgramLinkedList.cs new file mode 100644 index 0000000..ae50f81 --- /dev/null +++ b/conferences/2024/17-ilist-and-icollection/code/ProgramLinkedList.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.Collections; + +namespace Programacion +{ + #region LINKEDLIST + class Linkable + { + public T valor; + public Linkable prox; + public Linkable(T valor, Linkable prox) + { + this.valor = valor; + this.prox = prox; + } + } + class LinkedList : IList + { + Linkable primero; + Linkable ultimo; + public LinkedList() + { + Count = 0; + } + + #region MÉTODOS POR SER IList UN ICollection + public int Count { get; private set; } + public void Add(T x) + { + Linkable nodo = new Linkable(x, null); + if (Count == 0) + { + primero = nodo; + ultimo = nodo; + Count = 1; + } + else + { + ultimo.prox = nodo; + ultimo = nodo; + Count++; + } + } + public bool Contains(T x) + { + Linkable cursor = primero; + for (int i = 0; i < Count; i++) + if (cursor.valor.Equals(x)) return true; + else + cursor = cursor.prox; + return false; + } + #endregion + + #region CP IMPLEMENTAR LOS OTROS MÉTODOS DE ICollection + public bool IsReadOnly + { + get { throw new Exception("Implementar IsReadOnly en CP"); } + } + public void Clear() + { + throw new Exception("Implementar Clear en CP"); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new Exception("Implementar CopyTo en CP"); + } + public bool Remove(T item) + { + throw new Exception("Implementar Remove en CP"); + } + #endregion + + #region METODOS PROPIOS DE IList + public T this[int i] + { + get + { + Linkable cursor; + if (i >= 0 && i < Count) + { + cursor = primero; + for (int k = 0; k < i; k++) + cursor = cursor.prox; + return cursor.valor; + } + else throw new Exception("Index out of range"); + } + set + { + Linkable cursor; + if (i >= 0 && i < Count) + { + cursor = primero; + for (int k = 0; k < i; k++) + cursor = cursor.prox; + cursor.valor = value; + } + else throw new Exception("Indice fuera de rango"); + } + } + #endregion + + #region CP IMPLEMENTAR LOS OTROS MÉTODOS DE IList + public int IndexOf(T item) + { + throw new Exception("Implementar IndexOf en CP"); + } + + public void Insert(int index, T item) + { + throw new Exception("Implementar Insert en CP"); + } + + public void RemoveAt(int index) + { + throw new Exception("Implementar RemoveAt en CP"); + } + #endregion + + #region IMPLEMENTACIÓN DE IEnumerable USANDO UN IEnumerator + public IEnumerator GetEnumerator() + { + return new LinkedListEnumerator(this); + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + class LinkedListEnumerator : IEnumerator + { + bool seHizoMoveNext; + Linkable cursor; + int indiceCursor; + int total; + public LinkedListEnumerator(LinkedList list) + { + cursor = list.primero; total = list.Count; + } + public bool MoveNext() + { + if (seHizoMoveNext) + { + indiceCursor++; + cursor = cursor.prox; + return (indiceCursor < total); + } + seHizoMoveNext = true; + return (indiceCursor < total); + } + public T Current + { + get + { + if (!seHizoMoveNext) throw new Exception("Hay que hacer moveNext"); + if (indiceCursor < total) return cursor.valor; + else throw new Exception("No hay más elementos"); + } + } + object IEnumerator.Current + { + get + { + return Current; + } + } + public void Reset() + { + //CP IMPLEMENTAME !!!! + } + public void Dispose() + { + //POR AHORA NO ME IMPLEMENTES + } + } //LinkedListEnumerator + #endregion + + #region IMPLEMENTACION MÁS SIMPLE USANDO YIELD + //public IEnumerator GetEnumerator() + //{ + // if (Count > 0) + // { + // Linkable cursor = primero; + // for (int i = 0; i < Count; i++) + // { + // yield return cursor.valor; + // cursor = cursor.prox; + // } + // } + //} + //IEnumerator IEnumerable.GetEnumerator() + //{ + // return GetEnumerator(); + //} + #endregion + } + #endregion LINKEDLIST + + #region EJERCICIOS PARA CP IMPLEMENTAR MÉTODOS QUE OPEREN SOBRE IEnumerables y DEVUELVAN IEnumerables + static class Enumerable + { + public static IEnumerable Union(IEnumerable items1, IEnumerable items2) + { + throw new InvalidOperationException("Union no implementada aún"); + } + + public static IEnumerable Take(IEnumerable items, int n) + { + throw new InvalidOperationException("Take no implementado aún"); + } + + public static IEnumerable TakeLast(IEnumerable items, int n) + { + throw new InvalidOperationException("Take no implementado aún"); + } + + public static IEnumerable Reverse(IEnumerable items) + { + throw new InvalidOperationException("Reverse no implementado aún"); + } + } + #endregion + + class ProgramLinkedList + { + static void Main(string[] args) + { + LinkedList ints = new LinkedList(); + for (int i = 0; i < 20; i++) ints.Add(i * 2); + ints[10] = 1000; + Console.WriteLine("Hay {0} items", ints.Count); + Console.WriteLine("Iterando con for ..."); + for (int i = 0; i < ints.Count; i++) Console.WriteLine(ints[i]); + Console.WriteLine("Iterando con foreach ..."); + foreach (int k in ints) Console.WriteLine(k); + + //ERRORES POR MAL USO DE LOS TIPOS + //Console.WriteLine(ints.Contains("white")); + //ints[2] = "orange"; + //string s = ints[5]; + } + } +} \ No newline at end of file diff --git a/conferences/2024/17-ilist-and-icollection/lecture-17.md b/conferences/2024/17-ilist-and-icollection/lecture-17.md new file mode 100644 index 0000000..d49a3ea --- /dev/null +++ b/conferences/2024/17-ilist-and-icollection/lecture-17.md @@ -0,0 +1,227 @@ +# `IList` e `ICollection` + +Los arrays tienen limitaciones significativas como estructuras de datos para modelar colecciones indexables. Aunque son eficientes en el acceso directo a elementos por índice, tienen un tamaño fijo que debe definirse al inicio y no puede modificarse dinámicamente sin crear una nueva instancia. Sin embargo, no son la única forma de modelar la idea de una colección indexable en programación. Las listas, como estructuras de datos en general, ofrecen una alternativa más flexible y dinámica para almacenar elementos ordenados y acceder a ellos mediante índices. + +## `IList` + +La interfaz `IList` en C# define un contrato para una colección indexable de elementos del tipo T. Permite acceder, insertar, eliminar y modificar elementos en una secuencia ordenada a través de índices enteros. Esta interfaz hereda de `ICollection` y extiende `IEnumerable` y `IEnumerable`, proporcionando así métodos para manipular colecciones de elementos de manera eficiente y flexible. + +```csharp +public interface IList : ICollection, IEnumerable, IEnumerable +{ + T this[int index] { get; set; } + int IndexOf(T item); + void Insert(int index, T item); + void RemoveAt(int index); +} +``` + +- **this[int index]**: Permite acceder a un elemento específico mediante su índice y modificarlo si es necesario. +- **IndexOf(T item)**: Devuelve el índice de la primera aparición del elemento especificado dentro de la lista, o -1 si no se encuentra. +- **Insert(int index, T item)**: Inserta un elemento en la posición especificada del índice, desplazando los elementos existentes hacia la derecha. +- **RemoveAt(int index)**: Elimina el elemento en la posición especificada del índice y desplaza los elementos siguientes hacia la izquierda para llenar el espacio vacío. + +## `ICollection` + +La interfaz `ICollection` en C# define un contrato para una colección de elementos de tipo T. Esta interfaz proporciona métodos para trabajar con colecciones de elementos de manera eficiente y flexible, sin especificar el orden de los elementos. + +```csharp +public interface ICollection : IEnumerable, IEnumerable +{ + int Count { get; } + bool IsReadOnly { get; } + void Add(T item); + void Clear(); + bool Contains(T item); + void CopyTo(T[] array, int arrayIndex); + bool Remove(T item); +} +``` + +- **Count**: Proporciona el número de elementos presentes en la colección. +- **IsReadOnly**: Indica si la colección es de solo lectura o permite modificaciones. +- **Add(T item)**: Agrega un elemento al final de la colección. +- **Clear()**: Elimina todos los elementos de la colección. +- **Contains(T item)**: Determina si la colección contiene un elemento específico. +- **CopyTo(T[] array, int arrayIndex)**: Copia los elementos de la colección a un array, comenzando en el índice especificado del array de destino. +- **Remove(T item)**: Elimina la primera aparición de un elemento específico de la colección. + +## `ArrayList` + +La implementación de una lista dinámica, como `ArrayList`, se basa en el uso de un array subyacente que se redimensiona dinámicamente cuando es necesario añadir más elementos de los que puede contener en su tamaño actual. Esto se logra mediante los siguientes pasos: + +1. **Inicialización**: Se crea un array con una capacidad inicial fija. En el caso de `ArrayList`, esto se especifica al momento de la creación del objeto. + +2. **Adición de elementos**: Cuando se añade un nuevo elemento a la lista utilizando el método `Add(T item)`, se verifica si la cantidad actual de elementos (`Count`) es menor que la capacidad del array. Si es así, el elemento se agrega al final del array en la posición correspondiente a `Count` y luego se incrementa el contador de elementos. + +3. **Redimensionamiento**: Si la cantidad de elementos alcanza la capacidad máxima del array, se crea un nuevo array con una capacidad mayor (generalmente aumentando la capacidad actual en un tamaño predefinido o proporcional). Luego, se copian todos los elementos del array actual al nuevo array y se reemplaza el array subyacente por el nuevo. Esto asegura que haya suficiente espacio para más elementos sin necesidad de desplazar o reorganizar elementos existentes. + +Este enfoque optimiza la eficiencia de operaciones de adición y garantiza un rendimiento adecuado en la mayoría de los casos, aunque puede tener un costo de tiempo ocasional durante la operación de redimensionamiento del array. + + +```csharp +class ArrayList : IList +{ + T[] array; + int increaseLength; + + public int Count { get; private set; } + + public ArrayList(int increaseLength = 1000) + { + array = new T[increaseLength]; + this.increaseLength = increaseLength; + Count = 0; + } + + public void Add(T x) + { + if (Count < array.Length) + array[Count++] = x; + else + { + //Crear un array de mayor longitud para poder poner el nuevo elemento + T[] values = new T[array.Length + increaseLength]; + System.Array.Copy(array, 0, values, 0, array.Length); + values[Count++] = x; + array = values; + } + } + public bool Contains(T x) + { + for (int k = 0; k < Count; k++) + if (array[k].Equals(x)) return true; + return false; + } + + public T this[int i] + { + get + { + if (i < Count) return array[i]; + else throw new Exception("Index out of range"); + } + set + { + if (i < Count) array[i] = value; + else throw new Exception("Index out of range"); + } + } + + // ... +} +``` + +Esta implementación demuestra cómo se puede crear una estructura de datos dinámica utilizando un array subyacente en C#. + +## `LinkedList` + +La `LinkedList` es una estructura de datos enlazada que permite almacenar una colección de elementos de tipo `T` de manera dinámica. A diferencia de las listas basadas en arrays, donde los elementos están almacenados en ubicaciones contiguas de memoria, una lista enlazada organiza los elementos como nodos individuales que están conectados entre sí mediante referencias. + +### Objetivos + +La `LinkedList` tiene como objetivo principal optimizar las operaciones de inserción y eliminación en comparación con las listas basadas en arrays, donde agregar o eliminar elementos puede requerir mover o redimensionar grandes bloques de memoria. Sus principales objetivos incluyen: + +- **No tener espacio reservado sin usar**: A diferencia de un array donde se debe definir un tamaño fijo, una lista enlazada utiliza solo el espacio necesario para los elementos que contiene. + +- **No necesitar expansión cuando se llena**: No requiere redimensionamiento de estructuras de datos subyacentes al agregar elementos nuevos, ya que cada elemento se aloja en un nuevo nodo enlazado. + +- **No necesitar desplazar elementos al insertar o eliminar al comienzo o final**: Las operaciones de inserción y eliminación son más eficientes en términos de tiempo, ya que solo implican ajustar las referencias entre nodos, en lugar de mover elementos en una estructura de datos contigua. + +### ¿Cómo conseguirlo? + +Seguir una idea similar a la de **boxing**: para formar una colección de elementos, voy a envolver cada elemento en un objeto `Linkable`. El objeto que envuelve a parte de conocer el valor, mantiene una referencia al siguiente enlazable. + +```csharp +public class Linkable +{ + T Value {get; private set;} + Linkable Next {get; private set;} +} +``` + +- **Value**: Almacena el valor del elemento actual. +- **Next**: Mantiene una referencia al siguiente nodo en la secuencia, permitiendo la navegación secuencial a través de la lista. + +### Implementación de `LinkedList` + +La clase `LinkedList` maneja la estructura de la lista enlazada, manteniendo referencias al primer y último nodo de la lista: + +```csharp +class LinkedList : IList +{ + Linkable first; + Linkable last; + + public int Count { get; private set; } + + public LinkedList() + { + Count = 0; + } + + public void Add(T x) + { + Linkable node = new Linkable(x, null); + if (Count == 0) + { + first = node; + last = node; + Count = 1; + } + else + { + last.Next = node; + last = node; + Count++; + } + } + + public bool Contains(T x) + { + Linkable cursor = first; + for (int i = 0; i < Count; i++) + if (cursor.Value.Equals(x)) return true; + else + cursor = cursor.Next; + return false; + } + + public T this[int i] + { + get + { + Linkable cursor; + if (i >= 0 && i < Count) + { + cursor = first; + for (int k = 0; k < i; k++) + cursor = cursor.Next; + return cursor.Value; + } + else throw new Exception("Index out of range"); + } + set + { + Linkable cursor; + if (i >= 0 && i < Count) + { + cursor = first; + for (int k = 0; k < i; k++) + cursor = cursor.Next; + cursor.Value = value; + } + else throw new Exception("Indice fuera de rango"); + } + } + + // ... +} +``` + +Esta implementación de `LinkedList` permite agregar elementos al final de la lista de manera eficiente, recorrer la lista para verificar la presencia de un elemento y acceder a elementos específicos por índice, aprovechando la flexibilidad y eficiencia de las listas enlazadas para operaciones dinámicas en C#. + +## Notas generales + +- `List` es una opción común para almacenar y manipular colecciones de elementos en C# debido a su eficiencia y versatilidad. +- `LinkedList` puede ser más adecuado en situaciones donde se necesitan frecuentes inserciones y eliminaciones en el medio de la lista.