diff --git a/core/modules/node/lib/Drupal/node/Controller/NodeController.php b/core/modules/node/lib/Drupal/node/Controller/NodeController.php
index 09a9690..d1ea5da 100644
--- a/core/modules/node/lib/Drupal/node/Controller/NodeController.php
+++ b/core/modules/node/lib/Drupal/node/Controller/NodeController.php
@@ -16,14 +16,6 @@
 class NodeController {
 
   /**
-   * @todo Remove node_admin_nodes().
-   */
-  public function contentOverview() {
-    module_load_include('admin.inc', 'node');
-    return node_admin_nodes();
-  }
-
-  /**
    * @todo Remove node_add_page().
    */
   public function addPage() {
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index 92f4f5b..2c95a0c 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -30,6 +30,7 @@
  *       "delete" = "Drupal\node\Form\NodeDeleteForm",
  *       "edit" = "Drupal\node\NodeFormController"
  *     },
+ *     "list" = "Drupal\node\NodeListController",
  *     "translation" = "Drupal\node\NodeTranslationController"
  *   },
  *   base_table = "node",
diff --git a/core/modules/node/lib/Drupal/node/NodeListController.php b/core/modules/node/lib/Drupal/node/NodeListController.php
new file mode 100644
index 0000000..f34e57d
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/NodeListController.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\NodeListController.
+ */
+
+namespace Drupal\node;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Datetime\Date;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityListController;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a list controller for nodes.
+ */
+class NodeListController extends EntityListController {
+
+  /**
+   * The date service.
+   *
+   * @var \Drupal\Core\Datetime\Date
+   */
+  protected $dateService;
+
+  /**
+   * Constructs a new NodeListController object.
+   *
+   * @param string $entity_type
+   *   The type of entity to be listed.
+   * @param array $entity_info
+   *   An array of entity info for the entity type.
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
+   *   The entity storage controller class.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke hooks on.
+   * @param \Drupal\Core\Datetime\Date $date_service
+   *   The date service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
+   */
+  public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, Date $date_service) {
+    parent::__construct($entity_type, $entity_info, $storage, $module_handler);
+
+    $this->dateService = $date_service;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static(
+      $entity_type,
+      $entity_info,
+      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('module_handler'),
+      $container->get('date')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildHeader() {
+    // Enable language column and filter if multiple languages are enabled.
+    $header = array(
+      'title' => $this->t('Title'),
+      'type' => array(
+        'data' => $this->t('Content type'),
+        'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
+      ),
+      'author' => array(
+        'data' => $this->t('Author'),
+        'class' => array(RESPONSIVE_PRIORITY_LOW),
+      ),
+      'status' => $this->t('Status'),
+      'changed' => array(
+        'data' => $this->t('Updated'),
+        'class' => array(RESPONSIVE_PRIORITY_LOW)
+      ),
+    );
+    if (language_multilingual()) {
+      $header['language_name'] = array('data' => $this->t('Language'), 'class' => array(RESPONSIVE_PRIORITY_LOW));
+    }
+    $header['operations'] = $this->t('Operations');
+    return $header;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRow(EntityInterface $entity) {
+    $mark = array(
+      '#theme' => 'mark',
+      '#mark_type' => node_mark($entity->id(), $entity->getChangedTime()),
+    );
+    $langcode = $entity->language()->id;
+    $row['title']['data'] = array(
+      '#type' => 'link',
+      '#title' => $entity->label(),
+      '#href' => $entity->uri(),
+      '#options' => $langcode != Language::LANGCODE_NOT_SPECIFIED && isset($languages[$langcode]) ? array('language' => $languages[$langcode]) : array(),
+      '#suffix' => ' ' . drupal_render($mark),
+    );
+    $row['type'] = String::checkPlain(node_get_type_label($entity));
+    $row['author']['data'] = array(
+      '#theme' => 'username',
+      '#account' => $entity->getAuthor(),
+    );
+    $row['status'] = $entity->isPublished() ? $this->t('published') : $this->t('not published');
+    $row['changed'] = $this->dateService->format($entity->getChangedTime(), 'short');
+    if (language_multilingual()) {
+      $row['language_name'] = language_name($langcode);
+    }
+    $row['operations']['data'] = $this->buildOperations($entity);
+    return $row;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOperations(EntityInterface $entity) {
+    $operations = parent::getOperations($entity);
+    if ($entity->isTranslatable()) {
+      $operations['translate'] = array(
+        'title' => $this->t('Translate'),
+        'href' => $entity->uri() . '/translations',
+      );
+    }
+
+    $destination = drupal_get_destination();
+    foreach ($operations as $key => $operation) {
+      $operations[$key]['query'] = $destination;
+
+    }
+    return $operations;
+  }
+
+}
diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc
index 03387f8..a79328e 100644
--- a/core/modules/node/node.admin.inc
+++ b/core/modules/node/node.admin.inc
@@ -157,160 +157,3 @@ function _node_mass_update_batch_finished($success, $results, $operations) {
     drupal_set_message($message);
   }
 }
-
-/**
- * Returns the admin form object to node_admin_content().
- *
- * @ingroup forms
- */
-function node_admin_nodes() {
-  // Enable language column and filter if multiple languages are enabled.
-  $multilingual = language_multilingual();
-
-  // Build the sortable table header.
-  $header = array(
-    'title' => array(
-      'data' => t('Title'),
-      'field' => 'n.title',
-    ),
-    'type' => array(
-      'data' => t('Content type'),
-      'field' => 'n.type',
-      'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
-    ),
-    'author' => array(
-      'data' => t('Author'),
-      'class' => array(RESPONSIVE_PRIORITY_LOW),
-    ),
-    'status' => array(
-      'data' => t('Status'),
-      'field' => 'n.status',
-    ),
-    'changed' => array(
-      'data' => t('Updated'),
-      'field' => 'n.changed',
-      'sort' => 'desc',
-      'class' => array(RESPONSIVE_PRIORITY_LOW)
-    ,)
-  );
-  if ($multilingual) {
-    $header['language_name'] = array('data' => t('Language'), 'field' => 'n.langcode', 'class' => array(RESPONSIVE_PRIORITY_LOW));
-  }
-  $header['operations'] = array('data' => t('Operations'));
-
-  $query = db_select('node_field_data', 'n')
-    ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-    ->extend('Drupal\Core\Database\Query\TableSortExtender');
-
-  if (!user_access('bypass node access')) {
-    // If the user is able to view their own unpublished nodes, allow them
-    // to see these in addition to published nodes. Check that they actually
-    // have some unpublished nodes to view before adding the condition.
-    if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT DISTINCT nid FROM {node_field_data} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->id(), ':status' => 0))->fetchCol()) {
-      $query->condition(db_or()
-        ->condition('n.status', 1)
-        ->condition('n.nid', $own_unpublished, 'IN')
-      );
-    }
-    else {
-      // If not, restrict the query to published nodes.
-      $query->condition('n.status', 1);
-    }
-  }
-  $nids = $query
-    ->distinct()
-    ->fields('n', array('nid'))
-    ->limit(50)
-    ->orderByHeader($header)
-    ->addTag('node_access')
-    ->execute()
-    ->fetchCol();
-  $nodes = node_load_multiple($nids);
-
-  // Prepare the list of nodes.
-  $languages = language_list(Language::STATE_ALL);
-  $destination = drupal_get_destination();
-  $form['nodes'] = array(
-    '#type' => 'table',
-    '#header' => $header,
-    '#empty' => t('No content available.'),
-  );
-  foreach ($nodes as $node) {
-    $l_options = $node->language()->id != Language::LANGCODE_NOT_SPECIFIED && isset($languages[$node->language()->id]) ? array('language' => $languages[$node->language()->id]) : array();
-    $mark = array(
-      '#theme' => 'mark',
-      '#status' => node_mark($node->id(), $node->getChangedTime()),
-    );
-    $form['nodes'][$node->id()]['title'] = array(
-      '#type' => 'link',
-      '#title' => $node->label(),
-      '#href' => 'node/' . $node->id(),
-      '#options' => $l_options,
-      '#suffix' => ' ' . drupal_render($mark),
-    );
-    $form['nodes'][$node->id()]['type'] = array(
-      '#markup' => check_plain(node_get_type_label($node)),
-    );
-    $form['nodes'][$node->id()]['author'] = array(
-      '#theme' => 'username',
-      '#account' => $node->getAuthor(),
-    );
-    $form['nodes'][$node->id()]['status'] = array(
-      '#markup' => $node->isPublished() ? t('published') : t('not published'),
-    );
-    $form['nodes'][$node->id()]['changed'] = array(
-      '#markup' => format_date($node->getChangedTime(), 'short'),
-    );
-    if ($multilingual) {
-      $form['nodes'][$node->id()]['language_name'] = array(
-        '#markup' => $node->language()->name,
-      );
-    }
-
-    // Build a list of all the accessible operations for the current node.
-    $operations = array();
-    if (node_access('update', $node)) {
-      $operations['edit'] = array(
-        'title' => t('Edit'),
-        'href' => 'node/' . $node->id() . '/edit',
-        'query' => $destination,
-      );
-    }
-    if (node_access('delete', $node)) {
-      $operations['delete'] = array(
-        'title' => t('Delete'),
-        'href' => 'node/' . $node->id() . '/delete',
-        'query' => $destination,
-      );
-    }
-    if ($node->isTranslatable()) {
-      $operations['translate'] = array(
-        'title' => t('Translate'),
-        'href' => 'node/' . $node->id() . '/translations',
-        'query' => $destination,
-      );
-    }
-    $form['nodes'][$node->id()]['operations'] = array();
-    if (count($operations) > 1) {
-      // Render an unordered list of operations links.
-      $form['nodes'][$node->id()]['operations'] = array(
-        '#type' => 'operations',
-        '#subtype' => 'node',
-        '#links' => $operations,
-      );
-    }
-    elseif (!empty($operations)) {
-      // Render the first and only operation as a link.
-      $link = reset($operations);
-      $form['nodes'][$node->id()]['operations'] = array(
-        '#type' => 'link',
-        '#title' => $link['title'],
-        '#href' => $link['href'],
-        '#options' => array('query' => $link['query']),
-      );
-    }
-  }
-
-  $form['pager'] = array('#theme' => 'pager');
-  return $form;
-}
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index aabd9c4..31d494e 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -2,7 +2,7 @@ node.content_overview:
   path: '/admin/content'
   defaults:
     _title: 'Content'
-    _content: '\Drupal\node\Controller\NodeController::contentOverview'
+    _entity_list: 'node'
   requirements:
     _permission: 'access content overview'
 
